mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-10-16 05:25:42 -04:00
fix(imports): tolerate a UTF-8 BOM in uploaded Trakt JSON
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+19
-2
No files matched your search
@@ -424,6 +424,11 @@ function addTraktDocument(agg: TraktAggregate, json: unknown): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** JSON.parse that tolerates a leading UTF-8 BOM (as Blob.json() does). */
|
||||
function parseJsonText(text: string): unknown {
|
||||
return JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an uploaded Trakt export: Trakt's official ZIP (watched-history-*.json,
|
||||
* ratings-*.json, lists-watchlist.json, …), any single JSON file from it, or the
|
||||
@@ -447,7 +452,7 @@ export async function parseTraktExport(file: Blob): Promise<ParseResult> {
|
||||
const name = entry.entryName.split("/").pop() ?? entry.entryName;
|
||||
if (!name.toLowerCase().endsWith(".json")) continue;
|
||||
try {
|
||||
addTraktDocument(agg, JSON.parse(entry.getData().toString("utf-8")));
|
||||
addTraktDocument(agg, parseJsonText(entry.getData().toString("utf-8")));
|
||||
} catch {
|
||||
log.debug(`Skipping unreadable Trakt export entry: ${entry.entryName}`);
|
||||
}
|
||||
@@ -455,7 +460,7 @@ export async function parseTraktExport(file: Blob): Promise<ParseResult> {
|
||||
} else {
|
||||
let json: unknown;
|
||||
try {
|
||||
json = JSON.parse(buf.toString("utf8"));
|
||||
json = parseJsonText(buf.toString("utf8"));
|
||||
} catch {
|
||||
throw new Error("Invalid JSON file");
|
||||
}
|
||||
|
||||
@@ -1362,6 +1362,18 @@ describe("parseTraktExport", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("accepts a JSON file with a UTF-8 byte-order mark", async () => {
|
||||
const bytes = new Uint8Array([
|
||||
0xef,
|
||||
0xbb,
|
||||
0xbf,
|
||||
...new TextEncoder().encode(JSON.stringify(historyItems)),
|
||||
]);
|
||||
const result = await parseTraktExport(new Blob([bytes], { type: "application/json" }));
|
||||
expect(result.data.movies).toHaveLength(1);
|
||||
expect(result.data.episodes).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("skips an unparsable JSON entry inside the ZIP", async () => {
|
||||
const zip = createZip({
|
||||
"watched-history-1.json": JSON.stringify(historyItems),
|
||||
|
||||
Reference in new issue
Block a user