Files
musictest/js/data.js
T
arsam de345686fe Import source: js/data
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
2026-09-18 23:20:57 +03:30

181 lines
15 KiB
JavaScript

"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
};
})();