code wiki / (root) / nx_mvault_context.nx

nx_mvault_context.nx source

↩ module page · 72 lines · 3000 B

1// nx_mvault_context.nx -- CONTEXTUAL ingestion: type from surrounding context, 2// not just per-file magic bytes. 3// 4// WHY (operator 2026-07-17): "ingest ... hentai formats or other things that 5// may look like images but really be books ... the ingestion to be contextual." 6// A numbered image sequence in a gallery/doujin dir is ONE book, not N loose 7// images; a .cbz/.cbr is a comic; a .pdf/.epub is a book. The per-file sniffer 8// (nx_media_sniff) sees only bytes -- it would file every doujin page as a 9// separate loose IMAGE. This layer applies the CONTEXT the reindexer computes 10// (archive magic, in-a-numbered-sequence, ebook) to fold them into one work. 11// 12// The reindexer computes a ctx flag per item (from dir scan + these magic 13// detectors + extension) and calls mv_context_type; a whole image-sequence 14// dir becomes ONE MANGA item (its pages are members, not separate items). 15// 16// Composes nx_mvault_ingest (mv_type / MV_TYPE_*). license_tier: ORIGINAL 17 18import "nx_syscalls.nx" 19import "nx_media_pool.nx" 20import "nx_mvault_ingest.nx" 21 22// ===== context flags (reindexer-computed) ===== 23const MV_CTX_NONE: i64 = 0 // no context -> per-file sniff decides 24const MV_CTX_PDF: i64 = 1 // %PDF -> book 25const MV_CTX_EPUB: i64 = 2 // .epub (zip) -> book 26const MV_CTX_CBZ: i64 = 3 // comic zip -> manga 27const MV_CTX_CBR: i64 = 4 // comic rar -> manga 28const MV_CTX_IMGSEQ: i64 = 5 // page N of a numbered image gallery -> manga 29 30// ===== container magic detectors (building blocks for ctx computation) ===== 31 32// PDF: "%PDF" 33func mv_is_pdf(b: *u8, n: i64) -> i64 { 34 if n < 4 { return 0 } 35 if b[0] != (0x25 as u8) { return 0 } // % 36 if b[1] != (0x50 as u8) { return 0 } // P 37 if b[2] != (0x44 as u8) { return 0 } // D 38 if b[3] != (0x46 as u8) { return 0 } // F 39 return 1 40} 41 42// ZIP local file header: "PK\x03\x04" (cbz / epub / docx are zip containers) 43func mv_is_zip(b: *u8, n: i64) -> i64 { 44 if n < 4 { return 0 } 45 if b[0] != (0x50 as u8) { return 0 } // P 46 if b[1] != (0x4B as u8) { return 0 } // K 47 if b[2] != (0x03 as u8) { return 0 } 48 if b[3] != (0x04 as u8) { return 0 } 49 return 1 50} 51 52// RAR: "Rar!" (cbr comic archives) 53func mv_is_rar(b: *u8, n: i64) -> i64 { 54 if n < 4 { return 0 } 55 if b[0] != (0x52 as u8) { return 0 } // R 56 if b[1] != (0x61 as u8) { return 0 } // a 57 if b[2] != (0x72 as u8) { return 0 } // r 58 if b[3] != (0x21 as u8) { return 0 } // ! 59 return 1 60} 61 62// ===== contextual type ===== 63// ctx wins (it encodes the surrounding structure); else fall back to the 64// per-file sniffed type via mv_type. 65func mv_context_type(sniff_type: i64, ctx: i64) -> i64 { 66 if ctx == MV_CTX_PDF { return MV_TYPE_BOOK } 67 if ctx == MV_CTX_EPUB { return MV_TYPE_BOOK } 68 if ctx == MV_CTX_CBZ { return MV_TYPE_MANGA } 69 if ctx == MV_CTX_CBR { return MV_TYPE_MANGA } 70 if ctx == MV_CTX_IMGSEQ { return MV_TYPE_MANGA } 71 return mv_type(MV_HINT_NONE, sniff_type) 72}