Add Greatest of All Time manga dashboard
This commit is contained in:
1 parent
c8dd46d0fb
commit
5df94db9a3
16 files changed
+2717
-1
No files matched your search
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const RATINGS = Object.freeze(['flame', 'good', 'neutral', 'skip']);
|
||||
const WEIGHT = Object.freeze({ flame: 3, good: 1, neutral: 0, skip: -2 });
|
||||
const normalize = value => String(value || '').normalize('NFKD').toLowerCase().replace(/[^a-z0-9]+/g, '');
|
||||
|
||||
function loadCatalog(file = path.join(__dirname, 'catalog.json')) {
|
||||
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
if (parsed.schema !== 1 || !Array.isArray(parsed.entries)) throw new Error('Invalid Greatest of All Time catalog');
|
||||
const ids = new Set();
|
||||
for (const entry of parsed.entries) {
|
||||
if (!/^[a-z0-9-]+$/.test(entry.id) || ids.has(entry.id) || !entry.title || !entry.period || !Number.isInteger(entry.rank))
|
||||
throw new Error(`Invalid or duplicate Greatest of All Time entry: ${entry.id}`);
|
||||
if (entry.cover && !/^https:\/\//.test(entry.cover)) throw new Error(`Invalid cover URL for ${entry.id}`);
|
||||
if (entry.source?.url && !/^https:\/\//.test(entry.source.url)) throw new Error(`Invalid source URL for ${entry.id}`);
|
||||
ids.add(entry.id);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function findOwned(entry, mangas) {
|
||||
const names = new Set([entry.title, ...(entry.aliases || [])].map(normalize));
|
||||
return mangas.find(m => {
|
||||
const alt = m.meta?.altTitles;
|
||||
return [m.title, ...(Array.isArray(alt) ? alt : alt ? [alt] : [])].some(name => names.has(normalize(name)));
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function countsFor(entryId, votes, users) {
|
||||
const counts = { flame: 0, good: 0, neutral: 0, skip: 0 };
|
||||
for (const name of Object.keys(users)) {
|
||||
const rating = votes[name]?.[entryId];
|
||||
if (RATINGS.includes(rating)) counts[rating]++;
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
function scoreFor(counts) {
|
||||
return RATINGS.reduce((sum, rating) => sum + counts[rating] * WEIGHT[rating], 0);
|
||||
}
|
||||
|
||||
module.exports = { RATINGS, WEIGHT, normalize, loadCatalog, findOwned, countsFor, scoreFor };
|
||||
Reference in new issue
Block a user