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}