verify-tracks.ts (1472B)
1 import path from "node:path"; 2 3 import dotenv from "dotenv"; 4 5 import { resolveTracksDb } from "../server/paths.js"; 6 import { TrackStore } from "../server/trackStore.js"; 7 8 const PROJECT_ROOT = process.cwd(); 9 dotenv.config({ path: path.join(PROJECT_ROOT, ".env") }); 10 11 const SAMPLE_SIZE = Math.min( 12 500, 13 Math.max(1, Number(process.env.VERIFY_SAMPLE_SIZE ?? "50")), 14 ); 15 const TIMEOUT_MS = 15_000; 16 17 async function headOk(url: string): Promise<boolean> { 18 const ctrl = new AbortController(); 19 const t = setTimeout(() => ctrl.abort(), TIMEOUT_MS); 20 try { 21 const res = await fetch(url, { 22 method: "HEAD", 23 signal: ctrl.signal, 24 redirect: "follow", 25 }); 26 return res.ok || res.status === 206; 27 } catch { 28 return false; 29 } finally { 30 clearTimeout(t); 31 } 32 } 33 34 async function main() { 35 const dbPath = resolveTracksDb(process.env, PROJECT_ROOT); 36 const store = new TrackStore(dbPath); 37 if (!store.exists()) { 38 console.error(`tracks.db not found: ${dbPath}`); 39 process.exit(1); 40 } 41 store.open(); 42 43 const picks = store.sampleArchiveUrls(SAMPLE_SIZE); 44 45 let blocked = 0; 46 for (const row of picks) { 47 const ok = await headOk(row.archiveUrl); 48 if (!ok) { 49 store.blockId(row.id); 50 blocked += 1; 51 console.warn(`Blocked ${row.id} — HEAD failed`); 52 } 53 } 54 55 store.close(); 56 console.info(`verify-tracks: checked ${picks.length}, blocked ${blocked}`); 57 } 58 59 main().catch((e) => { 60 console.error(e); 61 process.exit(1); 62 });