Files
website/scripts/media-import.ts
T

68 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Import entries into the media trackers' generic children (webtoons, podcasts, videos;
* media.items, /trackers/webtoons|podcasts|videos). Idempotent: rows are keyed by kind +
* normalised title (+ creator for videos); re-running updates the given fields, never duplicates.
* The owner can paste the same JSON on each tracker page (import tile).
*
* On stan-ovh (DB_URL is loaded for you, nothing printed):
* scripts/media-run.sh ~/imports/webtoons.json --kind webtoons
* scripts/media-run.sh ~/imports/media.json # object keyed by kind
* Add --dry-run to parse and print a summary without touching the database.
*
* Format: an array of entries (for --kind, or each with its own "kind"), or an object
* { "webtoons": [...], "podcasts": [...], "videos": [...] }. Entry fields (only title required):
* title, creator | author | host | channel, platform, genre, status, progress | episode | chapter,
* total | total_episodes, series_status (ongoing|completed, webtoons), form (channel|video|playlist,
* videos), url, cover_url (https), rating (0.5–5), who (stan|juhee|both), started, finished
* (YYYY-MM-DD), notes, title_en, artist, synopsis, year_start, year_end, adaptation, source_ref.
* Statuses: webtoons reading|completed|on_hold|dropped|plan; podcasts listening|caught_up|paused|
* dropped|plan; videos watching|following|watched|dropped|plan ("plan to read" etc. map to plan).
*/
import { readFile } from 'node:fs/promises';
import postgres from 'postgres';
import { importRows, parseImport } from '../src/lib/server/media/store';
import { KINDS, MEDIA_KINDS, type MediaKind } from '../src/lib/media/kinds';
const args = process.argv.slice(2);
const flag = (name: string) => {
const i = args.indexOf(name);
return i >= 0 ? args.splice(i, 2)[1] : undefined;
};
const dry = args.includes('--dry-run');
const kindArg = flag('--kind');
const file = args.find((a) => !a.startsWith('--'));
if (!file)
throw new Error(
'usage: media-import.ts <file.json> [--kind webtoons|podcasts|videos] [--dry-run]'
);
const kind: MediaKind | undefined = kindArg
? (MEDIA_KINDS.find((k) => k === kindArg) ??
Object.values(KINDS).find((c) => c.name === kindArg)?.kind)
: undefined;
if (kindArg && !kind) throw new Error(`unknown --kind ${kindArg}`);
const { rows, errors } = parseImport(JSON.parse(await readFile(file, 'utf8')), kind);
const byKind = Object.groupBy(rows, (r) => r.kind);
console.log(
`parsed ${rows.length} entries: ${
Object.entries(byKind)
.map(([k, v]) => `${k} ${v?.length ?? 0}`)
.join(', ') || 'none'
}`
);
if (errors.length) {
for (const e of errors.slice(0, 50)) console.error(` ${e}`);
console.error(`${errors.length} invalid entries; nothing imported`);
process.exit(1);
}
if (dry || !rows.length) process.exit(0);
if (!process.env.DB_URL)
throw new Error('DB_URL is required (use scripts/media-run.sh on stan-ovh)');
const sql = postgres(process.env.DB_URL, { max: 1, onnotice: () => {} });
try {
const r = await importRows(sql, rows);
console.log(`imported ${r.total}: ${r.created} new, ${r.updated} updated`);
} finally {
await sql.end();
}