import path from "node:path"; import { randomBytes, randomInt } from "node:crypto"; import { songDir } from "./browse"; import { readJson, withStateLock, writeJsonAtomic } from "./state"; import { emptyCompares, type Compare, type CompareDoc } from "./compare-types"; export * from "./compare-types"; // videos//compares.json, beside verdicts.json and for the same reason: // a tree of songs is a thing that gets copied, and its judgements should travel // with it. Written read-modify-write under withStateLock + writeJsonAtomic, so // two tabs deciding at once cannot lose a row. export const comparesFile = (id: string) => path.join(songDir(id), "compares.json"); export async function readCompares(id: string): Promise { const j = await readJson(comparesFile(id), emptyCompares()); return j && typeof j === "object" && Array.isArray(j.compares) ? j : emptyCompares(); } /** Append one observation. Never mutates an existing row. */ export async function appendCompare(id: string, c: Omit): Promise { const row: Compare = { ...c, at: new Date().toISOString() }; await withStateLock(async () => { const doc = await readCompares(id); doc.compares.push(row); await writeJsonAtomic(comparesFile(id), doc); }); return row; } // --------------------------------------------------------------------------- // Blind pairings. // // A