Files
site/core.js
T

40 lines
2.7 KiB
JavaScript

/* ============================================================
gandora — a generative station.
seed → DJ (Venice chat) → ACE-Step (Venice /audio/queue) → player.
Every cover is drawn from the song's own settings: hue from key,
rings from BPM, density from energy, symmetry from the seed.
============================================================ */
export const DIRECT = "https://api.venice.ai/api/v1"; // with a personal key
export const SHARED = "https://inference.yolopreneur.lol/v1"; // without one: the factory's worker, quota'd per person
export const $ = (s) => document.querySelector(s);
export const LS = { key: "gandora.key", dj: "gandora.dj", cost: "gandora.cost" };
export const state = {
key: localStorage.getItem(LS.key) || "",
dj: localStorage.getItem(LS.dj) || "claude-haiku-5-5",
seed: "", station: "", stationId: "", sinceAd: 0,
taste: { like: [], avoid: [], notes: [] }, // what the listener has told the station
history: [], // tracks played this session (specs)
current: null, nextJob: null, playing: false,
recent: [], pos: -1, // ids of tracks played this session, and where the player stands in them
audio: Object.assign(new Audio(), { volume: Math.min(1, Math.max(0, parseFloat(localStorage.getItem("gandora.vol") || "1"))) }), cost: parseFloat(localStorage.getItem(LS.cost) || "0"),
};
state.audio.preload = "auto";
/* hooks: the seams something loaded after the page (brain.js) plugs into. Each is null or a function; the page calls
them at the named moments. Assign, don't override the page's functions. */
export const hooks = {
djPrompt: null, // (prompt) => prompt the DJ's prompt, to add to (brain.js adds the taste text)
jobMade: null, // (spec) a sheet about to be rendered (brain.js writes spec.guess)
stationOpened: null, // (seed, opts) after openStation finished
reacted: null, // (emoji, text) after a reaction was taken in
};
/* ---------- small utils ---------- */
export const toast = (m, ms = 2400) => { const t = $("#toast"); t.textContent = m; t.classList.add("on"); clearTimeout(toast.h); toast.h = setTimeout(() => t.classList.remove("on"), ms); };
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export const asDataUrl = (blob) => new Promise((ok, no) => { const r = new FileReader(); r.onload = () => ok(r.result); r.onerror = () => no(r.error); r.readAsDataURL(blob); });
export const ids = { seq: 0 }; // job ids; brain.js reaches it as window.seq
export const mmss = (s) => `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, "0")}`;