From de345686febb14288ccc4682b3d9e93b172c7147 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:20:57 +0330 Subject: [PATCH] Import source: js/data Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- js/data.js | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 js/data.js diff --git a/js/data.js b/js/data.js new file mode 100644 index 0000000..b0efed5 --- /dev/null +++ b/js/data.js @@ -0,0 +1,180 @@ +"use strict"; + +const AUDIO_POOL = Array.from({ length: 16 }, (_, i) => + "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-" + (i + 1) + ".mp3" +); + +function seeded(seed) { + return function () { + seed = (seed * 1664525 + 1013904223) >>> 0; + return seed / 4294967296; + }; +} + +function hashStr(str) { + let h = 2166136261; + for (let i = 0; i < str.length; i++) { + h ^= str.charCodeAt(i); + h = Math.imul(h, 16777619); + } + return h >>> 0; +} + +const ARTISTS = [ + { id: "ar1", name: "Aurora Veil", genre: "Synth-Pop", followers: 2840000, palette: ["#ff4d6d", "#8338ec", "#1a0b2e"], seed: 7, bio: "Dreamy synth-pop from Berlin's underground. Aurora Veil layers analog synths over velvet vocals, crafting nocturnal anthems for the city that never sleeps." }, + { id: "ar2", name: "The Midnight Cartographers", genre: "Indie", followers: 940000, palette: ["#457b9d", "#f4a261", "#1d3557"], seed: 13, bio: "Folk-inspired indie quartet mapping quiet roads and honest stories. Their warm harmonies have soundtracked a thousand long drives." }, + { id: "ar3", name: "Nova Sinclair", genre: "Pop", followers: 5100000, palette: ["#e63946", "#f1faee", "#a8dadc"], seed: 21, bio: "Chart-topping pop sensation known for cinematic choruses and confetti-stained anthems. Every Nova Sinclair record plays like a movie you want to live in." }, + { id: "ar4", name: "Detra", genre: "R&B/Soul", followers: 1760000, palette: ["#6d597a", "#e56b6f", "#355070"], seed: 33, bio: "Velvet-voiced R&B storyteller. Detra's music lives in the 2 a.m. hour — silk sheets, city lights, and confessions you only make in the dark." }, + { id: "ar5", name: "ORBIT", genre: "Hip-Hop", followers: 3900000, palette: ["#0b090a", "#fca311", "#e5383b"], seed: 42, bio: "Gravity-defying lyricist mapping outer space and inner streets. ORBIT's production is cold chrome and neon, his flow weightless." }, + { id: "ar6", name: "Neon Reverie", genre: "Electronic", followers: 1280000, palette: ["#06d6a0", "#ef476f", "#073b4c"], seed: 55, bio: "Electronic producer building chrome-plated soundscapes for night drives and dance floors. Retro-futurism with a heartbeat." }, + { id: "ar7", name: "Copper & Lime", genre: "Jazz", followers: 480000, palette: ["#2a6f97", "#f6bd60", "#1d3557"], seed: 68, bio: "Five-piece jazz ensemble pouring brass and bourbon after dark. Their Blue Hour Sessions have become a late-night institution." }, + { id: "ar8", name: "Marisol Vega", genre: "Latin", followers: 2200000, palette: ["#ef8354", "#2f3e46", "#ffd166"], seed: 77, bio: "Havana-born, Miami-raised, and irresistible. Marisol Vega fuses salsa brass, reggaeton bounce and a voice like golden hour." }, + { id: "ar9", name: "The Velvet Circuit", genre: "Alternative", followers: 860000, palette: ["#540b0e", "#9e2a2b", "#e09f3e"], seed: 88, bio: "Guitars like broken glass, melodies like smoke. The Velvet Circuit channel stadium-sized angst through four amps set to eleven." }, + { id: "ar10", name: "Cadence Field", genre: "Country", followers: 730000, palette: ["#7f5539", "#ddb892", "#b08968"], seed: 99, bio: "Country troubadour from nowhere, Tennessee. Cadence Field sings about dust, diesel, and the people who hold the map." }, + { id: "ar11", name: "HANA", genre: "K-Pop", followers: 6400000, palette: ["#ff006e", "#3a86ff", "#fb5607"], seed: 110, bio: "Seoul's brightest pop comet. HANA blends bubblegum hooks, razor-sharp choreo, and production that hits like a candy-colored lightning bolt." }, + { id: "ar12", name: "String Theory Quartet", genre: "Classical", followers: 320000, palette: ["#e9edc9", "#ccd5ae", "#588157"], seed: 121, bio: "A modern classical quartet turning glass gardens and frozen lakes into music. Minimal, luminous, and quietly devastating." }, + { id: "ar13", name: "Kodiak Bloom", genre: "Folk", followers: 510000, palette: ["#bc4749", "#386641", "#f2e8cf"], seed: 132, bio: "Folk singer and keeper of the flame. Kodiak Bloom writes songs that smell like cedar smoke and sound like home." }, + { id: "ar14", name: "YUN", genre: "K-Pop", followers: 1900000, palette: ["#0d1b2a", "#e0e1dd", "#778da9"], seed: 143, bio: "Producer and vocalist floating between Seoul and Los Angeles. YUN's midnight R&B has made him the most-requested feature in the scene." } +]; + +const ALBUMS = [ + { id: "al1", artistId: "ar1", title: "Chromatic", year: 2026, genre: "Synth-Pop", palette: ["#3a0ca3", "#f72585", "#7209b7"], seed: 11, songs: ["Prism Hearts", "Neon Lullaby", "Chrome Sunset", "Glitter Static", "Halcyon Drive", "Ultraviolet", "Silk & Static", "Afterglow"] }, + { id: "al2", artistId: "ar1", title: "Spectra", year: 2025, genre: "Synth-Pop", palette: ["#ff4d6d", "#8338ec", "#10002b"], seed: 23, songs: ["Spectra", "Glass City", "Parallel Lines", "Mirage", "Fever Dream", "Polaroid Sky"] }, + { id: "al3", artistId: "ar2", title: "Paper Boats", year: 2024, genre: "Indie", palette: ["#457b9d", "#f4a261", "#1d3557"], seed: 31, songs: ["Paper Boats", "Maps of the Morning", "Coastline Letter", "Half-Lit Porch", "Train Song for Emma", "Wildflowers at Dawn", "The River Knows", "Gravity (Acoustic)"] }, + { id: "al4", artistId: "ar3", title: "Heartbeat Cinema", year: 2026, genre: "Pop", palette: ["#e63946", "#a8dadc", "#f1faee"], seed: 43, songs: ["Dancing in the Dark Room", "Heartbeat Cinema", "Paper Planes & Promises", "Marquee Lights", "Slow Motion", "Confetti Hearts", "Final Scene", "Director's Cut"] }, + { id: "al5", artistId: "ar4", title: "Velvet Hours", year: 2025, genre: "R&B/Soul", palette: ["#6d597a", "#e56b6f", "#355070"], seed: 51, songs: ["Velvet Hours", "Midnight Oil", "Honey, Slow", "Grapefruit Moon", "Two A.M. Truth", "Silk Sheets", "After the Rain", "Blue Velvet (Reprise)", "Cigarettes & Jasmine"] }, + { id: "al6", artistId: "ar5", title: "Satellite Dreams", year: 2026, genre: "Hip-Hop", palette: ["#0b090a", "#fca311", "#e5383b"], seed: 63, songs: ["Orbit", "Starlight Freestyle", "Satellite Dreams", "Gravity Check", "Low Orbit", "City Lights", "Echo Chamber", "Weightless"] }, + { id: "al7", artistId: "ar6", title: "Neon Skyline", year: 2024, genre: "Electronic", palette: ["#06d6a0", "#ef476f", "#073b4c"], seed: 71, songs: ["Neon Skyline", "Midnight Protocol", "Laser Garden", "Velocity", "Chrome Hearts", "Analog Summer", "Night Drive", "Sunrise.exe"] }, + { id: "al8", artistId: "ar7", title: "Blue Hour Sessions", year: 2025, genre: "Jazz", palette: ["#2a6f97", "#f6bd60", "#1d3557"], seed: 83, songs: ["Blue Hour", "Café at Dusk", "The Slow Waltz", "Brass & Bourbon", "Rain on Canal Street", "Afterglow Suite", "Velvet Trombone", "Last Call"] }, + { id: "al9", artistId: "ar8", title: "Cielo Abierto", year: 2025, genre: "Latin", palette: ["#ef8354", "#ffd166", "#2f3e46"], seed: 91, songs: ["Cielo Abierto", "Baila Conmigo", "Verano en la Habana", "Corazón de Fuego", "Luna de Miel", "Paso Doble", "Mar de Cartas", "Bésame Así"] }, + { id: "al10", artistId: "ar9", title: "Static Bloom", year: 2024, genre: "Alternative", palette: ["#540b0e", "#e09f3e", "#9e2a2b"], seed: 103, songs: ["Static Bloom", "Razorlight Avenue", "Fault Lines", "Dying Light", "Glass Houses", "Crown of Thorns", "White Noise", "Petals & Petrol"] }, + { id: "al11", artistId: "ar10", title: "Backroad Radio", year: 2025, genre: "Country", palette: ["#7f5539", "#ddb892", "#b08968"], seed: 113, songs: ["Backroad Radio", "Dust on My Boots", "Honky-Tonk Heart", "Field of Gold", "Sunday Drive", "River Stone", "Grandpa's Gibson", "Neon Honky-Tonk"] }, + { id: "al12", artistId: "ar11", title: "Neon Bloom", year: 2026, genre: "K-Pop", palette: ["#ff006e", "#3a86ff", "#fb5607"], seed: 121, songs: ["Neon Bloom", "Electric Cherry", "Bubble Pop", "Midnight Seoul", "Gravity (feat. YUN)", "Butterfly Effect", "Cherry Blossom Club", "Overdrive"] }, + { id: "al13", artistId: "ar12", title: "Glass Gardens", year: 2024, genre: "Classical", palette: ["#e9edc9", "#ccd5ae", "#588157"], seed: 133, songs: ["Glass Gardens", "Nocturne in C Minor", "The Frozen Lake", "Aria for Strings", "Morning in Kyoto", "Elegy", "Light Through Leaves", "Finale: Spring"] }, + { id: "al14", artistId: "ar13", title: "Wildfire Season", year: 2026, genre: "Folk", palette: ["#bc4749", "#386641", "#f2e8cf"], seed: 141, songs: ["Wildfire Season", "Ember & Ash", "Cedar Grove", "The Long Winter", "Homeward", "Timberline", "Northern Lights", "Burning Bright"] } +]; + +const STATIONS = [ + { id: "st1", name: "Pop Hits Radio", desc: "Every pop smash, nonstop. New music, fresh picks, zero skips.", genres: ["Pop", "Synth-Pop", "K-Pop"], palette: ["#ff006e", "#3a86ff", "#ffbe0b"], seed: 4 }, + { id: "st2", name: "Rock Classics", desc: "Amp-stack anthems from the indie underground to the stadium.", genres: ["Alternative", "Indie", "Folk"], palette: ["#540b0e", "#e09f3e", "#1a1a1a"], seed: 9 }, + { id: "st3", name: "Chill Beats", desc: "Velvet R&B, dreamy synth, and low-light electronic.", genres: ["R&B/Soul", "Synth-Pop", "Electronic"], palette: ["#6d597a", "#e56b6f", "#22333b"], seed: 14 }, + { id: "st4", name: "Hip-Hop Now", desc: "The freshest verses and hardest 808s in the game.", genres: ["Hip-Hop"], palette: ["#0b090a", "#fca311", "#e5383b"], seed: 19 }, + { id: "st5", name: "Jazz Lounge", desc: "Brass, bourbon, and blue hours. For after dark.", genres: ["Jazz", "Classical"], palette: ["#2a6f97", "#f6bd60", "#1d3557"], seed: 24 }, + { id: "st6", name: "Latin Heat", desc: "Salsa, reggaeton and golden-hour romance.", genres: ["Latin"], palette: ["#ef8354", "#ffd166", "#2f3e46"], seed: 29 }, + { id: "st7", name: "Focus Flow", desc: "Instrumental calm for deep work and deep breath.", genres: ["Classical", "Jazz", "Electronic"], palette: ["#ccd5ae", "#588157", "#22333b"], seed: 34 } +]; + +const MIXES = [ + { id: "mix-new", name: "New Music Mix", desc: "Fresh cuts from the newest releases, updated daily.", palette: ["#ff4d6d", "#8338ec", "#ffd166"], seed: 3, type: "new" }, + { id: "mix-fav", name: "Favorites Mix", desc: "The songs you hearted, reimagined into one perfect flow.", palette: ["#fa233b", "#fb5c74", "#3a0ca3"], seed: 8, type: "fav" }, + { id: "mix-chill", name: "Chill Mix", desc: "Laid-back synth, soul and dream-pop for slow evenings.", palette: ["#06d6a0", "#073b4c", "#ef476f"], seed: 17, type: "chill" }, + { id: "mix-work", name: "Get Up! Mix", desc: "High-energy pop, hip-hop and dance to start the engine.", palette: ["#fca311", "#e5383b", "#14213d"], seed: 22, type: "work" } +]; + +const SONGS = (function build() { + const list = []; + ALBUMS.forEach((album, ai) => { + const rnd = seeded(album.seed * 97 + 13); + album.songs.forEach((title, ti) => { + const dur = Math.round(165 + rnd() * 150); + list.push({ + id: "s" + (list.length + 1), + title: title, + albumId: album.id, + artistId: album.artistId, + genre: album.genre, + year: album.year, + track: ti + 1, + dur: dur, + plays: Math.round(120000 + rnd() * 8800000), + audio: AUDIO_POOL[(ai * 3 + ti) % AUDIO_POOL.length] + }); + }); + }); + return list; +})(); + +const D = (function buildIndex() { + const byId = (list) => { + const m = {}; + list.forEach((x) => (m[x.id] = x)); + return m; + }; + const artistById = byId(ARTISTS); + const albumById = byId(ALBUMS); + const songById = byId(SONGS); + const albumSongs = (albumId) => SONGS.filter((s) => s.albumId === albumId).sort((a, b) => a.track - b.track); + const artistAlbums = (artistId) => ALBUMS.filter((a) => a.artistId === artistId).sort((a, b) => b.year - a.year); + const artistSongs = (artistId) => SONGS.filter((s) => s.artistId === artistId).sort((a, b) => b.plays - a.plays); + const albumPlays = (album) => albumSongs(album.id).reduce((t, s) => t + s.plays, 0); + const enrich = (s) => ({ + ...s, + artist: artistById[s.artistId].name, + album: albumById[s.albumId].title, + artistName: artistById[s.artistId].name + }); + const song = (id) => enrich(songById[id]); + const album = (id) => ({ ...albumById[id], artist: artistById[albumById[id].artistId].name }); + const artist = (id) => artistById[id]; + const totalPlays = SONGS.reduce((t, s) => t + s.plays, 0); + const totalDur = SONGS.reduce((t, s) => t + s.dur, 0); + return { + artists: ARTISTS, + albums: ALBUMS, + songs: SONGS, + stations: STATIONS, + mixes: MIXES, + song, album, artist, + albumSongs, artistAlbums, artistSongs, albumPlays, + topCharts: () => SONGS.slice().sort((a, b) => b.plays - a.plays).slice(0, 10).map(enrich), + topSongs: (n) => SONGS.slice().sort((a, b) => b.plays - a.plays).slice(0, n).map(enrich), + newReleases: () => ALBUMS.slice().sort((a, b) => (b.year - a.year) || (D.albumPlays(b) - D.albumPlays(a))), + featuredAlbum: () => { + let best = ALBUMS[0], bestP = -1; + ALBUMS.forEach((a) => { + const p = D.albumPlays(a); + if (p > bestP) { bestP = p; best = a; } + }); + return best; + }, + genreAlbums: (genre) => ALBUMS.filter((a) => a.genre === genre), + stationSongs: (station) => { + const set = new Set(station.genres); + return SONGS.filter((s) => set.has(s.genre)).map(enrich); + }, + mixSongs: (mix) => { + let songs; + if (mix.type === "new") { + const ids = new Set(D.newReleases().slice(0, 5).flatMap((a) => albumSongs(a.id).map((s) => s.id))); + songs = SONGS.filter((s) => ids.has(s.id)); + } else if (mix.type === "fav") { + songs = SONGS.filter((s) => Store.likes.has(s.id)); + if (!songs.length) songs = SONGS.slice(); + } else if (mix.type === "chill") { + const g = new Set(["Electronic", "R&B/Soul", "Synth-Pop", "Jazz"]); + songs = SONGS.filter((s) => g.has(s.genre)); + } else { + const g = new Set(["Pop", "Hip-Hop", "K-Pop", "Latin"]); + songs = SONGS.filter((s) => g.has(s.genre)); + } + const rnd = seeded(hashStr(mix.id)); + return songs.slice().sort(() => rnd() - 0.5).slice(0, 25).map(enrich); + }, + genreOf: (g) => SONGS.filter((s) => s.genre === g).map(enrich), + search: (query) => { + const q = query.toLowerCase().replace(/[àáâãäå]/g, "a").replace(/[èéêë]/g, "e").replace(/[ìíîï]/g, "i").replace(/[òóôõö]/g, "o").replace(/[ùúûü]/g, "u").replace(/ñ/g, "n").trim(); + if (!q) return { songs: [], albums: [], artists: [] }; + const rank = (s) => s.startsWith(q) ? 0 : s.includes(q) ? 1 : 2; + const songs = SONGS.filter((s) => { + const t = (s.title + " " + ARTISTS.find((a) => a.id === s.artistId).name + " " + ALBUMS.find((a) => a.id === s.albumId).title).toLowerCase(); + return t.includes(q); + }).map(enrich).sort((a, b) => rank(a.title.toLowerCase()) - rank(b.title.toLowerCase())); + const albums = ALBUMS.filter((a) => (a.title + " " + ARTISTS.find((x) => x.id === a.artistId).name).toLowerCase().includes(q)) + .sort((a, b) => rank(a.title.toLowerCase()) - rank(b.title.toLowerCase())); + const artists = ARTISTS.filter((a) => a.name.toLowerCase().includes(q)) + .sort((a, b) => rank(a.name.toLowerCase()) - rank(b.name.toLowerCase())); + return { songs, albums, artists }; + }, + totalPlays, totalDur + }; +})();