Skip to content

Commit dca801e

Browse files
committed
Read manifest in obz files
1 parent 9c25a40 commit dca801e

1 file changed

Lines changed: 54 additions & 13 deletions

File tree

src/processors/obfProcessor.ts

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ interface ObfButton {
5151
image_id?: string; // Reference to image in the images array
5252
}
5353

54+
interface ObfManifest {
55+
root?: string;
56+
paths?: {
57+
boards?: { [key: string]: string };
58+
images?: { [key: string]: string | ObfImage };
59+
sounds?: { [key: string]: string };
60+
};
61+
}
62+
63+
interface ObfImage {
64+
id: number;
65+
path: string;
66+
width: number;
67+
height: number;
68+
content_type: string;
69+
}
70+
5471
/**
5572
* Map OBF hidden value to AAC standard visibility
5673
* OBF: true = hidden, false/undefined = visible
@@ -201,15 +218,14 @@ class ObfProcessor extends BaseProcessor {
201218
}
202219
}
203220

204-
private async processBoard(boardData: ObfBoard, _boardPath: string): Promise<AACPage> {
221+
private async processBoard(
222+
boardData: ObfBoard,
223+
_boardPath: string,
224+
isZipEntry: boolean
225+
): Promise<AACPage> {
205226
const sourceButtons = boardData.buttons || [];
206227

207228
// Calculate page ID first (used to make button IDs unique)
208-
const isZipEntry =
209-
_boardPath &&
210-
_boardPath.endsWith('.obf') &&
211-
!_boardPath.includes('/') &&
212-
!_boardPath.includes('\\');
213229
const pageId = isZipEntry
214230
? _boardPath // Zip entry - use filename to match navigation paths
215231
: boardData?.id
@@ -437,7 +453,7 @@ class ObfProcessor extends BaseProcessor {
437453
const boardData = tryParseObfJson(content);
438454
if (boardData) {
439455
console.log('[OBF] Detected .obf file, parsed as JSON');
440-
const page = await this.processBoard(boardData, filePathOrBuffer);
456+
const page = await this.processBoard(boardData, filePathOrBuffer, false);
441457
tree.addPage(page);
442458

443459
// Set metadata from root board
@@ -464,7 +480,7 @@ class ObfProcessor extends BaseProcessor {
464480
const asJson = tryParseObfJson(filePathOrBuffer);
465481
if (asJson) {
466482
console.log('[OBF] Detected buffer/string as OBF JSON');
467-
const page = await this.processBoard(asJson, '[bufferOrString]');
483+
const page = await this.processBoard(asJson, '[bufferOrString]', false);
468484
tree.addPage(page);
469485

470486
// Set metadata from root board
@@ -511,18 +527,43 @@ class ObfProcessor extends BaseProcessor {
511527

512528
console.log('[OBF] Detected zip archive, extracting .obf files');
513529

514-
// Collect all .obf entries
515-
const obfEntries = this.zipFile
516-
.listFiles()
517-
.filter((name) => name.toLowerCase().endsWith('.obf'));
530+
// List manifest and OBF files
531+
const filesInZip = this.zipFile.listFiles();
532+
const manifestFile = filesInZip.filter((name) => name.toLowerCase() === 'manifest.json');
533+
let obfEntries = filesInZip.filter((name) => name.toLowerCase().endsWith('.obf'));
534+
535+
// Attempt to read manifest
536+
if (manifestFile && manifestFile.length === 1) {
537+
try {
538+
const content = await this.zipFile.readFile(manifestFile[0]);
539+
const data = decodeText(content);
540+
const str = typeof data === 'string' ? data : readTextFromInput(data);
541+
if (!str.trim()) throw new Error('Manifest object missing');
542+
const manifestObject = JSON.parse(str) as ObfManifest;
543+
if (!manifestObject) throw new Error('Manifest object is empty');
544+
545+
// Replace OBF file list
546+
if (manifestObject.paths && manifestObject.paths.boards) {
547+
obfEntries = Object.values(manifestObject.paths.boards);
548+
}
549+
550+
// Move root board to top of list
551+
if (manifestObject.root) {
552+
obfEntries = obfEntries.filter((item) => item !== manifestObject.root);
553+
obfEntries.unshift(manifestObject.root);
554+
}
555+
} catch (err) {
556+
console.warn('[OBF] Error processing mainfest', err);
557+
}
558+
}
518559

519560
// Process each .obf entry
520561
for (const entryName of obfEntries) {
521562
try {
522563
const content = await this.zipFile.readFile(entryName);
523564
const boardData = tryParseObfJson(decodeText(content));
524565
if (boardData) {
525-
const page = await this.processBoard(boardData, entryName);
566+
const page = await this.processBoard(boardData, entryName, true);
526567
tree.addPage(page);
527568

528569
// Set metadata if not already set (use first board as reference)

0 commit comments

Comments
 (0)