Spotify's power to w discovery - powerful search, personalized playlists (Discover Weekly, Release Radar) i recommendations.
1interface SearchResults {
2 tracks: Track[];
3 albums: Album[];
4 artists: Artist[];
5 playlists: Playlist[];
6}
7
8interface SearchFilters {
9 type?: 'track' | 'album' | 'artist' | 'playlist';
10 genre?: string[];
11 year?: { min?: number; max?: number };
12 explicit?: boolean;
13}
14
15// Search API
16async function search(
17 query: string,
18 filters?: SearchFilters
19): Promise<SearchResults> {
20 // Implementation...
21}1'use client';
2
3import { useState, useEffect } from 'react';
4
5export default function SearchPage() {
6 const [query, setQuery] = useState('');
7 const [activeTab, setActiveTab] = useState<'all' | 'tracks' | 'albums' | 'artists' | 'playlists'>('all');
8 const [results, setResults] = useState<SearchResults | null>(null);
9
10 useEffect(() => {
11 if (query.length > 0) {
12 // Debounced search
13 const timeout = setTimeout(() => {
14 performSearch(query);
15 }, 300);
16
17 return () => clearTimeout(timeout);
18 }
19 }, [query]);
20
21 const performSearch = async (searchQuery: string) => {
22 // Mock search results
23 setResults({
24 tracks: mockTracks.filter(t =>
25 t.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
26 t.artist.name.toLowerCase().includes(searchQuery.toLowerCase())
27 ),
28 albums: mockAlbums.filter(a =>
29 a.title.toLowerCase().includes(searchQuery.toLowerCase())
30 ),
31 artists: mockArtists.filter(a =>
32 a.name.toLowerCase().includes(searchQuery.toLowerCase())
33 ),
34 playlists: mockPlaylists.filter(p =>
35 p.name.toLowerCase().includes(searchQuery.toLowerCase())
36 )
37 });
38 };
39
40 return (
41 <div className="min-h-screen bg-black text-white p-8">
42 {/* Search Input */}
43 <div className="mb-6">
44 <div className="relative max-w-md">
45 <input
46 type="text"
47 value={query}
48 onChange={(e) => setQuery(e.target.value)}
49 placeholder="Czego chcesz posłuchać?"
50 className="w-full bg-gray-800 text-white px-12 py-3 rounded-full outline-none focus:ring-2 focus:ring-white"
51 autoFocus
52 />
53 <span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-xl">
54 🔍
55 </span>
56 </div>
57 </div>
58
59 {!query ? (
60 // Browse All / Recent Searches
61 <div>
62 <h2 className="text-2xl font-bold mb-4">Browse All</h2>
63 <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
64 {['Sea Shanties', 'Battle Anthems', 'Treasure Hunting', 'Calm Seas', 'Epic Adventures', 'Ship Repairs'].map(genre => (
65 <button
66 key={genre}
67 className="aspect-square bg-gradient-to-br from-purple-600 to-pink-600 rounded-lg p-4 font-bold text-lg hover:scale-105 transition"
68 >
69 {genre}
70 </button>
71 ))}
72 </div>
73 </div>
74 ) : results && (
75 <>
76 {/* Tabs */}
77 <div className="flex gap-4 mb-6 border-b border-gray-800">
78 {[
79 { key: 'all', label: 'All' },
80 { key: 'tracks', label: 'Songs' },
81 { key: 'albums', label: 'Albums' },
82 { key: 'artists', label: 'Artists' },
83 { key: 'playlists', label: 'Playlists' }
84 ].map(tab => (
85 <button
86 key={tab.key}
87 onClick={() => setActiveTab(tab.key as any)}
88 className={`pb-2 px-4 font-semibold ${
89 activeTab === tab.key
90 ? 'border-b-2 border-green-500'
91 : 'text-gray-400'
92 }`}
93 >
94 {tab.label}
95 </button>
96 ))}
97 </div>
98
99 {/* Results */}
100 <div className="space-y-6">
101 {(activeTab === 'all' || activeTab === 'tracks') && results.tracks.length > 0 && (
102 <div>
103 <h3 className="text-xl font-bold mb-4">Songs</h3>
104 <div className="space-y-2">
105 {results.tracks.slice(0, activeTab === 'all' ? 4 : undefined).map(track => (
106 <div
107 key={track.id}
108 className="flex items-center gap-4 p-2 rounded hover:bg-gray-800 cursor-pointer"
109 >
110 <img
111 src={track.album.coverArt}
112 alt={track.title}
113 className="w-12 h-12 rounded"
114 />
115 <div className="flex-1">
116 <div className="font-semibold">{track.title}</div>
117 <div className="text-sm text-gray-400">
118 {track.artist.name}
119 </div>
120 </div>
121 <span className="text-sm text-gray-400">
122 {Math.floor(track.duration / 60)}:{(track.duration % 60).toString().padStart(2, '0')}
123 </span>
124 </div>
125 ))}
126 </div>
127 </div>
128 )}
129
130 {(activeTab === 'all' || activeTab === 'artists') && results.artists.length > 0 && (
131 <div>
132 <h3 className="text-xl font-bold mb-4">Artists</h3>
133 <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
134 {results.artists.slice(0, activeTab === 'all' ? 4 : undefined).map(artist => (
135 <div
136 key={artist.id}
137 className="bg-gray-900 p-4 rounded hover:bg-gray-800 cursor-pointer"
138 >
139 <img
140 src={artist.avatar}
141 alt={artist.name}
142 className="w-full aspect-square rounded-full mb-4"
143 />
144 <div className="font-semibold">{artist.name}</div>
145 <div className="text-sm text-gray-400">Artist</div>
146 </div>
147 ))}
148 </div>
149 </div>
150 )}
151 </div>
152 </>
153 )}
154 </div>
155 );
156}1interface RecommendationEngine {
2 // Based on listening history
3 discoverWeekly: (userId: string) => Playlist;
4
5 // New releases from followed artists
6 releaseRadar: (userId: string) => Playlist;
7
8 // Based on specific seed
9 radioStation: (seed: Track | Artist | Genre) => Track[];
10
11 // Similar to a track
12 similarTracks: (trackId: string, limit?: number) => Track[];
13}
14
15// Discover Weekly - personalized playlist based on listening history
16function generateDiscoverWeekly(
17 listeningHistory: Track[],
18 likedTracks: Track[],
19 followedArtists: Artist[]
20): Playlist {
21 // Algorithm:
22 // 1. Analyze user's taste (genres, artists, audio features)
23 // 2. Find similar tracks user hasn't heard
24 // 3. Weight by popularity and recency
25 // 4. Limit to 30 tracks
26
27 return {
28 id: 'discover-weekly',
29 name: 'Discover Weekly',
30 description: 'Your weekly mixtape of fresh music',
31 coverImage: '/discover-weekly.jpg',
32 owner: { id: 'spotify', username: 'Spotify', avatar: '/spotify.png' },
33 tracks: [...], // 30 recommended tracks
34 followers: 0,
35 isPublic: false,
36 isCollaborative: false,
37 totalDuration: 0,
38 createdAt: new Date(),
39 updatedAt: new Date()
40 };
41}✅ Search system - tracks, albums, artists, playlists ✅ Debounced search - 300ms delay for performance ✅ Search tabs - filter results by type ✅ Browse categories - genre cards ✅ Recent searches - history tracking ✅ Discover Weekly - personalized recommendations ✅ Release Radar - new music from followed artists ✅ Radio station - endless playback based on seed ✅ Similar tracks - recommendation algorithm
Masz teraz kompletny Spotify clone z player, playlists, search i recommendations! 🎵
Do zobaczenia! 🚀