import "server-only"; import path from "node:path"; import { rawDb } from "@/lib/db/client"; export interface ManualSubtitle { code: string; partIdx: number; absPath: string; attachedAt: number; } interface ManualSubtitleRow { code: string; part_idx: number; abs_path: string; attached_at: number; } function rowToEntry(r: ManualSubtitleRow): ManualSubtitle { return { code: r.code, partIdx: r.part_idx, absPath: r.abs_path, attachedAt: r.attached_at }; } export function listManualSubtitlesForVariant(code: string, partIdx: number): ManualSubtitle[] { const rows = rawDb.prepare(` SELECT code, part_idx, abs_path, attached_at FROM manual_subtitles WHERE code = ? AND part_idx = ? ORDER BY attached_at DESC `).all(code, partIdx) as ManualSubtitleRow[]; return rows.map(rowToEntry); } /** True iff this exact abs path is recorded against any (code, part). */ export function isManualSubtitlePath(abs: string): boolean { const resolved = path.resolve(abs); // Windows paths are case-insensitive on disk but stored as-typed. // Compare with a case-insensitive LIKE on Windows, exact on POSIX. if (process.platform === "win32") { const row = rawDb.prepare(` SELECT 1 FROM manual_subtitles WHERE LOWER(abs_path) = LOWER(?) LIMIT 1 `).get(resolved); return !!row; } const row = rawDb.prepare(`SELECT 1 FROM manual_subtitles WHERE abs_path = ? LIMIT 1`).get(resolved); return !!row; } export function attachManualSubtitle(code: string, partIdx: number, absPath: string): void { rawDb.prepare(` INSERT OR REPLACE INTO manual_subtitles (code, part_idx, abs_path, attached_at) VALUES (?, ?, ?, ?) `).run(code, partIdx, path.resolve(absPath), Date.now()); } export function detachManualSubtitle(code: string, partIdx: number, absPath: string): void { rawDb.prepare(` DELETE FROM manual_subtitles WHERE code = ? AND part_idx = ? AND abs_path = ? `).run(code, partIdx, path.resolve(absPath)); }