Consolidate MangaCircus app, scraper, and Compose baseline
This commit is contained in:
1 parent
cfb2d79103
commit
eef4ffa07c
103 files changed
+9316
-172
No files matched your search
+9
-24
@@ -7,30 +7,14 @@
|
||||
* Image sizes are cached per chapter folder and only re-read when the folder's mtime changes.
|
||||
*/
|
||||
const fsp = require('fs').promises, path = require('path');
|
||||
const { parseSpecialFolder, parseKey } = require('./chapter-identity');
|
||||
const { resolveReadingDirection } = require('./reading-direction');
|
||||
|
||||
const IMG = /\.(jpe?g|png|webp|gif|avif|bmp)$/i;
|
||||
const COVER = /^cover\.(jpe?g|png|webp|gif|avif|bmp)$/i;
|
||||
const KEY = /^([a-z]*) ?(\d+(?:\.\d+)?)(?=$|[\s_\-[(])[\s_-]*(.*)$/i;
|
||||
const DIRECTIONS = { rtl: 'rtl', ltr: 'ltr', ttb: 'ttb', vertical: 'ttb', webtoon: 'ttb' };
|
||||
const cmp = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }).compare;
|
||||
const str = x => (typeof x === 'string' ? x.trim() : '');
|
||||
const order = (a, b) => a.x - b.x || a.num - b.num;
|
||||
|
||||
// "c0002.5 Tea Time [MangaDex]" -> { key: '2.5', x: false, num: 2.5, title: 'Tea Time', src: 'MangaDex' }
|
||||
function parseKey(name) {
|
||||
const m = KEY.exec(name);
|
||||
if (!m) return null;
|
||||
const x = m[1].toLowerCase() === 'x', num = parseFloat(m[2]);
|
||||
let title = m[3].trim(), src = '';
|
||||
const b = /\s*\[([^\]]*)\]$/.exec(title);
|
||||
if (b) { src = b[1].trim(); title = title.slice(0, b.index).trim(); }
|
||||
return { key: (x ? 'x' : '') + num, x, num, title: title.replace(/^[-:.]\s*/, ''), src };
|
||||
}
|
||||
|
||||
function direction(...metas) {
|
||||
for (const m of metas) if (DIRECTIONS[str(m.direction).toLowerCase()]) return DIRECTIONS[str(m.direction).toLowerCase()];
|
||||
return 'rtl';
|
||||
}
|
||||
const order = (a, b) => Number(!!a.special) - Number(!!b.special) || a.x - b.x || a.num - b.num || cmp(a.key, b.key);
|
||||
|
||||
async function readJson(file) {
|
||||
try { return JSON.parse((await fsp.readFile(file, 'utf8')).replace(/^/, '')); }
|
||||
@@ -123,7 +107,7 @@ const coverIn = (files, rel) => { const f = files.find(f => COVER.test(f)); retu
|
||||
async function scanManga(root, name, oldCache, cache) {
|
||||
const { dirs, files } = await list(path.join(root, name));
|
||||
const meta = await readJson(path.join(root, name, 'manga.json'));
|
||||
const m = { id: name, title: str(meta.title) || name, meta, direction: direction(meta), cover: coverIn(files, name), books: [], chapters: [], chapterById: new Map(), updated: 0 };
|
||||
const m = { id: name, title: str(meta.title) || name, meta, direction: resolveReadingDirection([meta]).value, cover: coverIn(files, name), books: [], chapters: [], chapterById: new Map(), updated: 0 };
|
||||
const bookKeys = new Set();
|
||||
for (const bName of dirs) {
|
||||
const bk = parseKey(bName);
|
||||
@@ -138,14 +122,15 @@ async function scanManga(root, name, oldCache, cache) {
|
||||
};
|
||||
const byKey = new Map();
|
||||
for (const cName of bl.dirs) {
|
||||
const ck = parseKey(cName);
|
||||
const ck = parseSpecialFolder(cName) || parseKey(cName);
|
||||
if (!ck) { console.warn(`[scan] ignoring "${bRel}/${cName}": chapter folders must be named <letters><number>[ title], e.g. "c0001 The Seed"`); continue; }
|
||||
const v = await scanVersion(root, bRel + '/' + cName, ck, oldCache, cache);
|
||||
if (!v.pages.length) continue;
|
||||
v.direction = direction(v.meta, bMeta, meta);
|
||||
v.directionInfo = resolveReadingDirection([v.meta, bMeta, meta], v.pages);
|
||||
v.direction = v.directionInfo.value;
|
||||
let ch = byKey.get(ck.key);
|
||||
if (!ch) {
|
||||
ch = { id: book.key + '/' + ck.key, book: book.key, key: ck.key, num: ck.num, x: ck.x, extra: book.extra || ck.x, label: (ck.x ? 'Extra ' : 'Ch. ') + ck.num, title: '', versions: [] };
|
||||
ch = { id: book.key + '/' + ck.key, book: book.key, key: ck.key, num: ck.num, x: ck.x, special: !!ck.special, extra: book.extra || ck.x, label: ck.special ? str(v.meta.label) || 'Special' : (ck.x ? 'Extra ' : 'Ch. ') + ck.num, title: '', versions: [] };
|
||||
byKey.set(ck.key, ch);
|
||||
}
|
||||
if (v.meta.extra === true) ch.extra = true;
|
||||
@@ -179,4 +164,4 @@ async function scanLibrary(root, oldCache = {}) {
|
||||
return { index: { mangas, byId: new Map(mangas.map(m => [m.id, m])) }, cache };
|
||||
}
|
||||
|
||||
module.exports = { scanLibrary, parseKey, sizeFromBuffer };
|
||||
module.exports = { scanLibrary, parseKey, sizeFromBuffer, imageSize };
|
||||
Reference in new issue
Block a user