code wiki / (root) / nx_galx_vidindex.nx

nx_galx_vidindex.nx source

↩ module page · 181 lines · 8544 B

1// nx_galx_vidindex.nx -- SOVEREIGN recordings index. Recursively scans the cam-recording archive 2// (sys_getdents64, no bash/find) and writes knowledge/status/galx_vid_paths.tsv = one video path per line. 3// Each cam recording is <model>_<date>_<time>.ts with a sibling <...>-poster.jpg the daemon serves as the 4// thumbnail (so recordings show instantly with NO video decode). Skips #recycle and .trickplay dirs. 5// Usage: nx_galx_vidindex (no args = full scan). license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 8const VI_MAGIC_8192: i64 = 8192 9const VI_MAGIC_8191: i64 = 8191 10const VI_MAGIC_2048: i64 = 2048 11 12const GBUF: i64 = 65536 13const VI_OUT: *u8 = "/volume1/ai/galx/knowledge/status/galx_vid_paths.tsv" as *u8 // the LIVE catalog nx_gallery_serve opens (NAS-native; the old /mnt/c path failed to write on the NAS) 14const VI_TMP: *u8 = "/volume1/ai/galx/knowledge/status/galx_vid_paths.tsv.reidx" as *u8 // build here, atomic-rename onto VI_OUT so the gallery never reads a half-written catalog 15const VI_OFF: *u8 = "/volume1/ai/galx/knowledge/status/galx_vid_off.bin" as *u8 16const VI_OFF_TMP: *u8 = "/volume1/ai/galx/knowledge/status/galx_vid_off.bin.reidx" as *u8 17 18func vi_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 19// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 20// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 21// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 22// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 23func vi_n(v: i64) -> i64 { nxi_out(v); return 0 } 24func vi_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 25// does name end with one of the video extensions? 26func vi_is_vid(name: *u8) -> i64 { 27 let n: i64 = vi_len(name) 28 if n >= 3 { if name[n-3]==(46 as u8) { if name[n-2]==(116 as u8) { if name[n-1]==(115 as u8) { return 1 } } } } // .ts 29 if n >= 4 { if name[n-4]==(46 as u8) { 30 if name[n-3]==(109 as u8) { if name[n-2]==(112 as u8) { if name[n-1]==(52 as u8) { return 1 } } } // .mp4 31 if name[n-3]==(109 as u8) { if name[n-2]==(107 as u8) { if name[n-1]==(118 as u8) { return 1 } } } // .mkv 32 if name[n-3]==(102 as u8) { if name[n-2]==(108 as u8) { if name[n-1]==(118 as u8) { return 1 } } } // .flv 33 } } 34 return 0 35} 36// does name end with ".trickplay" (a Jellyfin scrub-preview dir to skip)? 37func vi_is_trick(name: *u8) -> i64 { 38 let n: i64 = vi_len(name) 39 let suf: *u8 = ".trickplay" as *u8 40 if n < 10 { return 0 } 41 var i: i64 = 0 42 while i < 10 { if name[n-10+i] != suf[i] { return 0 } i = i + 1 } 43 return 1 44} 45func vi_walk(dir: *u8, depth: i64, outfd: i64, cntp: *i64) -> i64 { 46 if depth > 6 { return 0 } 47 let fd: i64 = sys_openat_rd(dir) 48 if fd < 0 { return 0 } 49 let gbuf: *u8 = sys_mmap(GBUF) 50 let child: *u8 = sys_mmap(VI_MAGIC_2048) 51 let dlen: i64 = vi_len(dir) 52 var go: i64 = 1 53 while go == 1 { 54 let n: i64 = sys_getdents64(fd, gbuf, GBUF) 55 if n <= 0 { go = 0 } else { 56 var off: i64 = 0 57 while off < n { 58 let rec: *u8 = ((gbuf as i64) + off) as *u8 59 let reclen: i64 = dirent_reclen(rec) 60 let typ: i64 = dirent_type(rec) 61 let name: *u8 = dirent_name(rec) 62 var skip: i64 = 0 63 if name[0]==(46 as u8) { if name[1]==(0 as u8) { skip=1 } else { if name[1]==(46 as u8) { if name[2]==(0 as u8) { skip=1 } } } } 64 if name[0]==(35 as u8) { skip=1 } // '#recycle' 65 if skip == 0 { 66 var co: i64 = 0 67 while co < dlen { child[co]=dir[co]; co=co+1 } 68 child[co]=47 as u8; co=co+1 69 var ni: i64 = 0 70 while name[ni]!=(0 as u8) { child[co]=name[ni]; co=co+1; ni=ni+1 } 71 child[co]=0 as u8 72 if vi_is_vid(name)==1 { 73 sys_write(outfd, child, co); sys_write(outfd, "\n" as *u8, 1); cntp[0]=cntp[0]+1 74 } else { 75 if vi_is_trick(name)==0 { if typ != DT_REG { vi_walk(child, depth+1, outfd, cntp) } } 76 } 77 } 78 if reclen <= 0 { off = n } else { off = off + reclen } 79 } 80 } 81 } 82 sys_close(fd) 83 return 0 84} 85func vi_contains(buf: *u8, n: i64, needle: *u8) -> i64 { 86 let m: i64 = vi_len(needle) 87 if m == 0 { return 1 } 88 var i: i64 = 0 89 while i + m <= n { 90 var j: i64 = 0 91 var ok: i64 = 1 92 while j < m { if buf[i+j] != needle[j] { ok = 0; j = m } else { j = j + 1 } } 93 if ok == 1 { return 1 } 94 i = i + 1 95 } 96 return 0 97} 98func vi_copy_nontorrent(srcpath: *u8, dstfd: i64, cntp: *i64) -> i64 { 99 let fd: i64 = sys_openat_rd(srcpath) 100 if fd < 0 { return 0 } 101 let chunk: *u8 = sys_mmap(GBUF) 102 let line: *u8 = sys_mmap(VI_MAGIC_8192) 103 let needle: *u8 = "/volume1/ai/torrent/" as *u8 104 var ll: i64 = 0 105 var go: i64 = 1 106 while go == 1 { 107 let r: i64 = sys_read(fd, chunk, GBUF) 108 if r <= 0 { go = 0 } else { 109 var i: i64 = 0 110 while i < r { 111 if chunk[i] == (10 as u8) { 112 if ll > 0 { if vi_contains(line, ll, needle) == 0 { sys_write(dstfd, line, ll); sys_write(dstfd, "\n" as *u8, 1); cntp[0]=cntp[0]+1 } } 113 ll = 0 114 } else { 115 if ll < VI_MAGIC_8191 { line[ll]=chunk[i]; ll=ll+1 } 116 } 117 i = i + 1 118 } 119 } 120 } 121 if ll > 0 { if vi_contains(line, ll, needle) == 0 { sys_write(dstfd, line, ll); sys_write(dstfd, "\n" as *u8, 1); cntp[0]=cntp[0]+1 } } 122 sys_close(fd) 123 return 0 124} 125func vi_wb64(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v>>56)&0xff) as u8; b[o+1]=((v>>48)&0xff) as u8; b[o+2]=((v>>40)&0xff) as u8; b[o+3]=((v>>32)&0xff) as u8; b[o+4]=((v>>24)&0xff) as u8; b[o+5]=((v>>16)&0xff) as u8; b[o+6]=((v>>8)&0xff) as u8; b[o+7]=(v&0xff) as u8; return o+8 } 126func vi_vidoff_build(pidsuf: *u8) -> i64 { 127 let vo: *u8 = VI_OUT 128 let szp: *i64 = sys_mmap(16) as *i64 129 let b: *u8 = sys_read_file(vo, szp) 130 if (b as i64)==0 { return 0 } 131 let sz: i64 = szp[0] 132 var n: i64 = 1; var i: i64 = 0 133 while i < sz { if b[i]==(10 as u8) { if i+1 < sz { n=n+1 } } i=i+1 } 134 let ob: *u8 = sys_mmap(16 + n*8 + 64) 135 vi_wb64(ob, 0, sz); vi_wb64(ob, 8, n); vi_wb64(ob, 16, 0) 136 var cnt: i64 = 1; i = 0 137 while i < sz { if b[i]==(10 as u8) { if i+1 < sz { if cnt < n { vi_wb64(ob, 16+cnt*8, i+1); cnt=cnt+1 } } } i=i+1 } 138 let vt2: *u8 = VI_OFF_TMP 139 let tmp: *u8 = sys_mmap(VI_MAGIC_2048) 140 var o2: i64 = 0 141 while vt2[o2] != (0 as u8) { tmp[o2]=vt2[o2]; o2=o2+1 } 142 var k: i64 = 0 143 while pidsuf[k] != (0 as u8) { tmp[o2]=pidsuf[k]; o2=o2+1; k=k+1 } 144 tmp[o2]=0 as u8 145 let wf: i64 = sys_openat_wr(tmp, 0x1a4) 146 if wf < 0 { return 0 } 147 sys_write(wf, ob, 16+n*8); sys_close(wf) 148 let voff: *u8 = VI_OFF 149 sys_renameat(tmp, voff) 150 return 1 151} 152func main(argc: i64, argv: *i64) -> i64 { 153 let vt: *u8 = VI_TMP 154 let vitmp: *u8 = sys_mmap(VI_MAGIC_2048) 155 var vto: i64 = 0 156 while vt[vto] != (0 as u8) { vitmp[vto]=vt[vto]; vto=vto+1 } 157 let vpid: i64 = __syscall(172, 0, 0, 0, 0, 0, 0) 158 let vd: *u8 = sys_mmap(28); var vm: i64 = vpid; var vk: i64 = 0 159 if vm <= 0 { vd[0]=48 as u8; vk=1 } 160 while vm > 0 { vd[vk]=(48+(vm%10)) as u8; vm=vm/10; vk=vk+1 } 161 var vq: i64 = 0 162 while vq < vk { vitmp[vto]=vd[vk-1-vq]; vto=vto+1; vq=vq+1 } 163 vitmp[vto]=0 as u8 164 let pidsuf: *u8 = sys_mmap(32) 165 var vz: i64 = 0 166 while vz < vk { pidsuf[vz]=vd[vk-1-vz]; vz=vz+1 } 167 pidsuf[vk]=0 as u8 168 let outfd: i64 = sys_openat_wr(vitmp, 0x1a4) 169 if outfd < 0 { vi_p("VIDINDEX FAIL: cannot write index\n" as *u8); sys_exit(1); return 1 } 170 let cntp: *i64 = sys_mmap(16) as *i64 171 cntp[0] = 0 172 vi_copy_nontorrent(VI_OUT, outfd, cntp) 173 vi_walk("/volume1/ai/torrent/media" as *u8, 0, outfd, cntp) 174 vi_walk("/volume1/ai/torrent/gallery" as *u8, 0, outfd, cntp) 175 sys_close(outfd) 176 sys_renameat(vitmp, VI_OUT) 177 let voffok: i64 = vi_vidoff_build(pidsuf) 178 vi_p("VIDINDEX OK videos=" as *u8); vi_n(cntp[0]); vi_p(" vidoff=" as *u8); vi_n(voffok); vi_p(" -> " as *u8); vi_p(VI_OUT); vi_p("\n" as *u8) 179 sys_exit(0) 180 return 0 181}