Initial commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { parseNfo, type NfoMetadata } from "./nfoParser";
|
||||
|
||||
/**
|
||||
* Lenient JSON metadata parser. Accepts a wide range of common keys produced by
|
||||
* scrapers (Kodi/Jellyfin/Stash/JavSP/etc.) and normalizes them into the same
|
||||
* shape as `NfoMetadata` so the cover editor can pre-fill from a file or a
|
||||
* pasted blob.
|
||||
*/
|
||||
export function parseMetaJson(raw: string): NfoMetadata | null {
|
||||
let obj: unknown;
|
||||
try {
|
||||
obj = JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (!obj || typeof obj !== "object") return null;
|
||||
|
||||
// Some scrapers wrap in {"data": {...}} or arrays.
|
||||
const root = (obj as Record<string, unknown>);
|
||||
const data = (root.data && typeof root.data === "object" ? root.data : root) as Record<string, unknown>;
|
||||
|
||||
const pick = (...keys: string[]): string | undefined => {
|
||||
for (const k of keys) {
|
||||
const v = data[k];
|
||||
if (typeof v === "string" && v.trim()) return v.trim();
|
||||
if (typeof v === "number" && Number.isFinite(v)) return String(v);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const pickArray = (...keys: string[]): string[] => {
|
||||
for (const k of keys) {
|
||||
const v = data[k];
|
||||
if (Array.isArray(v)) {
|
||||
return v
|
||||
.map((x) => {
|
||||
if (typeof x === "string") return x.trim();
|
||||
if (x && typeof x === "object") {
|
||||
const name = (x as Record<string, unknown>).name ?? (x as Record<string, unknown>).Name;
|
||||
return typeof name === "string" ? name.trim() : "";
|
||||
}
|
||||
return "";
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
if (typeof v === "string" && v.trim()) {
|
||||
return v.split(/[,、,]/).map((s) => s.trim()).filter(Boolean);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const runtimeRaw = pick("runtime", "runtimeMin", "runtime_min", "duration");
|
||||
const runtimeMin = runtimeRaw ? parseInt(runtimeRaw, 10) : NaN;
|
||||
|
||||
const out: NfoMetadata = {
|
||||
title: pick("title", "originaltitle", "originalTitle", "name"),
|
||||
code: pick("code", "id", "num", "number", "javId", "jav_id"),
|
||||
releaseDate: pick("releaseDate", "release_date", "premiered", "releasedate", "date", "year"),
|
||||
runtimeMin: Number.isFinite(runtimeMin) ? runtimeMin : undefined,
|
||||
director: pick("director"),
|
||||
studio: pick("studio", "maker", "manufacturer"),
|
||||
series: pick("series", "set"),
|
||||
actresses: pickArray("actresses", "actors", "actor", "performers", "cast", "stars"),
|
||||
genres: pickArray("genres", "genre", "tags", "categories"),
|
||||
notes: pick("plot", "outline", "summary", "description", "notes"),
|
||||
};
|
||||
|
||||
return strip(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try JSON first, fall back to XML. Returns null if neither matches.
|
||||
*/
|
||||
export function parseMetaAny(raw: string): NfoMetadata | null {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return null;
|
||||
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
||||
return parseMetaJson(trimmed);
|
||||
}
|
||||
return parseNfo(trimmed);
|
||||
}
|
||||
|
||||
function strip(o: NfoMetadata): NfoMetadata {
|
||||
const out: NfoMetadata = {};
|
||||
for (const [k, v] of Object.entries(o)) {
|
||||
if (v == null) continue;
|
||||
if (Array.isArray(v) && v.length === 0) continue;
|
||||
if (typeof v === "string" && v === "") continue;
|
||||
(out as Record<string, unknown>)[k] = v;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user