#!/usr/bin/env node 'use strict'; // Official chapter release dates from MangaUpdates (BL-0048) for a whole library or one manga. // Dry run by default. Apply only with the scraper stopped (--apply --writers-stopped), then let a // full reconciliation (or admin Rescan) pick up the edited chapter.json files. const fs = require('node:fs').promises; const path = require('node:path'); const { ReleaseDateSources, refreshMangaReleaseDates } = require('../scraper/metadata/release-dates'); const args = process.argv.slice(2); const apply = args.includes('--apply'); const only = args.find(arg => arg.startsWith('--manga='))?.slice('--manga='.length); const cacheDir = args.find(arg => arg.startsWith('--cache='))?.slice('--cache='.length) || path.join(require('node:os').tmpdir(), 'mangacircus-release-dates'); const root = args.find(arg => !arg.startsWith('--')); if (!root || args.some(arg => arg.startsWith('--') && !['--apply', '--writers-stopped'].includes(arg) && !/^--(manga|cache)=/.test(arg)) || (apply && !args.includes('--writers-stopped'))) { console.error('Usage: node tools/release-dates.js [--manga=] [--cache=] [--apply --writers-stopped]'); process.exit(2); } async function main() { const sources = new ReleaseDateSources({ cacheDir }); const totals = { manga: 0, matched: 0, chapters: 0, accepted: 0, written: 0, cleared: 0, outsideRun: 0, earlier: 0, outOfOrder: 0, noRun: 0 }; const names = (await fs.readdir(root, { withFileTypes: true })).filter(e => e.isDirectory() && !e.name.startsWith('.')).map(e => e.name).sort(); for (const name of names) { if (only && name !== only) continue; try { const s = await refreshMangaReleaseDates(path.join(root, name), sources, { apply }); totals.manga++; totals.chapters += s.chapters; if (s.ambiguous) { console.log(`${name.slice(0, 44).padEnd(44)} skipped: chapter numbers repeat across books`); continue; } if (!s.series) { console.log(`${name.slice(0, 44).padEnd(44)} no MangaUpdates match`); continue; } totals.matched++; totals.accepted += s.accepted; totals.written += s.written; totals.cleared += s.cleared; for (const k of ['outsideRun', 'earlier', 'outOfOrder', 'noRun']) totals[k] += s.rejected[k]; console.log(`${name.slice(0, 44).padEnd(44)} ${String(s.chapters).padStart(5)} ch official ${String(s.official).padStart(4)} accepted ${String(s.accepted).padStart(4)}` + ` rejected: outside run ${s.rejected.outsideRun}, earlier evidence ${s.rejected.earlier}, out of order ${s.rejected.outOfOrder}` + `${s.rejected.noRun ? `, no AniList run ${s.rejected.noRun}` : ''} (${s.series.title})`); } catch (error) { console.warn(`${name}: ${error.message}`); } } console.log(JSON.stringify({ apply, ...totals })); } main().catch(error => { console.error(error); process.exit(1); });