Files
music-video-gen/metabolism/src/demo/catalog.js
T

62 lines
2.1 KiB
JavaScript

// Catalog loading + display helpers for the demo page.
// The catalog itself is computed by the vite plugin (node-side analysis of the
// exact same pipeline the browser uses), so the library card and the player
// can never disagree about a song's shape.
const LAW_COLORS = {
intro: '#7d8fa3',
build: '#d8a24a',
drop: '#e2434b',
breakdown: '#4a8fe2',
sustain: '#3fbf9f',
outro: '#9a7fd6',
};
export function fetchCatalog() {
return fetch('/songs/catalog.json').then((r) => {
if (!r.ok) throw new Error(`catalog unavailable (${r.status})`);
return r.json();
});
}
export function lawColor(kind) {
return LAW_COLORS[kind] || '#5f6673';
}
/** A CSS accent (hue/sat from the track's palette, so the page wears the song). */
export function accentFor(entry) {
const hue = Number(entry.hue) || 0.6;
const sat = Number(entry.sat) || 0.6;
return `hsl(${Math.round(hue * 360)}, ${Math.round(Math.min(1, sat) * 100)}%, 58%)`;
}
export function fmtTime(s) {
if (!Number.isFinite(s) || s < 0) return '0:00';
const m = Math.floor(s / 60);
const ss = String(Math.floor(s % 60)).padStart(2, '0');
return `${m}:${ss}`;
}
/** A one-line natural-language summary of what the track is. */
export function tagsFor(entry) {
const q = entry.summary;
const bits = [];
if (q.bpm) bits.push(`${Math.round(q.bpm)} BPM`);
bits.push(q.centroid > 0.5 ? 'bright' : 'dark');
bits.push(q.flatness < 0.12 ? 'tonal' : q.flatness < 0.4 ? 'grained' : 'noisy');
bits.push(q.beatStrength > 0.5 ? 'pulsing' : 'beatless');
bits.push(`${entry.laws.length} ${entry.laws.length === 1 ? 'part' : 'parts'}`);
return bits.join(' · ');
}
/** Read the URL's song/seed/res and give safe defaults for the rest. */
export function parseQuery() {
const p = new URLSearchParams(location.search);
const res = ['640x360', '960x540', '1280x720'].includes(p.get('res')) ? p.get('res') : '640x360';
return { song: p.get('song') || 'drone', seed: parseSeed(p.get('seed')), res };
}
function parseSeed(v) {
const n = parseInt(v ?? '', 10);
return Number.isFinite(n) && n > 0 ? n : 12345;
}