feat(scripts): cached plugin bundle, single-plugin fetch, free-chapter prefix, nightly appender

- load-plugins: bundle once per source revision with an id index; loadPlugin(id)
  loads one plugin (144 ms / 87 MB instead of ~15 s / 1.2 GB).
- fetch-novel: keep only the contiguous run of free chapters (no locked teasers).
- update-novels: append newly free chapters to our EPUBs without moving positions.
- find-novel --json for programs; shared EPUB pieces in epub.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
masi
2026-09-26 14:05:51 -03:00
parent 750c2824ce
commit 5c444fc35a
5 changed files with 258 additions and 77 deletions
+10 -3
View File
@@ -1,13 +1,16 @@
// Search every plugin of the given languages for a title and list the sources
// that carry it. Usage: node scripts/find-novel.js "<title>" [lang...]
// Languages are plugin folder names (default: english multi).
// that carry it. Usage: node scripts/find-novel.js [--json] "<title>" [lang...]
// Languages are plugin folder names (default: english multi). --json prints one JSON
// array of {source, id, name, path, url, exact} for programs (the request page).
import { loadPlugins, print } from './load-plugins.js';
const TIMEOUT_MS = 20_000;
const CONCURRENCY = 16;
const PER_SOURCE = 3;
const [term, ...langArgs] = process.argv.slice(2);
const args = process.argv.slice(2);
const asJson = args[0] === '--json';
const [term, ...langArgs] = asJson ? args.slice(1) : args;
if (!term) {
console.error('Usage: node scripts/find-novel.js "<title>" [lang...]');
process.exit(2);
@@ -64,6 +67,10 @@ const matches = hits.filter(h =>
const rank = h => (norm(h.name) === exact ? 0 : 1);
matches.sort((a, b) => rank(a) - rank(b) || a.source.localeCompare(b.source));
if (asJson) {
print(JSON.stringify(matches.map(h => ({ ...h, exact: rank(h) === 0 }))));
process.exit(0);
}
for (const h of matches) {
print(
`${rank(h) === 0 ? '*' : ' '} ${h.source.padEnd(28)} ${h.name}\n` +