55 lines
2.7 KiB
JavaScript
55 lines
2.7 KiB
JavaScript
'use strict';
|
|
|
|
const { normalizeWorkIdentity } = require('./work-identity');
|
|
|
|
function workKey(item) {
|
|
const identity = normalizeWorkIdentity(item.workIdentity);
|
|
if (identity) return `${identity.scheme}:${identity.kind}:${identity.value}`;
|
|
if (item.libraryMangaId) return `library:${item.libraryMangaId}`;
|
|
return `source:${item.source}:${item.seriesId || item.url}`;
|
|
}
|
|
|
|
function sourceKey(item) {
|
|
return `${item.source}:${item.seriesId || item.url}`;
|
|
}
|
|
|
|
function recordSource(ledger, item, observation = {}) {
|
|
const key = sourceKey(item);
|
|
const prior = ledger[key] || {};
|
|
const availability = observation.availability || item.availability;
|
|
const count = observation.catalogTotal ?? item.catalogTotal;
|
|
const observedAt = observation.observedAt || (count != null ? item.checked || item.completed : null) || null;
|
|
const catalog = count != null ? 'found' : prior.catalog || 'unverified';
|
|
const adapter = count != null
|
|
? Number(count) > 0 ? 'compatible' : availability?.reason === 'unrecognized-labels' ? 'unsupported-labels' : 'no-selected-pages'
|
|
: prior.adapter || 'unverified';
|
|
ledger[key] = {
|
|
...prior, workKey: workKey(item), source: item.source, seriesId: item.seriesId || null,
|
|
url: item.url || prior.url || null, title: item.title || prior.title || null,
|
|
sourceTitle: observation.sourceTitle || item.sourceTitle || prior.sourceTitle || null,
|
|
language: availability?.language || prior.language || null,
|
|
catalog, adapter, rights: prior.rights || 'unverified',
|
|
hostedCount: count == null ? prior.hostedCount ?? null : Number(count),
|
|
externalCount: availability?.externalReleases ?? prior.externalCount ?? null,
|
|
coverage: availability?.coverage || prior.coverage || 'unknown',
|
|
reason: observation.availability ? availability.reason || null : availability?.reason || prior.reason || null,
|
|
lastObservedAt: observedAt || prior.lastObservedAt || null,
|
|
evidence: count != null && observedAt ? { kind: 'scraper-catalog', jobId: item.id || null } : prior.evidence || { kind: 'queue-url', jobId: item.id || null },
|
|
};
|
|
if (observation.catalogTotal != null && Number(count) > 0) delete ledger[key].lastFailure;
|
|
return ledger[key];
|
|
}
|
|
|
|
function recordFailure(ledger, item, failure, at = new Date().toISOString()) {
|
|
const row = recordSource(ledger, item);
|
|
row.lastFailure = { kind: failure.kind, message: String(failure.message || '').slice(0, 300), at };
|
|
return row;
|
|
}
|
|
|
|
function ledgerRows(ledger) {
|
|
return Object.values(ledger || {}).sort((a, b) => String(a.title || '').localeCompare(String(b.title || ''))
|
|
|| String(a.source || '').localeCompare(String(b.source || '')));
|
|
}
|
|
|
|
module.exports = { workKey, sourceKey, recordSource, recordFailure, ledgerRows };
|