code wiki / _hdl_build / nx_gallery_serve.nx
nx_gallery_serve.nx source
↩ module page · 2617 lines · 190004 B
1// nx_gallery_serve.nx -- SOVEREIGN Nishi image-library gallery daemon (port 18090). Pinterest-style masonry
2// of the FULL-RESOLUTION images (no thumbnails) with infinite scroll, sort, search, and STATE FILTERS:
3// viewed (clicked), rated, and favorite. Favorite = the LoRA-training flag (knowledge/status/galx_fav.log
4// is the training candidate set a downstream LoRA/i2i pipeline consumes). Clicking opens the lossless image
5// with the generation-factor panel and a rating control; opening marks it viewed.
6//
7// GET / -> gallery shell (masonry + infinite scroll + filters + lightbox)
8// GET /api/list?o=&n=&s=&q=&filter= -> {"total":N,"items":[cid...]} (filter=all|fav|rated|viewed)
9// GET /img/<cid> -> full-resolution lossless source PNG
10// GET /meta/<cid> -> {factor:value,...} from the PNG tEXt/iTXt
11// GET /state/<cid> -> {"fav":0|1,"rating":N,"viewed":0|1}
12// POST /view img= -> mark viewed (append galx_views.log)
13// POST /fav img=&v= -> set favorite 0/1 (append galx_fav.log) [LoRA-training flag]
14// POST /rate img=&score= -> append one EVAL O-line
15// All logic is NishiLang; bash is only ever a launcher. license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_ts_marks.nx" // T1 clip-analysis: ts_classify (markers+dur -> content label) for /vid/<id>/analysis
18import "nx_search_inverted_persist.nx" // durable inverted index (nx_inv_load/query + tokenizer + FNV hash)
19import "nx_bm25.nx" // Okapi BM25 primitives (bm_idf_micro / bm_sat_milli, integer x1e6)
20import "nx_galx_cid_index.nx"
21import "nx_galx_cidput.nx" // sovereign O(1) cid->path store (replaces the per-request galx_cid_paths.tsv scan)
22import "nx_galx_shell_jslint.nx" // sovereign JS-syntax guard for the inline shell <script> (no JS engine here)
23import "nx_prefix.nx" // R-UX-3 as-you-type autocomplete (sorted-vocab binary-search lower bound)
24import "nx_galx_sortindex.nx" // R2 sort engine: gated stable merge-sort (si_msort) for sort-by-key
25import "nx_galx_durbin_lib.nx" // R2 perf: O(1) galx_dur.bin dur lookup (db_lookup) -> no 16MB /dur PCR re-scan
26import "nx_galx_tags.nx" // R4 media-manager: append-only tag store (tg_*, gated nx_galx_tags_gate)
27const VIEW_MAGIC_2048: i64 = 2048
28const VIEW_MAGIC_2000: i64 = 2000
29const VIEW_MAGIC_1024: i64 = 1024
30const VIEW_MAGIC_1048576: i64 = 1048576
31const VIEW_MAGIC_8388608: i64 = 8388608
32const VIEW_MAGIC_7776000000: i64 = 7776000000
33const VIEW_MAGIC_1000000: i64 = 1000000
34const VIEW_MAGIC_8191: i64 = 8191
35const VIEW_MAGIC_50000000: i64 = 50000000
36const VIEW_MAGIC_262144: i64 = 262144
37const VIEW_MAGIC_262208: i64 = 262208
38const VIEW_MAGIC_4096: i64 = 4096
39const VIEW_MAGIC_1500000: i64 = 1500000
40const VIEW_MAGIC_65536: i64 = 65536
41const VIEW_MAGIC_258000: i64 = 258000
42const VIEW_MAGIC_200000: i64 = 200000
43const VIEW_MAGIC_250000: i64 = 250000
44const VIEW_MAGIC_1100: i64 = 1100
45const VIEW_MAGIC_8192: i64 = 8192
46const VIEW_MAGIC_16384: i64 = 16384
47const VIEW_MAGIC_60000: i64 = 60000
48const VIEW_MAGIC_131072: i64 = 131072
49const VIEW_MAGIC_100000: i64 = 100000
50const VIEW_MAGIC_200008: i64 = 200008
51const VIEW_MAGIC_18090: i64 = 18090
52const VIEW_MAGIC_4194304: i64 = 4194304
53
54const VIEW_IDX: *u8 = "knowledge/status/galx_view_index.txt" as *u8
55const SEARCH_IDX: *u8 = "knowledge/status/galx_search.tsv" as *u8
56const FAV_LOG: *u8 = "knowledge/status/galx_fav.log" as *u8
57const VIEW_LOG: *u8 = "knowledge/status/galx_views.log" as *u8
58const EVAL_LOG: *u8 = "knowledge/status/eval_lanes.log" as *u8
59const HIDDEN_LOG: *u8 = "knowledge/status/galx_hidden.log" as *u8 // R3 soft-hide: append "<id> <0|1>" (reversible)
60const TAGS_LOG: *u8 = "knowledge/status/galx_tags.log" as *u8 // R4 tags: append "<id>\t<tag>\t<0|1>" (last-wins, additive)
61
62func eh_find(buf: *u8, n: i64, pat: *u8, plen: i64) -> i64 {
63 var i: i64 = 0
64 while i + plen <= n {
65 var j: i64 = 0
66 var ok: i64 = 1
67 while j < plen { if buf[i+j] != pat[j] { ok = 0 } j = j + 1 }
68 if ok == 1 { return 1 }
69 i = i + 1
70 }
71 return 0
72}
73func gs_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off+i]=s[i];i=i+1} return off+i }
74func gs_u(dst: *u8, off: i64, v: i64) -> i64 {
75 var m: i64 = v
76 let t: *u8 = sys_mmap(28)
77 var k: i64 = 0
78 if m == 0 { t[0] = 48 as u8; k = 1 }
79 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
80 var o: i64 = off
81 var i: i64 = 0
82 while i < k { dst[o] = t[k-1-i]; o = o + 1; i = i + 1 }
83 return o
84}
85func gs_strlen(s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){i=i+1} return i }
86func gs_lower(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
87func gs_qint(req: *u8, rn: i64, key: *u8, deflt: i64) -> i64 {
88 let kl: i64 = gs_strlen(key)
89 var i: i64 = 0
90 var at: i64 = 0 - 1
91 var stop: i64 = rn
92 while i < stop {
93 if req[i] == (10 as u8) { stop = i } else {
94 if at < 0 { if i + kl <= rn { var j: i64=0; var ok: i64=1; while j<kl { if req[i+j]!=key[j]{ok=0} j=j+1 } if ok==1 { at=i+kl } } }
95 i = i + 1
96 }
97 }
98 if at < 0 { return deflt }
99 var v: i64 = 0; var any: i64 = 0; var q: i64 = at
100 while q < rn { if req[q] >= (48 as u8) { if req[q] <= (57 as u8) { v=v*10+((req[q] as i64)-48); any=1; q=q+1 } else { q=rn } } else { q=rn } }
101 if any == 0 { return deflt }
102 return v
103}
104func gs_404(rbuf: *u8) -> i64 { return gs_cat(rbuf, 0, "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 9\r\nConnection: close\r\n\r\nnot found" as *u8) }
105func gs_okjson(rbuf: *u8, body: *u8, blen: i64) -> i64 {
106 var o: i64 = 0
107 o = gs_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\nCache-Control: no-store\r\nContent-Length: " as *u8)
108 o = gs_u(rbuf, o, blen)
109 o = gs_cat(rbuf, o, "\r\n\r\n" as *u8)
110 var i: i64 = 0
111 while i < blen { rbuf[o] = body[i]; o = o + 1; i = i + 1 }
112 return o
113}
114
115// gs_okjson's twin, one content type apart -- ONE responder pair, not a second convention. This
116// daemon had no HTML sender at all, which is exactly why every HTML surface it has was being built by
117// client-side JS: there was nothing native to emit through.
118func gs_okhtml(rbuf: *u8, body: *u8, blen: i64) -> i64 {
119 var o: i64 = 0
120 o = gs_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nCache-Control: no-store\r\nContent-Length: " as *u8)
121 o = gs_u(rbuf, o, blen)
122 o = gs_cat(rbuf, o, "\r\n\r\n" as *u8)
123 var i: i64 = 0
124 while i < blen { rbuf[o] = body[i]; o = o + 1; i = i + 1 }
125 return o
126}
127
128// 303 See Other -- the POST-Redirect-GET leg. WITHOUT it a browser reload re-submits the form and
129// starts a SECOND ingest of the same thread; WITH it the result page is a plain GET that is safe to
130// reload. An HTTP primitive doing the job a client state machine was doing.
131func gs_see_other(rbuf: *u8, loc: *u8) -> i64 {
132 var o: i64 = 0
133 o = gs_cat(rbuf, o, "HTTP/1.1 303 See Other\r\nLocation: " as *u8)
134 o = gs_cat(rbuf, o, loc)
135 o = gs_cat(rbuf, o, "\r\nContent-Length: 0\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8)
136 return o
137}
138
139func gs_peel_cid(req: *u8, rn: i64, cidout: *u8) -> i64 {
140 let pat: *u8 = "GET /img/" as *u8
141 var i: i64 = 0; var at: i64 = 0 - 1
142 while i + 9 <= rn { var j: i64=0; var ok: i64=1; while j<9 { if req[i+j]!=pat[j]{ok=0} j=j+1 } if ok==1 { if at<0 { at=i+9 } } i=i+1 }
143 if at < 0 { return 0 }
144 var c: i64 = 0
145 while c < 69 { cidout[c] = req[at + c]; c = c + 1 }
146 cidout[69] = 0 as u8
147 return 1
148}
149func gs_peel_pat(req: *u8, rn: i64, pat: *u8, plen: i64, cidout: *u8) -> i64 {
150 var i: i64 = 0; var at: i64 = 0 - 1
151 while i + plen <= rn { var j: i64=0; var ok: i64=1; while j<plen { if req[i+j]!=pat[j]{ok=0} j=j+1 } if ok==1 { if at<0 { at=i+plen } } i=i+1 }
152 if at < 0 { return 0 }
153 var c: i64 = 0
154 while c < 69 { cidout[c] = req[at + c]; c = c + 1 }
155 cidout[69] = 0 as u8
156 return 1
157}
158// extract the img= CID value from a POST body (69 chars). returns 1 ok.
159func gs_body_cid(req: *u8, rn: i64, cidout: *u8) -> i64 {
160 var bodyat: i64 = 0 - 1
161 var i: i64 = 0
162 while i + 4 <= rn { if req[i]==(13 as u8) { if req[i+1]==(10 as u8) { if req[i+2]==(13 as u8) { if req[i+3]==(10 as u8) { if bodyat<0 { bodyat=i+4 } } } } } i=i+1 }
163 if bodyat < 0 { return 0 }
164 var iat: i64 = 0 - 1
165 var pi: i64 = bodyat
166 while pi + 4 <= rn { if req[pi]==(105 as u8) { if req[pi+1]==(109 as u8) { if req[pi+2]==(103 as u8) { if req[pi+3]==(61 as u8) { if iat<0 { iat=pi+4 } } } } } pi=pi+1 }
167 if iat < 0 { return 0 }
168 var c: i64 = 0
169 while c < 69 { if iat + c >= rn { return 0 } cidout[c] = req[iat + c]; c = c + 1 }
170 cidout[69] = 0 as u8
171 return 1
172}
173// cid->path resolution context: the sovereign O(1) hash index (idx+blob), loaded once at startup, COW-shared to children.
174struct NxCidx {
175 idx: *u8
176 blob: *u8
177 nb: i64
178 ready: i64
179}
180
181func gs_sidecar(cidx: *NxCidx, cid: *u8, pathout: *u8) -> i64 {
182 // FAST PATH: the sovereign O(1) index. Falls through to the legacy TSV scan only if it is absent or the cid is
183 // not indexed yet (a stale index after new images are added) -- zero-regression.
184 if (cidx as i64) != 0 { if cidx.ready == 1 {
185 if gs_cidindex_lookup(cidx.idx, cidx.blob, cidx.nb, cid, pathout) == 1 { return 1 }
186 } }
187 let szp: *i64 = sys_mmap(16) as *i64
188 let b: *u8 = sys_read_file("knowledge/status/galx_cid_paths.tsv" as *u8, szp)
189 let sz: i64 = szp[0]
190 if (b as i64) == 0 { return 0 }
191 var i: i64 = 0; var ls: i64 = 0
192 while i < sz {
193 if b[i] == (10 as u8) {
194 var eq: i64 = 1; var k: i64 = 0
195 while k < 69 { if b[ls + k] != cid[k] { eq = 0 } k = k + 1 }
196 if b[ls + 69] != (9 as u8) { eq = 0 }
197 if eq == 1 { var po: i64=0; var p: i64=ls+70; while p<i { pathout[po]=b[p]; po=po+1; p=p+1 } pathout[po]=0 as u8; return 1 }
198 ls = i + 1
199 }
200 i = i + 1
201 }
202 return 0
203}
204func gs_serve_png(rbuf: *u8, req: *u8, rn: i64, cidx: *NxCidx) -> i64 {
205 let cid: *u8 = sys_mmap(96)
206 if gs_peel_cid(req, rn, cid) == 0 { return gs_404(rbuf) }
207 let path: *u8 = sys_mmap(VIEW_MAGIC_2048)
208 if gs_sidecar(cidx, cid, path) == 0 { return gs_404(rbuf) }
209 let szp: *i64 = sys_mmap(16) as *i64
210 let png: *u8 = sys_read_file(path, szp)
211 let plen: i64 = szp[0]
212 if (png as i64) == 0 { return gs_404(rbuf) }
213 var o: i64 = 0
214 o = gs_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: image/png\r\nCache-Control: max-age=86400\r\nContent-Length: " as *u8)
215 o = gs_u(rbuf, o, plen)
216 o = gs_cat(rbuf, o, "\r\nConnection: close\r\n\r\n" as *u8)
217 var i: i64 = 0
218 while i < plen { rbuf[o] = png[i]; o = o + 1; i = i + 1 }
219 return o
220}
221
222// ---- image THUMBNAILS (S-class: NEVER serve full-res into a 160px grid cell) ----
223// GET /thumb/<cid> -> a small cached PNG thumbnail. Cold cache -> generate via nx_galx_thumb
224// (sovereign decode->bilinear downscale->encode), atomic .new+rename so a concurrent reader never sees a
225// partial file. If the generator is absent or fails, FALL BACK to the full-res image so the grid is never
226// broken (zero-regression: without the generator deployed, /thumb behaves exactly like /img). The lightbox
227// still requests /img/<cid> (full-res). Cache dir = thumbs/ under the gallery CWD (/volume1/ai/galx).
228const GS_THUMB_ELF: *u8 = "/volume1/ai/galx/nx_galx_thumb.elf"
229const GS_THUMB_MAX: *u8 = "384"
230
231// generic cid peel after an arbitrary request prefix (gs_peel_cid is /img/-only). 1 ok / 0 not found.
232func gs_peel_cid_pat(req: *u8, rn: i64, pat: *u8, plen: i64, cidout: *u8) -> i64 {
233 var i: i64 = 0; var at: i64 = 0 - 1
234 while i + plen <= rn { var j: i64 = 0; var ok: i64 = 1; while j < plen { if req[i+j] != pat[j] { ok = 0 } j = j + 1 } if ok == 1 { if at < 0 { at = i + plen } } i = i + 1 }
235 if at < 0 { return 0 }
236 if at + 69 > rn { return 0 }
237 var c: i64 = 0
238 while c < 69 { cidout[c] = req[at + c]; c = c + 1 }
239 cidout[69] = 0 as u8
240 return 1
241}
242func gs_file_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
243// read a file and serve it as image/png (7-day cache). returns response length, or gs_404 if missing.
244func gs_send_imgfile(rbuf: *u8, path: *u8, ctype: *u8) -> i64 {
245 let szp: *i64 = sys_mmap(16) as *i64
246 let png: *u8 = sys_read_file(path, szp)
247 let plen: i64 = szp[0]
248 if (png as i64) == 0 { return gs_404(rbuf) }
249 var o: i64 = gs_cat(rbuf, 0, "HTTP/1.1 200 OK\r\nContent-Type: " as *u8)
250 o = gs_cat(rbuf, o, ctype)
251 o = gs_cat(rbuf, o, "\r\nCache-Control: max-age=604800\r\nContent-Length: " as *u8)
252 o = gs_u(rbuf, o, plen)
253 o = gs_cat(rbuf, o, "\r\nConnection: close\r\n\r\n" as *u8)
254 var i: i64 = 0
255 while i < plen { rbuf[o] = png[i]; o = o + 1; i = i + 1 }
256 return o
257}
258// fork+exec nx_galx_thumb <src> <outnew> 384 ; returns child exit code (0=ok, 127=exec unavailable).
259func gs_thumb_run(src: *u8, outnew: *u8) -> i64 {
260 let argv: *i64 = sys_mmap(48) as *i64
261 argv[0] = GS_THUMB_ELF as i64; argv[1] = src as i64; argv[2] = outnew as i64; argv[3] = GS_THUMB_MAX as i64; argv[4] = 0
262 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
263 let pid: i64 = sys_fork()
264 if pid == 0 { sys_execve_clean(GS_THUMB_ELF, argv, envp); sys_exit(127) }
265 let st: *i64 = sys_mmap(16) as *i64
266 sys_wait4(pid, st, 0)
267 return (st[0] >> 8) & 0xff
268}
269func gs_thumb(rbuf: *u8, req: *u8, rn: i64, cidx: *NxCidx) -> i64 {
270 let cid: *u8 = sys_mmap(96)
271 if gs_peel_cid_pat(req, rn, "GET /thumb/" as *u8, 11, cid) == 0 { return gs_404(rbuf) }
272 // CACHE-FIRST: a cached thumb serves O(1) WITHOUT resolving cid->path at all -- the whole point on a 60-thumb grid.
273 let cache: *u8 = sys_mmap(VIEW_MAGIC_2048)
274 var co: i64 = gs_cat(cache, 0, "thumbs/" as *u8)
275 var k: i64 = 0; while k < 69 { cache[co] = cid[k]; co = co + 1; k = k + 1 }
276 co = gs_cat(cache, co, ".jpg" as *u8); cache[co] = 0 as u8
277 if gs_file_exists(cache) == 1 { return gs_send_imgfile(rbuf, cache, "image/jpeg" as *u8) }
278 // cold: NOW resolve the source (O(1) sovereign index), generate to .new, atomic rename, serve
279 let src: *u8 = sys_mmap(VIEW_MAGIC_2048)
280 if gs_sidecar(cidx, cid, src) == 0 { return gs_404(rbuf) }
281 __syscall(83, "thumbs" as *u8 as i64, 493, 0, 0, 0, 0) // mkdir thumbs (idempotent; EEXIST ignored)
282 let cnew: *u8 = sys_mmap(VIEW_MAGIC_2048)
283 var no: i64 = gs_cat(cnew, 0, cache); no = gs_cat(cnew, no, ".new" as *u8); cnew[no] = 0 as u8
284 if gs_thumb_run(src, cnew) == 0 { __syscall(82, cnew as i64, cache as i64, 0, 0, 0, 0) } // rename .new -> cache (atomic)
285 if gs_file_exists(cache) == 1 { return gs_send_imgfile(rbuf, cache, "image/jpeg" as *u8) }
286 // FALLBACK: generator absent/failed -> serve full-res so the grid is never broken
287 return gs_send_imgfile(rbuf, src, "image/png" as *u8)
288}
289
290const GS_UPSCALE_ELF: *u8 = "/volume1/ai/galx/nx_galx_upscale.elf"
291const GS_UPSCALE_MODE: *u8 = "one"
292const GS_UPSCALE_FACTOR: *u8 = "2"
293
294// ---- 4chan BROWSE (MV-6 rung 1): relay the sovereign nx_4chan adapter's JSON to the gated UI ----
295// READ-ONLY BY DESIGN: boards / catalog / thread only. `fetch` is deliberately NOT reachable over HTTP
296// -- a GET that writes to disk is an abuse surface, so downloads stay on the authenticated MCP/CLI path.
297const GS_4CHAN_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_4chan.elf"
298const GS_4CHAN_CWD: *u8 = "/volume1/homes/elderwesto/nishihost"
299// R11: AN ITEM CAP IS DATA, NOT CODE. The old hardcoded "60" silently truncated every catalog and
300// thread to 60 items and could only be changed by a rebuild -- it read to the user as "the gallery
301// randomly limits images". It now comes from a conf file, and the fallback is NAMED so an unreadable
302// conf reads as "fell back to the default" rather than becoming an invisible policy.
303// 4096 is NOT arbitrary: it is FC_MAX_OBJ, nx_4chan's own per-document enumeration cap, so the gallery
304// never caps BELOW what the adapter can actually return.
305const GS_4CHAN_CONF: *u8 = "/volume1/ai/galx/gallery_4chan.conf"
306const GS_4CHAN_MAX_KEY: *u8 = "max_items="
307const GS_4CHAN_MAX_DEF: *u8 = "4096"
308
309// Write the item cap as a NUL-terminated digit string into dst. Returns 1 if it came from the conf,
310// 0 if the named default was used -- the caller can therefore DISTINGUISH "configured" from "fell
311// back", which a bare string return could never express.
312func gs_4chan_max(dst: *u8) -> i64 {
313 var d: i64 = 0
314 while GS_4CHAN_MAX_DEF[d] != (0 as u8) { dst[d] = GS_4CHAN_MAX_DEF[d]; d = d + 1 }
315 dst[d] = 0 as u8
316 var klen: i64 = 0
317 while GS_4CHAN_MAX_KEY[klen] != (0 as u8) { klen = klen + 1 }
318 let fd: i64 = sys_openat_rd(GS_4CHAN_CONF)
319 if fd < 0 { return 0 }
320 let buf: *u8 = sys_mmap(VIEW_MAGIC_4096)
321 let n: i64 = sys_read(fd, buf, VIEW_MAGIC_4096 - 1)
322 sys_close(fd)
323 if n <= 0 { return 0 }
324 var i: i64 = 0
325 var at: i64 = 0 - 1
326 while i + klen <= n {
327 var j: i64 = 0
328 var ok: i64 = 1
329 while j < klen { if buf[i + j] != GS_4CHAN_MAX_KEY[j] { ok = 0; j = klen } else { j = j + 1 } }
330 if ok == 1 { at = i + klen; i = n } else { i = i + 1 }
331 }
332 if at < 0 { return 0 }
333 var o: i64 = 0
334 var run: i64 = 1
335 while run == 1 {
336 var c: i64 = 0 - 1
337 if at < n { c = buf[at] as i64 }
338 var isdig: i64 = 0
339 if c >= 48 { if c <= 57 { isdig = 1 } }
340 if isdig == 1 { if o < 18 { dst[o] = c as u8; o = o + 1 } at = at + 1 } else { run = 0 }
341 }
342 if o == 0 {
343 var d2: i64 = 0
344 while GS_4CHAN_MAX_DEF[d2] != (0 as u8) { dst[d2] = GS_4CHAN_MAX_DEF[d2]; d2 = d2 + 1 }
345 dst[d2] = 0 as u8
346 return 0
347 }
348 dst[o] = 0 as u8
349 return 1
350}
351
352// Peel up to 3 '/'-separated segments after "GET /api/4chan/". The charset is WHITELISTED to
353// [A-Za-z0-9_-] because these strings become execve argv: anything else is DROPPED, not escaped --
354// a dropped byte cannot become an injection, and a board name is never punctuation. Segments are
355// bounded at 63 so a crafted URL cannot run past the request line into the headers.
356func gs_4c_peel(req: *u8, rn: i64, s1: *u8, s2: *u8, s3: *u8) -> i64 {
357 let pat: *u8 = "GET /api/4chan/" as *u8
358 let plen: i64 = 15
359 var at: i64 = 0 - 1
360 var i: i64 = 0
361 while i + plen <= rn {
362 var j: i64 = 0
363 var ok: i64 = 1
364 while j < plen { if req[i + j] != pat[j] { ok = 0; j = plen } else { j = j + 1 } }
365 if ok == 1 { at = i + plen; i = rn } else { i = i + 1 }
366 }
367 if at < 0 { return 0 }
368 s1[0] = 0 as u8; s2[0] = 0 as u8; s3[0] = 0 as u8
369 var seg: i64 = 0
370 var o: i64 = 0
371 var p: i64 = at
372 var run: i64 = 1
373 while run == 1 {
374 if p >= rn { run = 0 } else {
375 let c: i64 = req[p] as i64
376 if c == 32 { run = 0 } else {
377 if c == 13 { run = 0 } else {
378 if c == 10 { run = 0 } else {
379 if c == 63 { run = 0 } else {
380 if c == 47 {
381 if seg == 0 { s1[o] = 0 as u8 } else { if seg == 1 { s2[o] = 0 as u8 } else { s3[o] = 0 as u8 } }
382 seg = seg + 1
383 o = 0
384 if seg > 2 { run = 0 } else { p = p + 1 }
385 } else {
386 var keep: i64 = 0
387 if c >= 48 { if c <= 57 { keep = 1 } }
388 if c >= 65 { if c <= 90 { keep = 1 } }
389 if c >= 97 { if c <= 122 { keep = 1 } }
390 if c == 95 { keep = 1 }
391 if c == 45 { keep = 1 }
392 if keep == 1 { if o < 63 {
393 if seg == 0 { s1[o] = c as u8 } else { if seg == 1 { s2[o] = c as u8 } else { s3[o] = c as u8 } }
394 o = o + 1
395 } }
396 p = p + 1
397 } } } } }
398 }
399 }
400 if seg == 0 { s1[o] = 0 as u8 } else { if seg == 1 { s2[o] = 0 as u8 } else { s3[o] = 0 as u8 } }
401 return 1
402}
403
404// fork nx_4chan with stdout captured to `outpath`; the parent reads it back and relays it.
405// chdir FIRST: nx_4chan resolves `data/mozilla_certdata.txt` RELATIVE TO CWD, and this daemon's CWD is
406// /volume1/ai/galx (no data/ there) -- without the chdir every call returns "certdata load failed".
407func gs_4chan_run(a1: *u8, a2: *u8, a3: *u8, outpath: *u8) -> i64 {
408 let argv: *i64 = sys_mmap(64) as *i64
409 argv[0] = GS_4CHAN_ELF as i64
410 argv[1] = a1 as i64
411 var n: i64 = 2
412 if a2[0] != (0 as u8) { argv[n] = a2 as i64; n = n + 1 }
413 if a3[0] != (0 as u8) { argv[n] = a3 as i64; n = n + 1 }
414 let maxs: *u8 = sys_mmap(32)
415 gs_4chan_max(maxs)
416 argv[n] = maxs as i64
417 n = n + 1
418 argv[n] = 0
419 let envp: *i64 = sys_mmap(8) as *i64
420 envp[0] = 0
421 let pid: i64 = sys_fork()
422 if pid == 0 {
423 // ★ NAMED WRAPPERS, NOT LITERAL SYSCALL NUMBERS. A literal in __syscall(<n>,...) is subject to
424 // the compile-time rv64->x86 translation (see sys_chdir: it forces the number through a runtime
425 // variable precisely "so the rv64->x86 xlate is skipped"). So __syscall(33)=dup2 and
426 // __syscall(39)=getpid were NOT those calls at all -- they were silently remapped, the child's
427 // stdout was never redirected, the capture file came back EMPTY, and the UI reported
428 // "4chan adapter produced no output". MEASURED 2026-08-10, same bug written twice (also nx_mcp).
429 sys_chdir(GS_4CHAN_CWD)
430 __syscall(263, 0 - 100, outpath as i64, 0, 0, 0, 0)
431 let fd: i64 = sys_openat_wr(outpath, 0x1a4)
432 if fd < 0 { sys_exit(126) }
433 sys_dup3(fd, 1, 0)
434 sys_execve(GS_4CHAN_ELF, argv, envp)
435 sys_exit(127)
436 }
437 let st: *i64 = sys_mmap(16) as *i64
438 sys_wait4(pid, st, 0)
439 return (st[0] >> 8) & 0xff
440}
441
442// ---- 4chan IMAGE PROXY. The gallery CSP is `img-src 'self'`, so a browser CANNOT load i.4cdn.org
443// directly -- the images render BROKEN. This proxies each media file THROUGH the gallery (same-origin,
444// CSP-clean), so the client never touches 4chan (safe browsing) and it rides nx_4chan's Chrome-JA3
445// fetch. Cached under 4chan_cache/<board>/<file> so each file is fetched at most once. nosniff is set
446// at the edge, so the Content-Type MUST be exact (derived from the extension).
447const GS_4C_CACHE_ABS: *u8 = "/volume1/ai/galx/4chan_cache/"
448func gs_4c_img_peel(req: *u8, rn: i64, board: *u8, file: *u8) -> i64 {
449 let pat: *u8 = "GET /api/4chan/img/" as *u8
450 let plen: i64 = 19
451 var at: i64 = 0 - 1
452 var i: i64 = 0
453 while i + plen <= rn {
454 var j: i64 = 0; var ok: i64 = 1
455 while j < plen { if req[i+j] != pat[j] { ok = 0; j = plen } else { j = j + 1 } }
456 if ok == 1 { at = i + plen; i = rn } else { i = i + 1 }
457 }
458 if at < 0 { return 0 }
459 var o: i64 = 0
460 var p: i64 = at
461 var run: i64 = 1
462 while run == 1 {
463 if p >= rn { run = 0 } else {
464 let c: i64 = req[p] as i64
465 if c == 47 { run = 0; p = p + 1 } else {
466 if c == 32 { run = 0 } else { if c == 13 { run = 0 } else { if c == 10 { run = 0 } else {
467 var keep: i64 = 0
468 if c >= 48 { if c <= 57 { keep = 1 } }
469 if c >= 97 { if c <= 122 { keep = 1 } }
470 if keep == 1 { if o < 32 { board[o] = c as u8; o = o + 1 } }
471 p = p + 1
472 } } } }
473 }
474 }
475 board[o] = 0 as u8
476 if o == 0 { return 0 }
477 o = 0
478 run = 1
479 while run == 1 {
480 if p >= rn { run = 0 } else {
481 let c2: i64 = req[p] as i64
482 if c2 == 32 { run = 0 } else { if c2 == 13 { run = 0 } else { if c2 == 10 { run = 0 } else { if c2 == 63 { run = 0 } else {
483 var keep2: i64 = 0
484 if c2 >= 48 { if c2 <= 57 { keep2 = 1 } }
485 if c2 >= 97 { if c2 <= 122 { keep2 = 1 } }
486 if c2 == 46 { keep2 = 1 }
487 if keep2 == 1 { if o < 96 { file[o] = c2 as u8; o = o + 1 } }
488 p = p + 1
489 } } } }
490 }
491 }
492 file[o] = 0 as u8
493 if o == 0 { return 0 }
494 return 1
495}
496func gs_4c_isuffix(s: *u8, n: i64, suf: *u8) -> i64 {
497 let m: i64 = gs_strlen(suf)
498 if n < m { return 0 }
499 var i: i64 = 0
500 while i < m { if s[n - m + i] != suf[i] { return 0 } i = i + 1 }
501 return 1
502}
503func gs_4c_ct(file: *u8) -> *u8 {
504 let n: i64 = gs_strlen(file)
505 if gs_4c_isuffix(file, n, ".webm" as *u8) == 1 { return "video/webm" as *u8 }
506 if gs_4c_isuffix(file, n, ".mp4" as *u8) == 1 { return "video/mp4" as *u8 }
507 if gs_4c_isuffix(file, n, ".png" as *u8) == 1 { return "image/png" as *u8 }
508 if gs_4c_isuffix(file, n, ".gif" as *u8) == 1 { return "image/gif" as *u8 }
509 return "image/jpeg" as *u8
510}
511func gs_4chan_fetch1(board: *u8, file: *u8, absdir: *u8) -> i64 {
512 let argv: *i64 = sys_mmap(64) as *i64
513 argv[0] = GS_4CHAN_ELF as i64
514 argv[1] = "saveone" as *u8 as i64
515 argv[2] = board as i64
516 argv[3] = file as i64
517 argv[4] = absdir as i64
518 argv[5] = 0
519 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
520 let pid: i64 = sys_fork()
521 if pid == 0 {
522 sys_chdir(GS_4CHAN_CWD)
523 sys_execve(GS_4CHAN_ELF, argv, envp)
524 sys_exit(127)
525 }
526 let st: *i64 = sys_mmap(16) as *i64
527 sys_wait4(pid, st, 0)
528 return (st[0] >> 8) & 0xff
529}
530func gs_4chan_img(rbuf: *u8, req: *u8, rn: i64) -> i64 {
531 let board: *u8 = sys_mmap(64)
532 let file: *u8 = sys_mmap(128)
533 if gs_4c_img_peel(req, rn, board, file) == 0 { return gs_404(rbuf) }
534 let cache: *u8 = sys_mmap(512)
535 var co: i64 = gs_cat(cache, 0, "4chan_cache/" as *u8)
536 co = gs_cat(cache, co, board); cache[co] = 47 as u8; co = co + 1
537 co = gs_cat(cache, co, file); cache[co] = 0 as u8
538 if gs_file_exists(cache) == 0 {
539 let absdir: *u8 = sys_mmap(512)
540 var ao: i64 = gs_cat(absdir, 0, GS_4C_CACHE_ABS)
541 ao = gs_cat(absdir, ao, board); absdir[ao] = 0 as u8
542 gs_4chan_fetch1(board, file, absdir)
543 }
544 if gs_file_exists(cache) == 1 { return gs_send_imgfile(rbuf, cache, gs_4c_ct(file)) }
545 return gs_404(rbuf)
546}
547func gs_4chan(rbuf: *u8, req: *u8, rn: i64) -> i64 {
548 let s1: *u8 = sys_mmap(64)
549 let s2: *u8 = sys_mmap(64)
550 let s3: *u8 = sys_mmap(64)
551 if gs_4c_peel(req, rn, s1, s2, s3) == 0 { return gs_404(rbuf) }
552 if s1[0] == (0 as u8) { return gs_404(rbuf) }
553 let tmp: *u8 = sys_mmap(128)
554 var t: i64 = gs_cat(tmp, 0, "/tmp/gs_4chan_" as *u8)
555 t = gs_u(tmp, t, __syscall(39, 0, 0, 0, 0, 0, 0))
556 t = gs_cat(tmp, t, ".json" as *u8)
557 tmp[t] = 0 as u8
558 gs_4chan_run(s1, s2, s3, tmp)
559 let body: *u8 = sys_mmap(4194304)
560 let fd: i64 = sys_openat_rd(tmp)
561 var bn: i64 = 0
562 if fd >= 0 {
563 var run2: i64 = 1
564 while run2 == 1 {
565 let k: i64 = sys_read(fd, ((body as i64) + bn) as *u8, 4194304 - bn)
566 if k <= 0 { run2 = 0 } else { bn = bn + k; if bn >= 4194304 { run2 = 0 } }
567 }
568 sys_close(fd)
569 }
570 __syscall(263, 0 - 100, tmp as i64, 0, 0, 0, 0)
571 if bn <= 0 { let e: *u8 = "{\"error\":\"4chan adapter produced no output\"}" as *u8; return gs_okjson(rbuf, e, gs_strlen(e)) }
572 return gs_okjson(rbuf, body, bn)
573}
574
575// ---------------------------------------------------------------------------------------------
576// 4chan INGEST -- the WRITE half of the browse surface (the half whose absence made the UI look
577// broken: the browser above is a LIVE PROXY, so nothing it displayed was ever kept, and there was
578// no download action because there was no endpoint to bind one to).
579// POST-ONLY BY DESIGN. The browse routes stay read-only because a GET that writes to disk is an abuse
580// surface -- prefetchable, crawlable, CSRF-able. Ingest therefore rides POST beside the existing
581// /fav /rate /tag writers and inherits the same gateway-authenticated path.
582// ASYNC BY CONSTRUCTION: one thread is hundreds of files and minutes of transfer, so a synchronous
583// handler would hold the connection past every timeout and report FAILURE for a download that is
584// working. The handler double-forks a detached worker and returns a descriptor the client polls.
585// It runs BOTH steps the CLI runs -- nx_4chan fetch, then nx_mvault_walk commit -- because the adapter
586// names that handoff in its own output ("vault_next"), and bytes that are never committed are
587// invisible to the gallery no matter how many of them landed.
588const GS_4C_ING_DIR: *u8 = "/volume1/homes/elderwesto/nishihost/media/4chan/"
589const GS_4C_JOBS: *u8 = "/volume1/ai/galx/4chan_jobs"
590const GS_MVAULT_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_mvault_walk.elf"
591
592// Pull key=<value> from a request, WHITELISTING [A-Za-z0-9_-] exactly as gs_4c_peel does: these become
593// execve argv, so an illegal byte is DROPPED, never escaped -- a dropped byte cannot become injection.
594// Returns the length written; 0 = key absent or held nothing legal, which the caller must reject.
595func gs_4c_form(req: *u8, rn: i64, key: *u8, dst: *u8, maxlen: i64) -> i64 {
596 var klen: i64 = 0
597 while key[klen] != (0 as u8) { klen = klen + 1 }
598 var i: i64 = 0
599 var at: i64 = 0 - 1
600 while i + klen <= rn {
601 var j: i64 = 0
602 var ok: i64 = 1
603 while j < klen { if req[i + j] != key[j] { ok = 0; j = klen } else { j = j + 1 } }
604 if ok == 1 { at = i + klen; i = rn } else { i = i + 1 }
605 }
606 dst[0] = 0 as u8
607 if at < 0 { return 0 }
608 var o: i64 = 0
609 var run: i64 = 1
610 while run == 1 {
611 var c: i64 = 0 - 1
612 if at < rn { c = req[at] as i64 }
613 var keep: i64 = 0
614 if c >= 48 { if c <= 57 { keep = 1 } }
615 if c >= 65 { if c <= 90 { keep = 1 } }
616 if c >= 97 { if c <= 122 { keep = 1 } }
617 if c == 95 { keep = 1 }
618 if c == 45 { keep = 1 }
619 if keep == 1 { if o < maxlen - 1 { dst[o] = c as u8; o = o + 1 } at = at + 1 } else { run = 0 }
620 }
621 dst[o] = 0 as u8
622 return o
623}
624
625// The detached worker: fetch -> commit. Runs in a grandchild, so nothing here blocks a request.
626func gs_4c_ingest_work(board: *u8, thread: *u8) -> i64 {
627 let dir: *u8 = sys_mmap(512)
628 var d: i64 = gs_cat(dir, 0, GS_4C_ING_DIR)
629 d = gs_cat(dir, d, board); dir[d] = 95 as u8; d = d + 1
630 d = gs_cat(dir, d, thread); dir[d] = 0 as u8
631 let outp: *u8 = sys_mmap(512)
632 var oo: i64 = gs_cat(outp, 0, GS_4C_JOBS); outp[oo] = 47 as u8; oo = oo + 1
633 oo = gs_cat(outp, oo, board); outp[oo] = 95 as u8; oo = oo + 1
634 oo = gs_cat(outp, oo, thread)
635 oo = gs_cat(outp, oo, ".json" as *u8); outp[oo] = 0 as u8
636 let maxs: *u8 = sys_mmap(32)
637 gs_4chan_max(maxs)
638 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
639 let a1: *i64 = sys_mmap(64) as *i64
640 a1[0] = GS_4CHAN_ELF as i64
641 a1[1] = "fetch" as *u8 as i64
642 a1[2] = board as i64
643 a1[3] = thread as i64
644 a1[4] = dir as i64
645 a1[5] = maxs as i64
646 a1[6] = 0
647 let p1: i64 = sys_fork()
648 if p1 == 0 {
649 sys_chdir(GS_4CHAN_CWD)
650 __syscall(263, 0 - 100, outp as i64, 0, 0, 0, 0)
651 let fd: i64 = sys_openat_wr(outp, 0x1a4)
652 if fd < 0 { sys_exit(126) }
653 sys_dup3(fd, 1, 0)
654 sys_execve(GS_4CHAN_ELF, a1, envp)
655 sys_exit(127)
656 }
657 let st: *i64 = sys_mmap(16) as *i64
658 sys_wait4(p1, st, 0)
659 let a2: *i64 = sys_mmap(64) as *i64
660 a2[0] = GS_MVAULT_ELF as i64
661 a2[1] = dir as i64
662 a2[2] = "real" as *u8 as i64
663 a2[3] = "4chan" as *u8 as i64
664 a2[4] = "commit" as *u8 as i64
665 a2[5] = 0
666 let p2: i64 = sys_fork()
667 if p2 == 0 {
668 sys_chdir(GS_4CHAN_CWD)
669 sys_execve(GS_MVAULT_ELF, a2, envp)
670 sys_exit(127)
671 }
672 sys_wait4(p2, st, 0)
673 return 0
674}
675
676// POST /api/4chan/ingest body: board=<b>&thread=<n>
677func gs_4chan_ingest(rbuf: *u8, req: *u8, rn: i64) -> i64 {
678 let board: *u8 = sys_mmap(64)
679 let thread: *u8 = sys_mmap(64)
680 if gs_4c_form(req, rn, "board=" as *u8, board, 64) == 0 {
681 let e: *u8 = "{\"error\":\"missing or illegal board\"}" as *u8
682 return gs_okjson(rbuf, e, gs_strlen(e))
683 }
684 if gs_4c_form(req, rn, "thread=" as *u8, thread, 64) == 0 {
685 let e2: *u8 = "{\"error\":\"missing or illegal thread\"}" as *u8
686 return gs_okjson(rbuf, e2, gs_strlen(e2))
687 }
688 __syscall(83, GS_4C_JOBS as i64, 493, 0, 0, 0, 0)
689 let pid: i64 = sys_fork()
690 if pid == 0 {
691 let pid2: i64 = sys_fork()
692 if pid2 == 0 { gs_4c_ingest_work(board, thread); sys_exit(0) }
693 sys_exit(0)
694 }
695 let st: *i64 = sys_mmap(16) as *i64
696 sys_wait4(pid, st, 0)
697 // POST-REDIRECT-GET. The form target must not RENDER, it must REDIRECT: otherwise a browser reload
698 // re-submits the POST and starts a SECOND ingest of the same thread. The panel then renders progress
699 // server-side, so there is no client state machine anywhere in this flow.
700 let loc: *u8 = sys_mmap(512)
701 var l: i64 = gs_cat(loc, 0, "/gallery/api/4chan/panel/" as *u8)
702 l = gs_cat(loc, l, board); loc[l] = 47 as u8; l = l + 1
703 l = gs_cat(loc, l, thread); loc[l] = 0 as u8
704 return gs_see_other(rbuf, loc)
705}
706
707// GET /api/4chan/ingest_status/<board>/<thread> -> the adapter's OWN result JSON once the worker is
708// done. An absent file means RUNNING, and that is reported as a distinct state: "not finished yet"
709// and "finished having downloaded nothing" must never read the same to a caller.
710func gs_4chan_ingest_status(rbuf: *u8, req: *u8, rn: i64) -> i64 {
711 let s1: *u8 = sys_mmap(64)
712 let s2: *u8 = sys_mmap(64)
713 let s3: *u8 = sys_mmap(64)
714 if gs_4c_peel(req, rn, s1, s2, s3) == 0 { return gs_404(rbuf) }
715 if s2[0] == (0 as u8) { return gs_404(rbuf) }
716 if s3[0] == (0 as u8) { return gs_404(rbuf) }
717 let outp: *u8 = sys_mmap(512)
718 var oo: i64 = gs_cat(outp, 0, GS_4C_JOBS); outp[oo] = 47 as u8; oo = oo + 1
719 oo = gs_cat(outp, oo, s2); outp[oo] = 95 as u8; oo = oo + 1
720 oo = gs_cat(outp, oo, s3)
721 oo = gs_cat(outp, oo, ".json" as *u8); outp[oo] = 0 as u8
722 let fd: i64 = sys_openat_rd(outp)
723 if fd < 0 {
724 let e: *u8 = "{\"state\":\"RUNNING\",\"note\":\"no result file yet -- the worker has not finished; this is NOT an error\"}" as *u8
725 return gs_okjson(rbuf, e, gs_strlen(e))
726 }
727 let body: *u8 = sys_mmap(VIEW_MAGIC_65536)
728 var bn: i64 = 0
729 var run: i64 = 1
730 while run == 1 {
731 let k: i64 = sys_read(fd, ((body as i64) + bn) as *u8, VIEW_MAGIC_65536 - bn)
732 if k <= 0 { run = 0 } else { bn = bn + k; if bn >= VIEW_MAGIC_65536 { run = 0 } }
733 }
734 sys_close(fd)
735 if bn <= 0 {
736 let e2: *u8 = "{\"state\":\"RUNNING\",\"note\":\"result file present but empty -- worker still writing\"}" as *u8
737 return gs_okjson(rbuf, e2, gs_strlen(e2))
738 }
739 return gs_okjson(rbuf, body, bn)
740}
741
742// ---------------------------------------------------------------------------------------------
743// SERVER-EMITTED INGEST SURFACE -- NishiLang from the first bit, ZERO JavaScript.
744// This daemon had NO html responder at all, which is the tell: every HTML surface it has was being
745// built by client-side JS. A JS-driven interface cannot run natively in the Nishi browser without
746// dragging in a whole JS engine, so a capability whose INTERFACE needs a foreign runtime is not native
747// however native its engine is. The action is an HTML <form method=post>; the polling is
748// <meta http-equiv=refresh>. Both are HTML primitives that need no VM to execute.
749
750// REFRESH CADENCE IS DERIVED, NOT PICKED: nx_4chan sleeps FC_FETCH_DELAY_MS (350ms) between media
751// fetches, so progress cannot move faster than ~3 files/sec. A 5s refresh therefore shows ~14 files of
752// movement per tick -- visible progress without hammering the daemon. Refreshing faster would re-render
753// the same numbers; slower would read as hung.
754const GS_4C_REFRESH_S: *u8 = "5"
755
756// GET /api/4chan/panel/<board>/<thread> -- the whole ingest UI, server-rendered.
757func gs_4chan_panel(rbuf: *u8, req: *u8, rn: i64) -> i64 {
758 let s1: *u8 = sys_mmap(64)
759 let s2: *u8 = sys_mmap(64)
760 let s3: *u8 = sys_mmap(64)
761 if gs_4c_peel(req, rn, s1, s2, s3) == 0 { return gs_404(rbuf) }
762 if s2[0] == (0 as u8) { return gs_404(rbuf) }
763 if s3[0] == (0 as u8) { return gs_404(rbuf) }
764 let outp: *u8 = sys_mmap(512)
765 var oo: i64 = gs_cat(outp, 0, GS_4C_JOBS); outp[oo] = 47 as u8; oo = oo + 1
766 oo = gs_cat(outp, oo, s2); outp[oo] = 95 as u8; oo = oo + 1
767 oo = gs_cat(outp, oo, s3)
768 oo = gs_cat(outp, oo, ".json" as *u8); outp[oo] = 0 as u8
769 let res: *u8 = sys_mmap(VIEW_MAGIC_65536)
770 var rl: i64 = 0
771 let fd: i64 = sys_openat_rd(outp)
772 if fd >= 0 {
773 var run: i64 = 1
774 while run == 1 {
775 let k: i64 = sys_read(fd, ((res as i64) + rl) as *u8, VIEW_MAGIC_65536 - rl)
776 if k <= 0 { run = 0 } else { rl = rl + k; if rl >= VIEW_MAGIC_65536 { run = 0 } }
777 }
778 sys_close(fd)
779 }
780 let body: *u8 = sys_mmap(VIEW_MAGIC_131072)
781 var b: i64 = gs_cat(body, 0, "<!doctype html><html><head><meta charset=utf-8><title>4chan ingest</title>" as *u8)
782 // Refresh ONLY while there is no result. A finished page that keeps refreshing re-renders forever
783 // and reads as a hang -- the refresh must stop when the thing it is waiting for has happened.
784 if rl <= 0 {
785 b = gs_cat(body, b, "<meta http-equiv=refresh content=" as *u8)
786 b = gs_cat(body, b, GS_4C_REFRESH_S)
787 b = gs_cat(body, b, ">" as *u8)
788 }
789 b = gs_cat(body, b, "<style>body{background:#0e0e0e;color:#ddd;font-family:system-ui,sans-serif;padding:20px;line-height:1.5}pre{background:#1b1b1b;padding:12px;border-radius:6px;overflow:auto;color:#9d9}button{background:#2b6;color:#062;border:0;border-radius:6px;padding:10px 16px;font-size:15px;font-weight:600;cursor:pointer}a{color:#6af}.m{color:#888;font-size:13px}</style></head><body>" as *u8)
790 b = gs_cat(body, b, "<h2>/" as *u8)
791 b = gs_cat(body, b, s2)
792 b = gs_cat(body, b, "/ thread " as *u8)
793 b = gs_cat(body, b, s3)
794 b = gs_cat(body, b, "</h2>" as *u8)
795 b = gs_cat(body, b, "<form method=post action=/gallery/api/4chan/ingest><input type=hidden name=board value=" as *u8)
796 b = gs_cat(body, b, s2)
797 b = gs_cat(body, b, "><input type=hidden name=thread value=" as *u8)
798 b = gs_cat(body, b, s3)
799 if rl <= 0 {
800 b = gs_cat(body, b, "><button type=submit>download all to library</button></form>" as *u8)
801 b = gs_cat(body, b, "<p class=m>No result yet. This page refreshes every " as *u8)
802 b = gs_cat(body, b, GS_4C_REFRESH_S)
803 b = gs_cat(body, b, "s while the worker runs. NOT-STARTED and FINISHED-WITH-NOTHING are different states and are never reported as the same thing.</p>" as *u8)
804 } else {
805 b = gs_cat(body, b, "><button type=submit>re-run ingest</button></form>" as *u8)
806 b = gs_cat(body, b, "<p class=m>Adapter result, its own output, unparsed -- this page does not re-derive counts the organ already emitted:</p><pre>" as *u8)
807 var q: i64 = 0
808 while q < rl { body[b] = res[q]; b = b + 1; q = q + 1 }
809 b = gs_cat(body, b, "</pre>" as *u8)
810 }
811 b = gs_cat(body, b, "<p class=m>Downloads land in the vault via nx_mvault_walk commit -- bytes that are never committed are invisible to the gallery no matter how many landed.</p>" as *u8)
812 b = gs_cat(body, b, "</body></html>" as *u8)
813 return gs_okhtml(rbuf, body, b)
814}
815// fork nx_galx_upscale (sovereign decode->bilinear UPscale->encode); rc 0 = ok. Mirrors gs_thumb_run.
816func gs_upscale_run(src: *u8, outnew: *u8) -> i64 {
817 let argv: *i64 = sys_mmap(56) as *i64
818 argv[0] = GS_UPSCALE_ELF as i64; argv[1] = GS_UPSCALE_MODE as i64; argv[2] = src as i64; argv[3] = outnew as i64; argv[4] = GS_UPSCALE_FACTOR as i64; argv[5] = 0
819 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
820 let pid: i64 = sys_fork()
821 if pid == 0 { sys_execve_clean(GS_UPSCALE_ELF, argv, envp); sys_exit(127) }
822 let st: *i64 = sys_mmap(16) as *i64
823 sys_wait4(pid, st, 0)
824 return (st[0] >> 8) & 0xff
825}
826// GET /upscale/<cid> -> a cached 2x bilinear-upscaled PNG. Cold -> resolve source -> fork nx_galx_upscale
827// -> atomic .new+rename -> serve. Generator absent/failed -> FALL BACK to the full-res original
828// (zero-regression: without nx_galx_upscale.elf deployed, /upscale behaves exactly like /img).
829func gs_upscale(rbuf: *u8, req: *u8, rn: i64, cidx: *NxCidx) -> i64 {
830 let cid: *u8 = sys_mmap(96)
831 if gs_peel_cid_pat(req, rn, "GET /upscale/" as *u8, 13, cid) == 0 { return gs_404(rbuf) }
832 let cache: *u8 = sys_mmap(VIEW_MAGIC_2048)
833 var co: i64 = gs_cat(cache, 0, "upscale/" as *u8)
834 var k: i64 = 0; while k < 69 { cache[co] = cid[k]; co = co + 1; k = k + 1 }
835 co = gs_cat(cache, co, "_2x.png" as *u8); cache[co] = 0 as u8
836 if gs_file_exists(cache) == 1 { return gs_send_imgfile(rbuf, cache, "image/png" as *u8) }
837 let src: *u8 = sys_mmap(VIEW_MAGIC_2048)
838 if gs_sidecar(cidx, cid, src) == 0 { return gs_404(rbuf) }
839 __syscall(83, "upscale" as *u8 as i64, 493, 0, 0, 0, 0)
840 let cnew: *u8 = sys_mmap(VIEW_MAGIC_2048)
841 var no: i64 = gs_cat(cnew, 0, cache); no = gs_cat(cnew, no, ".new" as *u8); cnew[no] = 0 as u8
842 if gs_upscale_run(src, cnew) == 0 { __syscall(82, cnew as i64, cache as i64, 0, 0, 0, 0) }
843 if gs_file_exists(cache) == 1 { return gs_send_imgfile(rbuf, cache, "image/png" as *u8) }
844 return gs_send_imgfile(rbuf, src, "image/png" as *u8)
845}
846// ---- recordings (cam .ts archive; poster.jpg sibling = ready-made thumbnail) ----
847func gs_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 }
848func gs_rb64(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<56)|((b[o+1] as i64)<<48)|((b[o+2] as i64)<<40)|((b[o+3] as i64)<<32)|((b[o+4] as i64)<<24)|((b[o+5] as i64)<<16)|((b[o+6] as i64)<<8)|(b[o+7] as i64) }
849// Build galx_vid_off.bin (id -> TSV byte offset) so gs_vid_line is O(1) instead of a 4.35MB scan/request.
850// Header (big-endian): tsv_size(8) n(8) then n*off(8). tsv_size = freshness check. Called once at startup;
851// nx_galx_vidoff is the standalone refresher (no restart needed). Idempotent.
852func gs_vidoff_build() -> i64 {
853 let szp: *i64 = sys_mmap(16) as *i64
854 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
855 if (b as i64)==0 { return 0 }
856 let sz: i64 = szp[0]
857 var n: i64 = 1; var i: i64 = 0
858 while i < sz { if b[i]==(10 as u8) { if i+1 < sz { n=n+1 } } i=i+1 }
859 let ob: *u8 = sys_mmap(16 + n*8 + 64)
860 gs_wb64(ob, 0, sz); gs_wb64(ob, 8, n); gs_wb64(ob, 16, 0)
861 var cnt: i64 = 1; i = 0
862 while i < sz { if b[i]==(10 as u8) { if i+1 < sz { if cnt < n { gs_wb64(ob, 16+cnt*8, i+1); cnt=cnt+1 } } } i=i+1 }
863 let wf: i64 = sys_openat_wr("knowledge/status/galx_vid_off.bin" as *u8, 0x1a4)
864 if wf < 0 { return 0 }
865 sys_write(wf, ob, 16+n*8); sys_close(wf)
866 return 1
867}
868// copy the path on line `id` of the video index into out; returns 1 if found.
869// fast path: O(1) via galx_vid_off.bin (freshness-guarded by tsv_size); falls back to a full scan.
870func gs_vid_line(id: i64, out: *u8) -> i64 {
871 let ofd: i64 = sys_openat_rd("knowledge/status/galx_vid_off.bin" as *u8)
872 if ofd >= 0 {
873 let hdr: *u8 = sys_mmap(32); let hr: i64 = sys_read(ofd, hdr, 16)
874 if hr == 16 {
875 let stored: i64 = gs_rb64(hdr, 0); let n: i64 = gs_rb64(hdr, 8)
876 let tfd: i64 = sys_openat_rd("knowledge/status/galx_vid_paths.tsv" as *u8)
877 if tfd >= 0 {
878 let cur: i64 = sys_lseek(tfd, 0, 2)
879 if cur == stored { if id >= 0 { if id < n {
880 sys_lseek(ofd, 16 + id*8, 0)
881 let obf: *u8 = sys_mmap(16); let orr: i64 = sys_read(ofd, obf, 8)
882 if orr == 8 {
883 let off: i64 = gs_rb64(obf, 0)
884 sys_lseek(tfd, off, 0)
885 let lb: *u8 = sys_mmap(VIEW_MAGIC_2048); let lr: i64 = sys_read(tfd, lb, VIEW_MAGIC_2000)
886 if lr > 0 {
887 var o: i64=0; var p: i64=0
888 while p < lr { if lb[p]==(10 as u8) { p=lr } else { out[o]=lb[p]; o=o+1; p=p+1 } }
889 out[o]=0 as u8; sys_close(ofd); sys_close(tfd)
890 if o > 0 { return 1 } else { return 0 }
891 }
892 }
893 } } }
894 sys_close(tfd)
895 }
896 }
897 sys_close(ofd)
898 }
899 // fallback: full scan (offset index missing or stale)
900 let szp: *i64 = sys_mmap(16) as *i64
901 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
902 let sz: i64 = szp[0]
903 if (b as i64) == 0 { return 0 }
904 var i: i64 = 0; var ls: i64 = 0; var ln: i64 = 0
905 while i <= sz {
906 var nl: i64 = 0
907 if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
908 if nl == 1 {
909 if ln == id { if i > ls { var o: i64=0; var p: i64=ls; while p<i { out[o]=b[p]; o=o+1; p=p+1 } out[o]=0 as u8; return 1 } return 0 }
910 ln = ln + 1; ls = i + 1
911 }
912 i = i + 1
913 }
914 return 0
915}
916// parse a numeric id that follows `pat` in the request line.
917func gs_peel_num(req: *u8, rn: i64, pat: *u8, plen: i64) -> i64 {
918 var i: i64 = 0; var at: i64 = 0 - 1
919 while i + plen <= rn { var j: i64=0; var ok: i64=1; while j<plen { if req[i+j]!=pat[j]{ok=0} j=j+1 } if ok==1 { if at<0 { at=i+plen } } i=i+1 }
920 if at < 0 { return 0 - 1 }
921 var v: i64 = 0; var any: i64 = 0; var q: i64 = at
922 while q < rn { if req[q]>=(48 as u8) { if req[q]<=(57 as u8) { v=v*10+((req[q] as i64)-48); any=1; q=q+1 } else { q=rn } } else { q=rn } }
923 if any == 0 { return 0 - 1 }
924 return v
925}
926// derive the poster path: replace the final ".ext" with "-poster.jpg".
927func gs_poster_path(vid: *u8, out: *u8) -> i64 {
928 let nn: i64 = gs_strlen(vid)
929 var dot: i64 = nn
930 var i: i64 = nn - 1
931 while i >= 0 { if vid[i]==(46 as u8) { if dot==nn { dot = i } } i = i - 1 }
932 var o: i64 = 0; var k: i64 = 0
933 while k < dot { out[o]=vid[k]; o=o+1; k=k+1 }
934 let suf: *u8 = "-poster.jpg" as *u8
935 var s: i64 = 0
936 while suf[s]!=(0 as u8){ out[o]=suf[s]; o=o+1; s=s+1 }
937 out[o]=0 as u8
938 return 0
939}
940// GET /vidthumb/<id> -> the recording's poster jpg (the thumbnail). Small file -> via rbuf.
941func gs_vidthumb(rbuf: *u8, req: *u8, rn: i64) -> i64 {
942 let id: i64 = gs_peel_num(req, rn, "GET /vidthumb/" as *u8, 14)
943 if id < 0 { return gs_404(rbuf) }
944 let vid: *u8 = sys_mmap(VIEW_MAGIC_2048)
945 if gs_vid_line(id, vid) == 0 { return gs_404(rbuf) }
946 let poster: *u8 = sys_mmap(VIEW_MAGIC_2048)
947 gs_poster_path(vid, poster)
948 let szp: *i64 = sys_mmap(16) as *i64
949 let jpg: *u8 = sys_read_file(poster, szp)
950 let jl: i64 = szp[0]
951 if (jpg as i64) == 0 { return gs_404(rbuf) }
952 var o: i64 = 0
953 o = gs_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nCache-Control: max-age=86400\r\nContent-Length: " as *u8)
954 o = gs_u(rbuf, o, jl)
955 o = gs_cat(rbuf, o, "\r\nConnection: close\r\n\r\n" as *u8)
956 var i: i64 = 0
957 while i < jl { rbuf[o]=jpg[i]; o=o+1; i=i+1 }
958 return o
959}
960// ---- /vid playback: native containers (mp4/mov/webm/...) stream directly with HTTP Range (seekable + audio);
961// MPEG-TS is demuxed to fMP4 on the fly. All written DIRECTLY to cfd (files can be GBs). Returns 1 (sent). ----
962func gs_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 }
963// lowercased file extension (after the last '.' of the basename) -> ex (NUL-terminated, <=14 chars)
964func gs_vid_ext(vid: *u8, ex: *u8) -> i64 {
965 let nn: i64 = gs_strlen(vid)
966 var dot: i64 = 0 - 1; var go: i64 = 1; var i: i64 = nn - 1
967 while i >= 0 { if go==1 { if vid[i]==(47 as u8) { go=0 } else { if vid[i]==(46 as u8) { if dot<0 { dot=i } } } } i=i-1 }
968 var k: i64 = 0
969 if dot >= 0 { var p: i64 = dot+1; while p < nn { if k<14 { var c: i64=vid[p] as i64; if c>=65 { if c<=90 { c=c+32 } } ex[k]=c as u8; k=k+1 } p=p+1 } }
970 ex[k]=0 as u8
971 return k
972}
973func gs_vid_mime(ex: *u8) -> *u8 {
974 if gs_streq(ex, "webm" as *u8)==1 { return "video/webm" as *u8 }
975 if gs_streq(ex, "mkv" as *u8)==1 { return "video/x-matroska" as *u8 }
976 if gs_streq(ex, "flv" as *u8)==1 { return "video/x-flv" as *u8 }
977 return "video/mp4" as *u8
978}
979// serve a regular file with HTTP Range support (206/Content-Range/Accept-Ranges) so the browser can seek.
980func gs_serve_file_range(cfd: i64, req: *u8, rn: i64, path: *u8, ctype: *u8) -> i64 {
981 let nf: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8
982 let fd: i64 = sys_openat_rd(path)
983 if fd < 0 { sys_write(cfd, nf, gs_strlen(nf)); return 1 }
984 let size: i64 = sys_lseek(fd, 0, 2)
985 var hasrange: i64 = 0; var rstart: i64 = 0; var rend: i64 = size - 1
986 let pat: *u8 = "Range: bytes=" as *u8; let pl: i64 = 13
987 var i: i64 = 0; var at: i64 = 0 - 1
988 while i + pl <= rn { if at<0 { var j: i64=0; var ok: i64=1; while j<pl { if req[i+j]!=pat[j]{ok=0} j=j+1 } if ok==1 { at=i+pl } } i=i+1 }
989 if at >= 0 {
990 hasrange = 1
991 var p: i64 = at
992 var sv: i64 = 0; var sany: i64 = 0; var stop: i64 = 0
993 while stop==0 { if p>=rn { stop=1 } else { let c: i64=req[p] as i64; if c>=48 { if c<=57 { sv=sv*10+(c-48); sany=1; p=p+1 } else { stop=1 } } else { stop=1 } } }
994 if sany==1 { rstart = sv }
995 if p < rn { if req[p]==(45 as u8) { p=p+1 } }
996 var ev: i64 = 0; var eany: i64 = 0; stop=0
997 while stop==0 { if p>=rn { stop=1 } else { let c: i64=req[p] as i64; if c>=48 { if c<=57 { ev=ev*10+(c-48); eany=1; p=p+1 } else { stop=1 } } else { stop=1 } } }
998 if eany==1 { rend = ev }
999 }
1000 if rstart < 0 { rstart = 0 }
1001 if rstart >= size { rstart = 0; rend = size - 1; hasrange = 0 }
1002 if rend >= size { rend = size - 1 }
1003 if rend < rstart { rend = size - 1 }
1004 let clen: i64 = rend - rstart + 1
1005 let hb: *u8 = sys_mmap(VIEW_MAGIC_1024); var ho: i64 = 0
1006 if hasrange == 1 {
1007 ho = gs_cat(hb, ho, "HTTP/1.1 206 Partial Content\r\nContent-Type: " as *u8)
1008 ho = gs_cat(hb, ho, ctype)
1009 ho = gs_cat(hb, ho, "\r\nAccept-Ranges: bytes\r\nContent-Range: bytes " as *u8)
1010 ho = gs_u(hb, ho, rstart); hb[ho]=45 as u8; ho=ho+1; ho = gs_u(hb, ho, rend); hb[ho]=47 as u8; ho=ho+1; ho = gs_u(hb, ho, size)
1011 ho = gs_cat(hb, ho, "\r\nContent-Length: " as *u8); ho = gs_u(hb, ho, clen)
1012 ho = gs_cat(hb, ho, "\r\nCache-Control: max-age=3600\r\nConnection: close\r\n\r\n" as *u8)
1013 } else {
1014 ho = gs_cat(hb, ho, "HTTP/1.1 200 OK\r\nContent-Type: " as *u8)
1015 ho = gs_cat(hb, ho, ctype)
1016 ho = gs_cat(hb, ho, "\r\nAccept-Ranges: bytes\r\nContent-Length: " as *u8); ho = gs_u(hb, ho, size)
1017 ho = gs_cat(hb, ho, "\r\nCache-Control: max-age=3600\r\nConnection: close\r\n\r\n" as *u8)
1018 }
1019 sys_write(cfd, hb, ho)
1020 sys_lseek(fd, rstart, 0)
1021 let buf: *u8 = sys_mmap(VIEW_MAGIC_1048576)
1022 var remain: i64 = clen
1023 while remain > 0 {
1024 var want: i64 = VIEW_MAGIC_1048576
1025 if remain < want { want = remain }
1026 let got: i64 = sys_read(fd, buf, want)
1027 if got <= 0 { remain = 0 } else {
1028 var w: i64 = 0
1029 while w < got { let r2: i64 = sys_write(cfd, ((buf as i64)+w) as *u8, got-w); if r2<=0 { w=got; remain=0 } else { w=w+r2 } }
1030 if remain > 0 { remain = remain - got }
1031 }
1032 }
1033 sys_close(fd)
1034 return 1
1035}
1036// i64 -> NUL-terminated decimal string in buf; returns length.
1037func gs_itoa(v: i64, buf: *u8) -> i64 {
1038 var m: i64=v; if m<0 { m=0-m }
1039 let t: *u8=sys_mmap(32); var k: i64=0
1040 if m==0 { t[0]=(48 as u8); k=1 }
1041 while m>0 { let q: i64=m/10; t[k]=((48+(m-q*10)) as u8); m=q; k=k+1 }
1042 var i: i64=0; while i<k { buf[i]=t[k-1-i]; i=i+1 } buf[k]=(0 as u8)
1043 return k
1044}
1045// scan TS packets in b[0..n) for PCRs (adaptation field, PCR_flag 0x10, 33-bit base @90kHz).
1046func gs_pcr_scan(b: *u8, n: i64, fp: *i64, lp: *i64) -> i64 {
1047 var i: i64=0
1048 while i+188<=n {
1049 if b[i]!=(0x47 as u8) { i=i+1 } else {
1050 let afc: i64=((b[i+3] as i64)>>4)&3
1051 if afc>=2 { let afl: i64=b[i+4] as i64; if afl>0 { let fl: i64=b[i+5] as i64; if ((fl>>4)&1)==1 {
1052 let base: i64=((b[i+6] as i64)<<25)|((b[i+7] as i64)<<17)|((b[i+8] as i64)<<9)|((b[i+9] as i64)<<1)|((b[i+10] as i64)>>7)
1053 if fp[0]<0 { fp[0]=base } lp[0]=base
1054 } } }
1055 i=i+188
1056 }
1057 }
1058 return 0
1059}
1060// MPEG-TS duration in ms via first+last PCR (reads only an 8MB window at each end, no full scan).
1061func gs_ts_dur(path: *u8) -> i64 {
1062 let fd: i64=sys_openat_rd(path); if fd<0 { return 0 }
1063 let size: i64=sys_lseek(fd,0,2)
1064 let W: i64=VIEW_MAGIC_8388608
1065 let b: *u8=sys_mmap(W+16)
1066 let fp: *i64=sys_mmap(16) as *i64; fp[0]=0-1
1067 let lp: *i64=sys_mmap(16) as *i64; lp[0]=0-1
1068 sys_lseek(fd,0,0)
1069 var got: i64=0; var go: i64=1
1070 while go==1 { if got>=W { go=0 } else { let r: i64=sys_read(fd,((b as i64)+got) as *u8,W-got); if r<=0 { go=0 } else { got=got+r } } }
1071 gs_pcr_scan(b,got,fp,lp)
1072 let first: i64=fp[0]
1073 let fp2: *i64=sys_mmap(16) as *i64; fp2[0]=0-1
1074 let lp2: *i64=sys_mmap(16) as *i64; lp2[0]=0-1
1075 var es: i64=size-W; if es<0 { es=0 }
1076 sys_lseek(fd,es,0)
1077 got=0; go=1
1078 while go==1 { if got>=W { go=0 } else { let r: i64=sys_read(fd,((b as i64)+got) as *u8,W-got); if r<=0 { go=0 } else { got=got+r } } }
1079 gs_pcr_scan(b,got,fp2,lp2)
1080 let last: i64=lp2[0]
1081 sys_close(fd)
1082 if first<0 { return 0 }
1083 if last<0 { return 0 }
1084 var d: i64=last-first; if d<=0 { return 0 } if d>VIEW_MAGIC_7776000000 { return 0 }
1085 return d/90
1086}
1087// current galx_vid_paths.tsv size -> freshness key for galx_dur.bin (line N -> video N valid while unchanged).
1088func gs_vidpaths_size() -> i64 {
1089 let tfd: i64 = sys_openat_rd("knowledge/status/galx_vid_paths.tsv" as *u8)
1090 if tfd < 0 { return 0 }
1091 let s: i64 = sys_lseek(tfd, 0, 2); sys_close(tfd); return s
1092}
1093// GET /vid/<id>/dur -> total duration in ms (text). The .ts seekbar needs this (fly-remux has no Content-Length).
1094// FAST PATH: O(1) precomputed lookup in galx_dur.bin (built by nx_galx_durbin from the durindex output);
1095// a <=0 result (not-yet-indexed / unknown) falls back to the live 16MB first+last-PCR scan, so never wrong.
1096func gs_vid_dur_reply(cfd: i64, vid: *u8, id: i64) -> i64 {
1097 var dur: i64 = db_lookup("knowledge/status/galx_dur.bin" as *u8, id, gs_vidpaths_size())
1098 if dur <= 0 { dur = gs_ts_dur(vid) }
1099 let body: *u8=sys_mmap(32); let blen: i64=gs_itoa(dur, body)
1100 let hb: *u8=sys_mmap(256); var ho: i64=0
1101 ho=gs_cat(hb,ho,"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: no-store\r\nConnection: close\r\nContent-Length: " as *u8)
1102 ho=gs_u(hb,ho,blen)
1103 ho=gs_cat(hb,ho,"\r\n\r\n" as *u8)
1104 sys_write(cfd,hb,ho); sys_write(cfd,body,blen)
1105 return 1
1106}
1107// reload-on-seek: ?t=<ms>&d=<dur_ms> -> proportional start byte = size * t / dur (client passes dur from /dur).
1108func gs_seek_byte(req: *u8, rn: i64, vid: *u8) -> i64 {
1109 let f: i64=gs_peel_num(req,rn,"?b=" as *u8,3)
1110 if f<=0 { return 0 }
1111 let fd: i64=sys_openat_rd(vid); if fd<0 { return 0 }
1112 let size: i64=sys_lseek(fd,0,2); sys_close(fd)
1113 return (size*f)/VIEW_MAGIC_1000000
1114}
1115// GET /vid/<id>/init.mp4 -> MSE init segment (ftyp+moov, both tracks) via the muxer's init mode.
1116func gs_vid_init(cfd: i64, vid: *u8) -> i64 {
1117 let hdr: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: max-age=3600\r\nConnection: close\r\n\r\n" as *u8
1118 sys_write(cfd, hdr, gs_strlen(hdr))
1119 sys_dup3(cfd, 1, 0)
1120 let wpath: *u8 = "/volume1/ai/galx/nx_ts2fmp4.elf" as *u8
1121 let mi: *u8 = "init" as *u8
1122 let argv: *i64 = sys_mmap(64) as *i64
1123 argv[0]=wpath as i64; argv[1]=vid as i64; argv[2]=mi as i64; argv[3]=0
1124 let envp: *i64 = sys_mmap(8) as *i64; envp[0]=0
1125 sys_execve_clean(wpath, argv, envp); sys_exit(127); return 1
1126}
1127// ---- sovereign MSE backend: read the NXVI index, serve init/segments, lazy-build the index ----
1128func gs_be32(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<24)|((b[o+1] as i64)<<16)|((b[o+2] as i64)<<8)|(b[o+3] as i64) }
1129func gs_be64(b: *u8, o: i64) -> i64 { return (gs_be32(b,o)<<32)|(gs_be32(b,o+4)&0xffffffff) }
1130func gs_hex2(b: *u8, o: i64, v: i64) -> i64 { let h: *u8="0123456789abcdef" as *u8; b[o]=h[(v>>4)&0xf]; b[o+1]=h[v&0xf]; return o+2 }
1131func gs_idxpath(id: i64, buf: *u8) -> i64 { var o: i64=gs_cat(buf,0,"knowledge/status/galxidx_" as *u8); let d: *u8=sys_mmap(32); let dl: i64=gs_itoa(id,d); var k: i64=0; while k<dl { buf[o]=d[k]; o=o+1; k=k+1 } o=gs_cat(buf,o,".idx" as *u8); buf[o]=0 as u8; return o }
1132// fork a detached background indexer (nx_ts_index <vid> <idxpath>); orphan survives the request child.
1133func gs_idx_build(id: i64, vid: *u8) -> i64 {
1134 let p: i64 = sys_fork()
1135 if p == 0 {
1136 let ipath: *u8 = sys_mmap(256); gs_idxpath(id, ipath)
1137 let wpath: *u8 = "/volume1/ai/galx/nx_ts_index.elf" as *u8
1138 let argv: *i64 = sys_mmap(64) as *i64
1139 argv[0]=wpath as i64; argv[1]=vid as i64; argv[2]=ipath as i64; argv[3]=0
1140 let envp: *i64 = sys_mmap(8) as *i64; envp[0]=0
1141 sys_execve_clean(wpath, argv, envp); sys_exit(127)
1142 }
1143 return 0
1144}
1145// GET /vid/<id>/seg?t=<ms> -> the media segment (moof+mdat) for the keyframe covering <ms>, via muxer seg mode.
1146func gs_vid_seg(cfd: i64, vid: *u8, sb: i64, eb: i64, base: i64, vpid: i64, apid: i64) -> i64 {
1147 let hdr: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: max-age=3600\r\nConnection: close\r\n\r\n" as *u8
1148 sys_write(cfd, hdr, gs_strlen(hdr)); sys_dup3(cfd, 1, 0)
1149 let wpath: *u8 = "/volume1/ai/galx/nx_ts2fmp4.elf" as *u8
1150 let ms: *u8 = "seg" as *u8
1151 let a3: *u8 = sys_mmap(32); gs_itoa(sb, a3); let a4: *u8 = sys_mmap(32); gs_itoa(eb, a4); let a5: *u8 = sys_mmap(32); gs_itoa(base, a5)
1152 var vp: i64 = vpid; if vp < 1 { vp = 0 } if vp > VIEW_MAGIC_8191 { vp = 0 } var ap: i64 = apid; if ap < 1 { ap = 0 } if ap > VIEW_MAGIC_8191 { ap = 0 }
1153 let a6: *u8 = sys_mmap(32); gs_itoa(vp, a6); let a7: *u8 = sys_mmap(32); gs_itoa(ap, a7)
1154 let argv: *i64 = sys_mmap(64) as *i64
1155 argv[0]=wpath as i64; argv[1]=vid as i64; argv[2]=ms as i64; argv[3]=a3 as i64; argv[4]=a4 as i64; argv[5]=a5 as i64; argv[6]=a6 as i64; argv[7]=a7 as i64; argv[8]=0
1156 let envp: *i64 = sys_mmap(8) as *i64; envp[0]=0
1157 sys_execve_clean(wpath, argv, envp); sys_exit(127); return 1
1158}
1159func gs_503(cfd: i64) -> i64 { let m: *u8="HTTP/1.1 503 Service Unavailable\r\nContent-Type: text/plain\r\nCache-Control: no-store\r\nContent-Length: 8\r\nConnection: close\r\n\r\nbuilding" as *u8; sys_write(cfd, m, gs_strlen(m)); return 1 }
1160func gs_seg_handler(cfd: i64, id: i64, vid: *u8, req: *u8, rn: i64) -> i64 {
1161 let ipath: *u8 = sys_mmap(256); gs_idxpath(id, ipath)
1162 let szp: *i64 = sys_mmap(16) as *i64
1163 let b: *u8 = sys_read_file(ipath, szp)
1164 if (b as i64)==0 { gs_idx_build(id, vid); return gs_503(cfd) }
1165 if szp[0] < 48 { return gs_503(cfd) }
1166 if b[0]!=(78 as u8) { return gs_503(cfd) }
1167 if gs_be32(b,4)!=2 { gs_idx_build(id, vid); return gs_503(cfd) }
1168 var tt: i64 = gs_peel_num(req, rn, "?t=" as *u8, 3); if tt<0 { tt=0 }
1169 let vidpid: i64 = gs_be32(b, 24)
1170 let audpid: i64 = gs_be32(b, 28)
1171 let spslen: i64 = gs_be32(b, 32)
1172 let ppslen: i64 = gs_be32(b, 36+spslen)
1173 let nkf: i64 = gs_be64(b, 40+spslen+ppslen)
1174 let kfo: i64 = 48 + spslen + ppslen
1175 var n: i64 = 0; var i: i64 = 0
1176 while i < nkf { let tm: i64 = gs_be64(b, kfo+i*16); if tm <= tt { n=i } else { i=nkf } i=i+1 }
1177 let sb: i64 = gs_be64(b, kfo+n*16+8)
1178 var eb: i64 = sb + VIEW_MAGIC_50000000
1179 if n+1 < nkf { eb = gs_be64(b, kfo+(n+1)*16+8) }
1180 let base: i64 = gs_be64(b, kfo+n*16)
1181 return gs_vid_seg(cfd, vid, sb, eb, base, vidpid, audpid)
1182}
1183// browser-native containers (mp4/m4v/mov/webm) play + seek directly via HTTP Range -> no NXVI index/MSE needed.
1184func gs_vid_native(ex: *u8) -> i64 { if gs_streq(ex,"mp4" as *u8)==1 {return 1} if gs_streq(ex,"m4v" as *u8)==1 {return 1} if gs_streq(ex,"mov" as *u8)==1 {return 1} if gs_streq(ex,"webm" as *u8)==1 {return 1} return 0 }
1185func gs_segs_handler(cfd: i64, id: i64, vid: *u8) -> i64 {
1186 let ex0: *u8 = sys_mmap(16); gs_vid_ext(vid, ex0)
1187 if gs_vid_native(ex0)==1 { let nb: *u8 = sys_mmap(128); let nl: i64 = gs_okjson(nb, "{\"native\":1}" as *u8, 12); sys_write(cfd, nb, nl); return 1 }
1188 if gs_streq(ex0,"ts" as *u8)==0 { if gs_streq(ex0,"m2ts" as *u8)==0 { let rb0: *u8 = sys_mmap(128); let rl0: i64 = gs_okjson(rb0, "{\"ready\":0}" as *u8, 11); sys_write(cfd, rb0, rl0); return 1 } }
1189 let ipath: *u8 = sys_mmap(256); gs_idxpath(id, ipath)
1190 let szp: *i64 = sys_mmap(16) as *i64
1191 let b: *u8 = sys_read_file(ipath, szp)
1192 let body: *u8 = sys_mmap(128); var bo: i64 = 0
1193 if (b as i64)==0 { gs_idx_build(id, vid); bo=gs_cat(body,0,"{\"ready\":0}" as *u8) } else { if b[0]!=(78 as u8) { bo=gs_cat(body,0,"{\"ready\":0}" as *u8) } else { if gs_be32(b,4)!=2 { gs_idx_build(id, vid); bo=gs_cat(body,0,"{\"ready\":0}" as *u8) } else {
1194 let dur: i64 = gs_be64(b, 8); let spslen: i64 = gs_be32(b, 32); let ppslen: i64 = gs_be32(b, 36+spslen); let nkf: i64 = gs_be64(b, 40+spslen+ppslen)
1195 if nkf > 0 { bo=gs_cat(body,0,"{\"ready\":1,\"dur\":" as *u8); bo=gs_u(body,bo,dur); bo=gs_cat(body,bo,",\"n\":" as *u8); bo=gs_u(body,bo,nkf); bo=gs_cat(body,bo,",\"codec\":\"avc1." as *u8); bo=gs_hex2(body,bo,b[37] as i64); bo=gs_hex2(body,bo,b[38] as i64); bo=gs_hex2(body,bo,b[39] as i64); bo=gs_cat(body,bo,"\"}" as *u8) } else { bo=gs_cat(body,0,"{\"ready\":0}" as *u8) }
1196 } } }
1197 let rb: *u8 = sys_mmap(512); let ln: i64 = gs_okjson(rb, body, bo); sys_write(cfd, rb, ln); return 1
1198}
1199// remux an MPEG-TS recording to fragmented MP4 on the fly (stdout = the client socket).
1200func gs_vid_remux_ts(cfd: i64, vid: *u8, start_byte: i64) -> i64 {
1201 let hdr: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8
1202 sys_write(cfd, hdr, gs_strlen(hdr))
1203 sys_dup3(cfd, 1, 0)
1204 let wpath: *u8 = "/volume1/ai/galx/nx_ts2fmp4.elf" as *u8
1205 let argv: *i64 = sys_mmap(64) as *i64
1206 argv[0] = wpath as i64; argv[1] = vid as i64
1207 if start_byte > 0 { let sb: *u8 = sys_mmap(32); gs_itoa(start_byte, sb); let mz: *u8 = "0" as *u8; argv[2] = mz as i64; argv[3] = sb as i64; argv[4] = 0 } else { argv[2] = 0 }
1208 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
1209 sys_execve_clean(wpath, argv, envp)
1210 sys_exit(127)
1211 return 1
1212}
1213// remux a Matroska (.mkv) recording to fragmented MP4 on the fly (stdout = client socket). H.264/HEVC video.
1214func gs_vid_remux_mkv(cfd: i64, vid: *u8, start_byte: i64) -> i64 {
1215 let hdr: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8
1216 sys_write(cfd, hdr, gs_strlen(hdr))
1217 sys_dup3(cfd, 1, 0)
1218 let wpath: *u8 = "/volume1/ai/galx/nx_mkv2fmp4.elf" as *u8
1219 let argv: *i64 = sys_mmap(64) as *i64
1220 argv[0] = wpath as i64; argv[1] = vid as i64
1221 if start_byte > 0 { let sb: *u8 = sys_mmap(32); gs_itoa(start_byte, sb); argv[2] = sb as i64; argv[3] = 0 } else { argv[2] = 0 }
1222 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
1223 sys_execve_clean(wpath, argv, envp); sys_exit(127); return 1
1224}
1225// GET /vid/<id>/marks -> clip-analysis markers (dead-air etc.) from the NXVI's back-compat NXMK sidecar tail.
1226// JSON [{type,start,end}] in ms (type 1=dead-air). Empty [] if no index or a legacy idx without the tail.
1227func gs_vid_marks(cfd: i64, id: i64) -> i64 {
1228 let ipath: *u8 = sys_mmap(256); gs_idxpath(id, ipath)
1229 let szp: *i64 = sys_mmap(16) as *i64
1230 let b: *u8 = sys_read_file(ipath, szp)
1231 let body: *u8 = sys_mmap(VIEW_MAGIC_262144); var bo: i64 = gs_cat(body, 0, "[" as *u8)
1232 if (b as i64) != 0 { if szp[0] > 48 { if b[0]==(78 as u8) {
1233 let spslen: i64 = gs_be32(b, 32)
1234 let ppslen: i64 = gs_be32(b, 36+spslen)
1235 let nkf: i64 = gs_be64(b, 40+spslen+ppslen)
1236 let mo: i64 = 48 + spslen + ppslen + nkf*16
1237 if (mo+12) <= szp[0] { if b[mo]==(78 as u8) { if b[mo+1]==(88 as u8) { if b[mo+2]==(77 as u8) { if b[mo+3]==(75 as u8) {
1238 let nm: i64 = gs_be64(b, mo+4)
1239 var ko: i64 = mo + 12
1240 var i: i64 = 0
1241 while i < nm {
1242 if (ko+24) <= szp[0] {
1243 let ty: i64 = gs_be32(b, ko)
1244 let st: i64 = gs_be64(b, ko+4)
1245 let en: i64 = gs_be64(b, ko+12)
1246 if i > 0 { bo = gs_cat(body, bo, "," as *u8) }
1247 bo = gs_cat(body, bo, "{\"type\":" as *u8); bo = gs_u(body, bo, ty)
1248 bo = gs_cat(body, bo, ",\"start\":" as *u8); bo = gs_u(body, bo, st)
1249 bo = gs_cat(body, bo, ",\"end\":" as *u8); bo = gs_u(body, bo, en)
1250 bo = gs_cat(body, bo, "}" as *u8)
1251 }
1252 ko = ko + 24
1253 i = i + 1
1254 }
1255 } } } } }
1256 } } }
1257 bo = gs_cat(body, bo, "]" as *u8)
1258 let rb: *u8 = sys_mmap(VIEW_MAGIC_262208); let ln: i64 = gs_okjson(rb, body, bo)
1259 sys_write(cfd, rb, ln); return 1
1260}
1261// GET /vid/<id>/analysis -> the FIRST classification rung: fuse the NXMK markers + duration into a coarse content
1262// label (quiet/active/rhythmic-dance/scene-heavy/mixed) + the profile. ts_classify (gated 11/11) does the join.
1263func gs_vid_analysis(cfd: i64, id: i64) -> i64 {
1264 let ipath: *u8 = sys_mmap(256); gs_idxpath(id, ipath)
1265 let szp: *i64 = sys_mmap(16) as *i64
1266 let b: *u8 = sys_read_file(ipath, szp)
1267 let mt: *i64 = sys_mmap(8*VIEW_MAGIC_4096) as *i64; let ms: *i64 = sys_mmap(8*VIEW_MAGIC_4096) as *i64; let me: *i64 = sys_mmap(8*VIEW_MAGIC_4096) as *i64
1268 var nm: i64 = 0
1269 var dur: i64 = 0
1270 if (b as i64) != 0 { if szp[0] > 48 { if b[0]==(78 as u8) {
1271 dur = gs_be64(b, 8)
1272 let spslen: i64 = gs_be32(b, 32)
1273 let ppslen: i64 = gs_be32(b, 36+spslen)
1274 let nkf: i64 = gs_be64(b, 40+spslen+ppslen)
1275 let mo: i64 = 48 + spslen + ppslen + nkf*16
1276 if (mo+12) <= szp[0] { if b[mo]==(78 as u8) { if b[mo+1]==(88 as u8) { if b[mo+2]==(77 as u8) { if b[mo+3]==(75 as u8) {
1277 let cnt: i64 = gs_be64(b, mo+4)
1278 var ko: i64 = mo + 12
1279 var i: i64 = 0
1280 while i < cnt {
1281 if (ko+24) <= szp[0] { if nm < VIEW_MAGIC_4096 { mt[nm] = gs_be32(b, ko); ms[nm] = gs_be64(b, ko+4); me[nm] = gs_be64(b, ko+12); nm = nm + 1 } }
1282 ko = ko + 24
1283 i = i + 1
1284 }
1285 } } } } }
1286 } } }
1287 let out: *i64 = sys_mmap(8*8) as *i64
1288 let label: i64 = ts_classify(mt, ms, me, nm, dur, out)
1289 var lbl: *u8 = "mixed" as *u8
1290 if label == 0 { lbl = "quiet/static" as *u8 }
1291 if label == 1 { lbl = "active/dynamic" as *u8 }
1292 if label == 2 { lbl = "rhythmic/dance-like" as *u8 }
1293 if label == 3 { lbl = "scene-heavy" as *u8 }
1294 let body: *u8 = sys_mmap(512); var bo: i64 = 0
1295 bo = gs_cat(body, bo, "{\"label\":\"" as *u8); bo = gs_cat(body, bo, lbl)
1296 bo = gs_cat(body, bo, "\",\"dur\":" as *u8); bo = gs_u(body, bo, dur)
1297 bo = gs_cat(body, bo, ",\"quiet_ms\":" as *u8); bo = gs_u(body, bo, out[0])
1298 bo = gs_cat(body, bo, ",\"active_ms\":" as *u8); bo = gs_u(body, bo, out[1])
1299 bo = gs_cat(body, bo, ",\"rhythmic_ms\":" as *u8); bo = gs_u(body, bo, out[2])
1300 bo = gs_cat(body, bo, ",\"scenes\":" as *u8); bo = gs_u(body, bo, out[3])
1301 bo = gs_cat(body, bo, ",\"marks\":" as *u8); bo = gs_u(body, bo, nm)
1302 bo = gs_cat(body, bo, "}" as *u8)
1303 let rb: *u8 = sys_mmap(VIEW_MAGIC_1024); let ln: i64 = gs_okjson(rb, body, bo)
1304 sys_write(cfd, rb, ln); return 1
1305}
1306// GET /vid/<id> -> play a recording: native container direct (Range) or MPEG-TS/Matroska remux.
1307func gs_vid_stream(cfd: i64, req: *u8, rn: i64) -> i64 {
1308 let nf: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8
1309 let id: i64 = gs_peel_num(req, rn, "GET /vid/" as *u8, 9)
1310 if id < 0 { sys_write(cfd, nf, gs_strlen(nf)); return 1 }
1311 let vid: *u8 = sys_mmap(VIEW_MAGIC_2048)
1312 if gs_vid_line(id, vid) == 0 { sys_write(cfd, nf, gs_strlen(nf)); return 1 }
1313 if eh_find(req, rn, "/init.mp4" as *u8, 9) == 1 { return gs_vid_init(cfd, vid) }
1314 if eh_find(req, rn, "/dur" as *u8, 4) == 1 { return gs_vid_dur_reply(cfd, vid, id) }
1315 if eh_find(req, rn, "/segs" as *u8, 5) == 1 { return gs_segs_handler(cfd, id, vid) }
1316 if eh_find(req, rn, "/marks" as *u8, 6) == 1 { return gs_vid_marks(cfd, id) }
1317 if eh_find(req, rn, "/analysis" as *u8, 9) == 1 { return gs_vid_analysis(cfd, id) }
1318 if eh_find(req, rn, "/seg" as *u8, 4) == 1 { return gs_seg_handler(cfd, id, vid, req, rn) }
1319 let ex: *u8 = sys_mmap(16)
1320 gs_vid_ext(vid, ex)
1321 if gs_streq(ex, "ts" as *u8)==1 { return gs_vid_remux_ts(cfd, vid, gs_seek_byte(req, rn, vid)) }
1322 if gs_streq(ex, "m2ts" as *u8)==1 { return gs_vid_remux_ts(cfd, vid, gs_seek_byte(req, rn, vid)) }
1323 if gs_streq(ex, "mkv" as *u8)==1 { return gs_vid_remux_mkv(cfd, vid, gs_seek_byte(req, rn, vid)) }
1324 return gs_serve_file_range(cfd, req, rn, vid, gs_vid_mime(ex))
1325}
1326// resolve a video path's collection from the data-driven rules file (substring<TAB>cat, first match wins).
1327// 'r'=Recordings (cam) / 'm'=Media (SFW) / 'v'=Videos (NSFW). default 'r'. Lets sources be re-sorted w/o rebuild.
1328func gs_path_cat(b: *u8, ls: i64, le: i64, rb: *u8, rsz: i64) -> i64 {
1329 if (rb as i64) == 0 { return 114 }
1330 var i: i64 = 0; var lstart: i64 = 0
1331 while i <= rsz {
1332 var nl: i64 = 0; if i==rsz { nl=1 } else { if rb[i]==(10 as u8) { nl=1 } }
1333 if nl==1 {
1334 var tab: i64 = 0-1; var p: i64 = lstart
1335 while p < i { if rb[p]==(9 as u8) { if tab<0 { tab=p } } p=p+1 }
1336 if tab > lstart { if tab+1 < i {
1337 let slen: i64 = tab - lstart
1338 var q: i64 = ls; var found: i64 = 0
1339 while q + slen <= le {
1340 var j: i64=0; var ok: i64=1
1341 while j<slen { if b[q+j]!=rb[lstart+j]{ok=0; j=slen} else { j=j+1 } }
1342 if ok==1 { found=1; q=le } else { q=q+1 }
1343 }
1344 if found==1 { return rb[tab+1] as i64 }
1345 } }
1346 lstart = i+1
1347 }
1348 i = i+1
1349 }
1350 return 114
1351}
1352// ---- per-category INDEX (R4: O(page) collection listing instead of a 372k-line scan + rule-match/request) ----
1353// At startup gs_catidx_build scans galx_vid_paths.tsv ONCE, applies the rules (gs_path_cat), and writes a
1354// per-category file galx_cat_<c>.bin = [tsv_size i64][count i64][lineno i64 ...] (file order; reader slices
1355// newest-first). gs_catidx_emit serves a page directly from that file; if it is missing or its tsv_size stamp
1356// != the live galx_vid_paths.tsv size (stale), it returns 0 and gs_collection_list FALLS BACK to the full scan
1357// -> zero-regression. Research-grounded (a keyset/index slice is ~hundreds x faster at depth than an offset scan).
1358func gs_file_size(path: *u8) -> i64 {
1359 let fd: i64 = sys_openat_rd(path)
1360 if fd < 0 { return 0 - 1 }
1361 let sz: i64 = sys_lseek(fd, 0, 2)
1362 sys_close(fd)
1363 return sz
1364}
1365func gs_catidx_path(wantc: i64, out: *u8) -> i64 {
1366 var o: i64 = gs_cat(out, 0, "knowledge/status/galx_cat_" as *u8)
1367 out[o] = wantc as u8; o = o + 1
1368 o = gs_cat(out, o, ".bin" as *u8); out[o] = 0 as u8
1369 return o
1370}
1371func gs_catidx_write(path: *u8, tsvsz: i64, arr: *i64, cnt: i64) -> i64 {
1372 let ob: *u8 = sys_mmap(16 + cnt * 8 + 64)
1373 gs_wb64(ob, 0, tsvsz); gs_wb64(ob, 8, cnt)
1374 var k: i64 = 0; while k < cnt { gs_wb64(ob, 16 + k * 8, arr[k]); k = k + 1 }
1375 let fd: i64 = sys_openat_wr(path, 0x1a4) // 0644
1376 if fd < 0 { return 0 - 1 }
1377 sys_write(fd, ob, 16 + cnt * 8)
1378 sys_close(fd)
1379 return cnt
1380}
1381// Scan galx_vid_paths.tsv once + bucket line numbers by category, matching gs_collection_list's lineno/keep
1382// semantics EXACTLY (lineno counts every line; only lines with len>3 are bucketed; the same gs_path_cat rules).
1383func gs_catidx_build() -> i64 {
1384 let rszp: *i64 = sys_mmap(16) as *i64
1385 let rb: *u8 = sys_read_file("knowledge/status/galx_vid_rules.tsv" as *u8, rszp)
1386 let rsz: i64 = rszp[0]
1387 let szp: *i64 = sys_mmap(16) as *i64
1388 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
1389 let sz: i64 = szp[0]
1390 if (b as i64) == 0 { return 0 }
1391 let ar: *i64 = sys_mmap(8 * VIEW_MAGIC_1500000) as *i64; var nr: i64 = 0
1392 let am: *i64 = sys_mmap(8 * VIEW_MAGIC_1500000) as *i64; var nm: i64 = 0
1393 let av: *i64 = sys_mmap(8 * VIEW_MAGIC_1500000) as *i64; var nv: i64 = 0
1394 var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
1395 while i <= sz {
1396 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
1397 if nl == 1 {
1398 if i - ls > 3 {
1399 let cat: i64 = gs_path_cat(b, ls, i, rb, rsz)
1400 if cat == 114 { if nr < VIEW_MAGIC_1500000 { ar[nr] = lineno; nr = nr + 1 } }
1401 else { if cat == 109 { if nm < VIEW_MAGIC_1500000 { am[nm] = lineno; nm = nm + 1 } }
1402 else { if cat == 118 { if nv < VIEW_MAGIC_1500000 { av[nv] = lineno; nv = nv + 1 } } } }
1403 }
1404 lineno = lineno + 1; ls = i + 1
1405 }
1406 i = i + 1
1407 }
1408 let pth: *u8 = sys_mmap(256)
1409 gs_catidx_path(114, pth); gs_catidx_write(pth, sz, ar, nr)
1410 gs_catidx_path(109, pth); gs_catidx_write(pth, sz, am, nm)
1411 gs_catidx_path(118, pth); gs_catidx_write(pth, sz, av, nv)
1412 return nr + nm + nv
1413}
1414// Serve one page from the pre-built category index, newest-first. Returns the response length, or 0 if the
1415// index is missing/stale (the caller then does the full scan = the fallback). Matches gs_collection_list output.
1416// Discovery offset (2026-06-28 deep-research validated): surface the MIDDLE of a collection, not just
1417// the ends. A materialized rank array (our catidx/matched[]) makes jump-to-position O(1) -- the
1418// Elasticsearch search_after "no random access" limit applies to disk cursors, not in-RAM arrays.
1419// &pct=N -> jump to N% through the collection (0..100), deterministic.
1420// &rand=1 -> random page (mono-clock nsec seed) so repeated loads roam the whole library, not the ends.
1421// Returns o0 when neither is set. Next rung: least-recently-surfaced weighted rotation (Efraimidis-Spirakis).
1422func gs_eff_offset(req: *u8, rn: i64, count: i64, o0: i64) -> i64 {
1423 if count <= 0 { return 0 }
1424 let pct: i64 = gs_qint(req, rn, "pct=", 0 - 1)
1425 if pct >= 0 { var pc: i64 = pct; if pc > 100 { pc = 100 } var off: i64 = (count * pc) / 100; if off >= count { off = count - 1 } return off }
1426 let rnd: i64 = gs_qint(req, rn, "rand=", 0)
1427 if rnd == 1 { let ts: *i64 = sys_mmap(16) as *i64; sys_clock_gettime_mono(ts); var seed: i64 = ts[1]; if seed < 0 { seed = 0 - seed } return seed % count }
1428 return o0
1429}
1430func gs_catidx_emit(rbuf: *u8, req: *u8, rn: i64, wantc: i64, o0: i64, n: i64) -> i64 {
1431 let live: i64 = gs_file_size("knowledge/status/galx_vid_paths.tsv" as *u8)
1432 if live < 0 { return 0 }
1433 let pth: *u8 = sys_mmap(256); gs_catidx_path(wantc, pth)
1434 let szp: *i64 = sys_mmap(16) as *i64
1435 let cb: *u8 = sys_read_file(pth, szp)
1436 if (cb as i64) == 0 { return 0 }
1437 if szp[0] < 16 { return 0 }
1438 if gs_rb64(cb, 0) != live { return 0 } // stale -> fall back to the scan
1439 let count: i64 = gs_rb64(cb, 8)
1440 let eo0: i64 = gs_eff_offset(req, rn, count, o0)
1441 let body: *u8 = sys_mmap(VIEW_MAGIC_65536); var bo: i64 = 0
1442 bo = gs_cat(body, bo, "{\"kind\":\"rec\",\"total\":" as *u8); bo = gs_u(body, bo, count); bo = gs_cat(body, bo, ",\"items\":[" as *u8)
1443 var k: i64 = 0; var first: i64 = 1
1444 while k < n { let disp: i64 = eo0 + k; if disp >= count { k = n } else { let id: i64 = gs_rb64(cb, 16 + (count - 1 - disp) * 8); if first == 0 { body[bo] = 44 as u8; bo = bo + 1 } first = 0; body[bo] = 34 as u8; bo = bo + 1; bo = gs_u(body, bo, id); body[bo] = 34 as u8; bo = bo + 1; k = k + 1 } }
1445 bo = gs_cat(body, bo, "]}" as *u8)
1446 return gs_okjson(rbuf, body, bo)
1447}
1448// R2 sort-by-key handlers (ss_emit_size / ss_emit_runtime) live in nx_galx_sortindex.nx (imported above) and
1449// are sovereignly gated by nx_galx_sortserve_gate; the daemon's sort= routes call them directly. Index format:
1450// galx_sort_<key>_<c>.bin = [stamp=vid_paths size][count][ids asc-by-key], read fwd(asc)/bwd(desc) + si_eff_offset.
1451// GET /api/list?filter=recordings|media|videos[&model=] -> ids of that collection, newest-first.
1452func gs_collection_list(rbuf: *u8, req: *u8, rn: i64, wantc: i64, o0: i64, n: i64) -> i64 {
1453 let mbuf: *u8 = sys_mmap(256); var mlen: i64 = 0
1454 var li: i64 = 0; var mpos: i64 = 0 - 1; var stop: i64 = rn
1455 while li < stop { if req[li]==(10 as u8) { stop = li } else { if mpos < 0 { if li+6 <= rn { if req[li]==(109 as u8){ if req[li+1]==(111 as u8){ if req[li+2]==(100 as u8){ if req[li+3]==(101 as u8){ if req[li+4]==(108 as u8){ if req[li+5]==(61 as u8){ mpos = li+6 } } } } } } } } li = li + 1 } }
1456 if mpos >= 0 { var p: i64 = mpos; var go: i64 = 1; while go == 1 { if p >= rn { go = 0 } else { let c: i64 = req[p] as i64; if c==38 { go=0 } else { if c==32 { go=0 } else { if c==10 { go=0 } else { if c==13 { go=0 } else { if mlen < 255 { mbuf[mlen]=c as u8; mlen=mlen+1 } p=p+1 } } } } } } }
1457 // R4 fast path: no model filter -> serve the page straight from the pre-built category index (fresh check
1458 // inside). Returns 0 (miss/stale) -> fall through to the full scan below. The &model= path always scans.
1459 if mlen == 0 { let fr: i64 = gs_catidx_emit(rbuf, req, rn, wantc, o0, n); if fr > 0 { return fr } }
1460 let rszp: *i64 = sys_mmap(16) as *i64
1461 let rb: *u8 = sys_read_file("knowledge/status/galx_vid_rules.tsv" as *u8, rszp)
1462 let rsz: i64 = rszp[0]
1463 let szp: *i64 = sys_mmap(16) as *i64
1464 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
1465 let sz: i64 = szp[0]
1466 let matched: *i64 = sys_mmap(8 * VIEW_MAGIC_1000000) as *i64; var nmatch: i64 = 0
1467 if (b as i64) != 0 {
1468 var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
1469 while i <= sz {
1470 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
1471 if nl == 1 {
1472 if i - ls > 3 {
1473 let cat: i64 = gs_path_cat(b, ls, i, rb, rsz)
1474 if cat == wantc {
1475 var keep: i64 = 1
1476 if mlen > 0 {
1477 var last: i64 = 0-1; var prev: i64 = 0-1; var p: i64 = ls
1478 while p < i { if b[p]==(47 as u8) { prev = last; last = p } p = p + 1 }
1479 keep = 0
1480 if prev >= 0 { if last > prev { let ps: i64 = prev+1; let pl: i64 = last-ps
1481 if pl == mlen { var eq: i64 = 1; var c: i64 = 0; while c < pl { if b[ps+c] != mbuf[c] { eq = 0 } c = c + 1 } if eq == 1 { keep = 1 } }
1482 } }
1483 }
1484 if keep == 1 { if nmatch < VIEW_MAGIC_1000000 { matched[nmatch] = lineno; nmatch = nmatch + 1 } }
1485 }
1486 }
1487 lineno = lineno + 1; ls = i + 1
1488 }
1489 i = i + 1
1490 }
1491 }
1492 let body: *u8 = sys_mmap(VIEW_MAGIC_65536); var bo: i64 = 0
1493 bo = gs_cat(body, bo, "{\"kind\":\"rec\",\"total\":" as *u8); bo = gs_u(body, bo, nmatch); bo = gs_cat(body, bo, ",\"items\":[" as *u8)
1494 let eo0: i64 = gs_eff_offset(req, rn, nmatch, o0)
1495 var k: i64 = 0; var first: i64 = 1
1496 while k < n { let disp: i64 = eo0 + k; if disp >= nmatch { k = n } else { let id: i64 = matched[nmatch-1-disp]; if first==0 { body[bo]=44 as u8; bo=bo+1 } first=0; body[bo]=34 as u8; bo=bo+1; bo = gs_u(body, bo, id); body[bo]=34 as u8; bo=bo+1; k=k+1 } }
1497 bo = gs_cat(body, bo, "]}" as *u8)
1498 return gs_okjson(rbuf, body, bo)
1499}
1500// GET /api/list?filter=recordings -> {"total":N,"kind":"rec","items":[id...]} newest-first (reverse index).
1501func gs_recordings_list(rbuf: *u8, req: *u8, rn: i64, o0: i64, n: i64) -> i64 {
1502 // parse optional &model=<name> (the person/folder) from the request line
1503 let mbuf: *u8 = sys_mmap(256); var mlen: i64 = 0
1504 var li: i64 = 0; var mpos: i64 = 0 - 1; var stop: i64 = rn
1505 while li < stop { if req[li]==(10 as u8) { stop = li } else { if mpos < 0 { if li+6 <= rn { if req[li]==(109 as u8){ if req[li+1]==(111 as u8){ if req[li+2]==(100 as u8){ if req[li+3]==(101 as u8){ if req[li+4]==(108 as u8){ if req[li+5]==(61 as u8){ mpos = li+6 } } } } } } } } li = li + 1 } }
1506 if mpos >= 0 { var p: i64 = mpos; var go: i64 = 1; while go == 1 { if p >= rn { go = 0 } else { let c: i64 = req[p] as i64; if c==38 { go=0 } else { if c==32 { go=0 } else { if c==10 { go=0 } else { if c==13 { go=0 } else { if mlen < 255 { mbuf[mlen]=c as u8; mlen=mlen+1 } p=p+1 } } } } } } }
1507 let szp: *i64 = sys_mmap(16) as *i64
1508 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
1509 let sz: i64 = szp[0]
1510 let body: *u8 = sys_mmap(VIEW_MAGIC_65536)
1511 var bo: i64 = 0
1512 if mlen == 0 {
1513 var total: i64 = 0
1514 if (b as i64) != 0 { var i: i64 = 0; while i < sz { if b[i]==(10 as u8) { total = total + 1 } i = i + 1 } }
1515 bo = gs_cat(body, bo, "{\"kind\":\"rec\",\"total\":" as *u8); bo = gs_u(body, bo, total); bo = gs_cat(body, bo, ",\"items\":[" as *u8)
1516 let eo0: i64 = gs_eff_offset(req, rn, total, o0)
1517 var k: i64 = 0; var first: i64 = 1
1518 while k < n { let disp: i64 = eo0 + k; if disp >= total { k = n } else { let id: i64 = total-1-disp; if first==0 { body[bo]=44 as u8; bo=bo+1 } first=0; body[bo]=34 as u8; bo=bo+1; bo = gs_u(body, bo, id); body[bo]=34 as u8; bo=bo+1; k=k+1 } }
1519 bo = gs_cat(body, bo, "]}" as *u8)
1520 } else {
1521 let matched: *i64 = sys_mmap(8 * VIEW_MAGIC_1000000) as *i64; var nmatch: i64 = 0
1522 if (b as i64) != 0 {
1523 var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
1524 while i <= sz {
1525 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
1526 if nl == 1 { if i - ls > 3 {
1527 var last: i64 = 0-1; var prev: i64 = 0-1; var p: i64 = ls
1528 while p < i { if b[p]==(47 as u8) { prev = last; last = p } p = p + 1 }
1529 if prev >= 0 { if last > prev { let ps: i64 = prev+1; let pl: i64 = last-ps
1530 if pl == mlen { var eq: i64 = 1; var c: i64 = 0; while c < pl { if b[ps+c] != mbuf[c] { eq = 0 } c = c + 1 } if eq == 1 { if nmatch < VIEW_MAGIC_1000000 { matched[nmatch] = lineno; nmatch = nmatch + 1 } } }
1531 } }
1532 lineno = lineno + 1
1533 } ls = i + 1 }
1534 i = i + 1
1535 }
1536 }
1537 bo = gs_cat(body, bo, "{\"kind\":\"rec\",\"total\":" as *u8); bo = gs_u(body, bo, nmatch); bo = gs_cat(body, bo, ",\"items\":[" as *u8)
1538 let eo0: i64 = gs_eff_offset(req, rn, nmatch, o0)
1539 var k: i64 = 0; var first: i64 = 1
1540 while k < n { let disp: i64 = eo0 + k; if disp >= nmatch { k = n } else { let id: i64 = matched[nmatch-1-disp]; if first==0 { body[bo]=44 as u8; bo=bo+1 } first=0; body[bo]=34 as u8; bo=bo+1; bo = gs_u(body, bo, id); body[bo]=34 as u8; bo=bo+1; k=k+1 } }
1541 bo = gs_cat(body, bo, "]}" as *u8)
1542 }
1543 return gs_okjson(rbuf, body, bo)
1544}
1545// GET /api/models -> [{"m":"<person>","c":<count>},...] from galx_models.tsv (sorted by count desc)
1546func gs_models(rbuf: *u8) -> i64 {
1547 let szp: *i64 = sys_mmap(16) as *i64
1548 let b: *u8 = sys_read_file("knowledge/status/galx_models.tsv" as *u8, szp)
1549 let sz: i64 = szp[0]
1550 let body: *u8 = sys_mmap(VIEW_MAGIC_262144); var bo: i64 = 0
1551 body[bo] = 91 as u8; bo = bo + 1
1552 var first: i64 = 1
1553 if (b as i64) != 0 {
1554 var i: i64 = 0; var ls: i64 = 0
1555 while i <= sz {
1556 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
1557 if nl == 1 { if i - ls >= 3 {
1558 var tab: i64 = 0-1; var p: i64 = ls; while p < i { if b[p]==(9 as u8) { if tab < 0 { tab = p } } p = p + 1 }
1559 if tab >= 0 { if bo < VIEW_MAGIC_258000 {
1560 if first == 0 { body[bo]=44 as u8; bo=bo+1 } first = 0
1561 bo = gs_cat(body, bo, "{\"m\":\"" as *u8)
1562 var c: i64 = ls; while c < tab { let ch: i64 = b[c] as i64; if ch==34 { body[bo]=92 as u8; bo=bo+1; body[bo]=34 as u8; bo=bo+1 } else { if ch==92 { body[bo]=92 as u8; bo=bo+1; body[bo]=92 as u8; bo=bo+1 } else { body[bo]=ch as u8; bo=bo+1 } } c = c + 1 }
1563 bo = gs_cat(body, bo, "\",\"c\":" as *u8)
1564 var d: i64 = tab+1; while d < i { body[bo]=b[d]; bo=bo+1; d=d+1 }
1565 body[bo] = 125 as u8; bo = bo + 1
1566 } }
1567 } ls = i + 1 }
1568 i = i + 1
1569 }
1570 }
1571 body[bo] = 93 as u8; bo = bo + 1
1572 return gs_okjson(rbuf, body, bo)
1573}
1574
1575// ---- /meta ----
1576func gs_jesc(dst: *u8, o: i64, ch: i64) -> i64 {
1577 if ch == 34 { dst[o]=92 as u8; dst[o+1]=34 as u8; return o+2 }
1578 if ch == 92 { dst[o]=92 as u8; dst[o+1]=92 as u8; return o+2 }
1579 if ch < 32 { dst[o]=32 as u8; return o+1 }
1580 dst[o]=ch as u8; return o+1
1581}
1582func gs_meta(rbuf: *u8, req: *u8, rn: i64, cidx: *NxCidx) -> i64 {
1583 let cid: *u8 = sys_mmap(96)
1584 let okc: i64 = gs_peel_pat(req, rn, "GET /meta/" as *u8, 10, cid)
1585 let body: *u8 = sys_mmap(VIEW_MAGIC_262144)
1586 var bo: i64 = 0
1587 body[bo] = 123 as u8; bo = bo + 1
1588 var first: i64 = 1
1589 if okc == 1 {
1590 let path: *u8 = sys_mmap(VIEW_MAGIC_2048)
1591 if gs_sidecar(cidx, cid, path) == 1 {
1592 let szp: *i64 = sys_mmap(16) as *i64
1593 let png: *u8 = sys_read_file(path, szp)
1594 let n: i64 = szp[0]
1595 if (png as i64) != 0 { if n > 8 {
1596 var off: i64 = 8
1597 var guard: i64 = 0
1598 while off + 12 <= n {
1599 if guard > VIEW_MAGIC_200000 { off = n } else {
1600 guard = guard + 1
1601 let len: i64 = gs_be32(png, off)
1602 let t0: i64=png[off+4] as i64; let t1: i64=png[off+5] as i64; let t2: i64=png[off+6] as i64; let t3: i64=png[off+7] as i64
1603 let dataoff: i64 = off + 8
1604 if dataoff + len > n { off = n } else { if len < 0 { off = n } else {
1605 let dend: i64 = dataoff + len
1606 var istext: i64=0; if t0==116 { if t1==69 { if t2==88 { if t3==116 { istext=1 } } } }
1607 var isitxt: i64=0; if t0==105 { if t1==84 { if t2==88 { if t3==116 { isitxt=1 } } } }
1608 if istext == 1 {
1609 var kwe: i64 = dend; var p: i64 = dataoff
1610 while p < dend { if png[p]==(0 as u8) { if kwe==dend { kwe=p } } p=p+1 }
1611 if kwe < dend { if bo < VIEW_MAGIC_250000 {
1612 if first==0 { body[bo]=44 as u8; bo=bo+1 } first=0
1613 body[bo]=34 as u8; bo=bo+1
1614 var k: i64=dataoff; while k<kwe { bo=gs_jesc(body,bo,png[k] as i64); k=k+1 }
1615 body[bo]=34 as u8; bo=bo+1; body[bo]=58 as u8; bo=bo+1; body[bo]=34 as u8; bo=bo+1
1616 var v: i64=kwe+1; while v<dend { if bo<VIEW_MAGIC_258000 { bo=gs_jesc(body,bo,png[v] as i64) } v=v+1 }
1617 body[bo]=34 as u8; bo=bo+1
1618 } }
1619 }
1620 if isitxt == 1 {
1621 var kwe2: i64=dend; var p2: i64=dataoff
1622 while p2<dend { if png[p2]==(0 as u8) { if kwe2==dend { kwe2=p2 } } p2=p2+1 }
1623 if kwe2 + 2 < dend {
1624 let cflag: i64=png[kwe2+1] as i64
1625 var lange: i64=dend; var p3: i64=kwe2+3
1626 while p3<dend { if png[p3]==(0 as u8) { if lange==dend { lange=p3 } } p3=p3+1 }
1627 var transe: i64=dend; var p4: i64=lange+1
1628 while p4<dend { if png[p4]==(0 as u8) { if transe==dend { transe=p4 } } p4=p4+1 }
1629 let textoff: i64=transe+1
1630 if cflag==0 { if kwe2<dend { if textoff<=dend { if bo<VIEW_MAGIC_250000 {
1631 if first==0 { body[bo]=44 as u8; bo=bo+1 } first=0
1632 body[bo]=34 as u8; bo=bo+1
1633 var k2: i64=dataoff; while k2<kwe2 { bo=gs_jesc(body,bo,png[k2] as i64); k2=k2+1 }
1634 body[bo]=34 as u8; bo=bo+1; body[bo]=58 as u8; bo=bo+1; body[bo]=34 as u8; bo=bo+1
1635 var v2: i64=textoff; while v2<dend { if bo<VIEW_MAGIC_258000 { bo=gs_jesc(body,bo,png[v2] as i64) } v2=v2+1 }
1636 body[bo]=34 as u8; bo=bo+1
1637 } } } }
1638 }
1639 }
1640 let nextoff: i64 = dataoff + len + 4
1641 if nextoff <= off { off = n } else { off = nextoff }
1642 } }
1643 }
1644 }
1645 }}
1646 }
1647 }
1648 body[bo] = 125 as u8; bo = bo + 1
1649 return gs_okjson(rbuf, body, bo)
1650}
1651
1652// ---- state logs (append-only) ----
1653func gs_append_line(path: *u8, line: *u8, len: i64) -> i64 {
1654 let fd: i64 = sys_openat_append(path, 0x1a4)
1655 if fd < 0 { return 0 - 1 }
1656 sys_write(fd, line, len)
1657 sys_close(fd)
1658 return 0
1659}
1660const TELE_LOG: *u8 = "knowledge/status/galx_telemetry.log" as *u8
1661// POST /telemetry: the client beacons JS errors / perf / long-tasks here; we append the raw JSON line to a sovereign
1662// log the operator/Claude reads -> the closed loop (client issue -> server log -> autonomous fix). One line/event, capped.
1663func gs_telemetry(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1664 var bodyat: i64 = 0 - 1
1665 var i: i64 = 0
1666 while i + 4 <= rn { if req[i]==(13 as u8) { if req[i+1]==(10 as u8) { if req[i+2]==(13 as u8) { if req[i+3]==(10 as u8) { if bodyat<0 { bodyat=i+4 } } } } } i=i+1 }
1667 if bodyat >= 0 {
1668 var blen: i64 = rn - bodyat
1669 if blen > VIEW_MAGIC_1024 { blen = VIEW_MAGIC_1024 }
1670 if blen > 0 {
1671 let line: *u8 = sys_mmap(VIEW_MAGIC_1100)
1672 var lo: i64 = 0
1673 var b: i64 = 0
1674 while b < blen { let ch: i64 = req[bodyat+b] as i64; if ch != 10 { if ch != 13 { line[lo]=ch as u8; lo=lo+1 } } b=b+1 }
1675 line[lo] = 10 as u8; lo = lo + 1
1676 gs_append_line(TELE_LOG, line, lo)
1677 }
1678 }
1679 return gs_cat(rbuf, 0, "HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8)
1680}
1681func gs_same_origin(req: *u8, rn: i64) -> i64 {
1682 let h1: i64 = eh_find(req, rn, "Host: 127.0.0.1:18090" as *u8, 21)
1683 let h2: i64 = eh_find(req, rn, "Host: localhost:18090" as *u8, 21)
1684 if h1 == 0 { if h2 == 0 { return 0 } }
1685 let hasorigin: i64 = eh_find(req, rn, "Origin: " as *u8, 8)
1686 let o1: i64 = eh_find(req, rn, "Origin: http://127.0.0.1:18090" as *u8, 30)
1687 let o2: i64 = eh_find(req, rn, "Origin: http://localhost:18090" as *u8, 30)
1688 if hasorigin == 1 { if o1 == 0 { if o2 == 0 { return 0 } } }
1689 return 1
1690}
1691func gs_forbidden(rbuf: *u8) -> i64 { return gs_cat(rbuf, 0, "HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nContent-Length: 9\r\nConnection: close\r\n\r\nforbidden" as *u8) }
1692func gs_okrate(rbuf: *u8) -> i64 { return gs_cat(rbuf, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok" as *u8) }
1693// POST /view img=<cid> -> append cid to views log
1694func gs_view(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1695 if gs_same_origin(req, rn) == 0 { return gs_forbidden(rbuf) }
1696 let cid: *u8 = sys_mmap(96)
1697 if gs_body_cid(req, rn, cid) == 0 { return gs_okrate(rbuf) }
1698 let line: *u8 = sys_mmap(80)
1699 var c: i64 = 0
1700 while c < 69 { line[c] = cid[c]; c = c + 1 }
1701 line[69] = 10 as u8
1702 gs_append_line(VIEW_LOG, line, 70)
1703 return gs_okrate(rbuf)
1704}
1705// POST /fav img=<cid>&v=<0|1> -> append "cid\tv" to fav log (LoRA-training flag set)
1706func gs_fav(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1707 if gs_same_origin(req, rn) == 0 { return gs_forbidden(rbuf) }
1708 let cid: *u8 = sys_mmap(96)
1709 if gs_body_cid(req, rn, cid) == 0 { return gs_okrate(rbuf) }
1710 var v: i64 = gs_qint(req, rn, "v=" as *u8, 1) // body param (gs_qint scans whole request first line; v= is in body but no newline before it in a single-segment POST) -- fallback handled below
1711 // robust: find v= in the body specifically
1712 var bodyat: i64 = 0 - 1
1713 var bi: i64 = 0
1714 while bi + 4 <= rn { if req[bi]==(13 as u8) { if req[bi+1]==(10 as u8) { if req[bi+2]==(13 as u8) { if req[bi+3]==(10 as u8) { if bodyat<0 { bodyat=bi+4 } } } } } bi=bi+1 }
1715 if bodyat >= 0 {
1716 var p: i64 = bodyat
1717 var vat: i64 = 0 - 1
1718 while p + 2 <= rn { if req[p]==(118 as u8) { if req[p+1]==(61 as u8) { if vat<0 { vat=p+2 } } } p=p+1 }
1719 if vat >= 0 { if req[vat]==(48 as u8) { v = 0 } else { v = 1 } }
1720 }
1721 let line: *u8 = sys_mmap(80)
1722 var c: i64 = 0
1723 while c < 69 { line[c] = cid[c]; c = c + 1 }
1724 line[69] = 9 as u8
1725 if v == 0 { line[70] = 48 as u8 } else { line[70] = 49 as u8 }
1726 line[71] = 10 as u8
1727 gs_append_line(FAV_LOG, line, 72)
1728 return gs_okrate(rbuf)
1729}
1730// POST /hide id=<lineno>&v=<0|1> -> append "<id> <v>" to galx_hidden.log. R3 reversible soft-hide: the
1731// sort-index build excludes current-hidden ids (rule 13: NEVER deletes the file, fully restorable via v=0).
1732func gs_hide(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1733 if gs_same_origin(req, rn) == 0 { return gs_forbidden(rbuf) }
1734 var bodyat: i64 = 0 - 1; var bi: i64 = 0
1735 while bi + 4 <= rn { if req[bi]==(13 as u8) { if req[bi+1]==(10 as u8) { if req[bi+2]==(13 as u8) { if req[bi+3]==(10 as u8) { if bodyat<0 { bodyat=bi+4 } } } } } bi=bi+1 }
1736 if bodyat < 0 { bodyat = 0 }
1737 let bp: *u8 = ((req as i64) + bodyat) as *u8; let bl: i64 = rn - bodyat
1738 let id: i64 = gs_qint(bp, bl, "id=" as *u8, 0 - 1)
1739 if id < 0 { return gs_okrate(rbuf) }
1740 var v: i64 = gs_qint(bp, bl, "v=" as *u8, 1); if v != 0 { v = 1 }
1741 let line: *u8 = sys_mmap(48); var o: i64 = 0
1742 o = gs_u(line, o, id); line[o] = 32 as u8; o = o + 1
1743 if v == 0 { line[o] = 48 as u8 } else { line[o] = 49 as u8 } o = o + 1
1744 line[o] = 10 as u8; o = o + 1
1745 gs_append_line(HIDDEN_LOG, line, o)
1746 return gs_okrate(rbuf)
1747}
1748// GET /api/hidden -> {"hidden":[id,...]} the linenos currently hidden (last v=1). Sorted views already exclude
1749// them server-side; the client uses this to skip them in the non-sorted (newest/oldest, &model=) views too.
1750func gs_hidden_list(rbuf: *u8) -> i64 {
1751 let bm: *u8 = sys_mmap(VIEW_MAGIC_262144); ss_load_hidden(bm)
1752 let body: *u8 = sys_mmap(VIEW_MAGIC_1048576); var bo: i64 = 0
1753 bo = gs_cat(body, bo, "{\"hidden\":[" as *u8)
1754 var first: i64 = 1; var byte: i64 = 0
1755 while byte < VIEW_MAGIC_262144 {
1756 if bm[byte] != (0 as u8) {
1757 var bit: i64 = 0
1758 while bit < 8 {
1759 if (bm[byte] as i64 & (1 << bit)) != 0 {
1760 if first == 0 { body[bo] = 44 as u8; bo = bo + 1 } first = 0
1761 bo = gs_u(body, bo, byte * 8 + bit)
1762 }
1763 bit = bit + 1
1764 }
1765 }
1766 byte = byte + 1
1767 }
1768 bo = gs_cat(body, bo, "]}" as *u8)
1769 return gs_okjson(rbuf, body, bo)
1770}
1771// GET /api/durs?ids=1,2,3 -> {"<id>":dur_ms,...} runtime badges for a grid page in ONE request (O(1) per id
1772// from galx_dur.bin). Omits unknown/zero/stale ids. Composes db_batch_durs (gated in nx_galx_durbin_lib).
1773func gs_durs_batch(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1774 let ids: *u8 = sys_mmap(VIEW_MAGIC_8192); var o: i64 = 0
1775 var p: i64 = 0 - 1; var i: i64 = 0
1776 while i + 4 <= rn {
1777 if req[i]==(105 as u8) { if req[i+1]==(100 as u8) { if req[i+2]==(115 as u8) { if req[i+3]==(61 as u8) { p = i + 4; i = rn } } } }
1778 i = i + 1
1779 }
1780 if p >= 0 {
1781 var j: i64 = p
1782 while j < rn { let c: i64 = req[j] as i64
1783 if c == 32 { j = rn } else { if c == 38 { j = rn } else { if c == 13 { j = rn } else { if c == 10 { j = rn } else { ids[o]=req[j]; o=o+1; j=j+1 } } } } }
1784 }
1785 ids[o] = 0 as u8
1786 let body: *u8 = sys_mmap(VIEW_MAGIC_1048576)
1787 let bo: i64 = db_batch_durs(body, ids, "knowledge/status/galx_dur.bin" as *u8, gs_vidpaths_size())
1788 return gs_okjson(rbuf, body, bo)
1789}
1790// extract + sanitise a "tag=" value from buf[0..bl): keep alnum/dash, '+' -> space, drop the rest; max 32.
1791// (keeps tabs/newlines/quotes OUT of the log + JSON so the store can't be corrupted or injected.)
1792func gs_extract_tag(bp: *u8, bl: i64, out: *u8) -> i64 {
1793 var p: i64 = 0 - 1; var i: i64 = 0
1794 while i + 4 <= bl { if bp[i]==(116 as u8) { if bp[i+1]==(97 as u8) { if bp[i+2]==(103 as u8) { if bp[i+3]==(61 as u8) { p=i+4; i=bl } } } } i=i+1 }
1795 if p < 0 { return 0 }
1796 var o: i64 = 0; var j: i64 = p
1797 while j < bl {
1798 let c: i64 = bp[j] as i64
1799 var stop: i64 = 0
1800 if c == 38 { stop = 1 } if c == 32 { stop = 1 } if c == 13 { stop = 1 } if c == 10 { stop = 1 }
1801 if stop == 1 { j = bl } else {
1802 var ch: i64 = c; var keep: i64 = 0
1803 if c == 43 { ch = 32; keep = 1 }
1804 if c >= 48 { if c <= 57 { keep = 1 } }
1805 if c >= 65 { if c <= 90 { keep = 1 } }
1806 if c >= 97 { if c <= 122 { keep = 1 } }
1807 if c == 45 { keep = 1 }
1808 if keep == 1 { if o < 32 { out[o] = ch as u8; o = o + 1 } }
1809 j = j + 1
1810 }
1811 }
1812 return o
1813}
1814// extract the "id=" value from buf[0..bl) into out (up to &/space/CR/LF; tab stripped; max 80). Works for an
1815// int video lineno OR a 69-char image cid -- the unified string id for the string-keyed tag store. returns len.
1816func gs_extract_id(bp: *u8, bl: i64, out: *u8) -> i64 {
1817 var p: i64 = 0 - 1; var i: i64 = 0
1818 while i + 3 <= bl { if bp[i]==(105 as u8) { if bp[i+1]==(100 as u8) { if bp[i+2]==(61 as u8) { p=i+3; i=bl } } } i=i+1 }
1819 if p < 0 { return 0 }
1820 var o: i64 = 0; var j: i64 = p
1821 while j < bl {
1822 let c: i64 = bp[j] as i64
1823 var stop: i64 = 0
1824 if c == 38 { stop = 1 } if c == 32 { stop = 1 } if c == 13 { stop = 1 } if c == 10 { stop = 1 }
1825 if stop == 1 { j = bl } else {
1826 if c != 9 { if o < 80 { out[o] = bp[j] as u8; o = o + 1 } }
1827 j = j + 1
1828 }
1829 }
1830 return o
1831}
1832// POST /tag id=<videoLineno|imageCid>&tag=<name>&v=<0|1> -> append "<id>\t<tag>\t<v>" (string-keyed; rule-13).
1833func gs_tag(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1834 if gs_same_origin(req, rn) == 0 { return gs_forbidden(rbuf) }
1835 var bodyat: i64 = 0 - 1; var bi: i64 = 0
1836 while bi + 4 <= rn { if req[bi]==(13 as u8) { if req[bi+1]==(10 as u8) { if req[bi+2]==(13 as u8) { if req[bi+3]==(10 as u8) { if bodyat<0 { bodyat=bi+4 } } } } } bi=bi+1 }
1837 if bodyat < 0 { bodyat = 0 }
1838 let bp: *u8 = ((req as i64) + bodyat) as *u8; let bl: i64 = rn - bodyat
1839 let idp: *u8 = sys_mmap(96); let idl: i64 = gs_extract_id(bp, bl, idp)
1840 if idl <= 0 { return gs_okrate(rbuf) }
1841 var v: i64 = gs_qint(bp, bl, "v=" as *u8, 1); if v != 0 { v = 1 }
1842 let tag: *u8 = sys_mmap(64); let tl: i64 = gs_extract_tag(bp, bl, tag)
1843 if tl <= 0 { return gs_okrate(rbuf) }
1844 let line: *u8 = sys_mmap(256); let o: i64 = tg_op_line_s(line, idp, idl, tag, tl, v)
1845 gs_append_line(TAGS_LOG, line, o)
1846 return gs_okrate(rbuf)
1847}
1848// GET /api/tags?id=<lineno> -> ["tag",...] the video's current tags.
1849func gs_tags_get(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1850 let idp: *u8 = sys_mmap(96); let idl: i64 = gs_extract_id(req, rn, idp)
1851 let body: *u8 = sys_mmap(VIEW_MAGIC_8192); var bo: i64 = 0
1852 if idl <= 0 { bo = gs_cat(body, 0, "[]" as *u8) } else {
1853 let szp: *i64 = sys_mmap(16) as *i64; let log: *u8 = sys_read_file(TAGS_LOG, szp)
1854 if (log as i64) == 0 { bo = gs_cat(body, 0, "[]" as *u8) } else { bo = tg_tags_for_id_s(log, szp[0], idp, idl, body) }
1855 }
1856 return gs_okjson(rbuf, body, bo)
1857}
1858// GET /api/tagged?tag=<name> -> [id,...] videos currently carrying the tag (the tag FILTER).
1859func gs_tagged_get(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1860 let tag: *u8 = sys_mmap(64); let tl: i64 = gs_extract_tag(req, rn, tag)
1861 let body: *u8 = sys_mmap(VIEW_MAGIC_1048576); var bo: i64 = 0
1862 if tl <= 0 { bo = gs_cat(body, 0, "[]" as *u8) } else {
1863 let szp: *i64 = sys_mmap(16) as *i64; let log: *u8 = sys_read_file(TAGS_LOG, szp)
1864 if (log as i64) == 0 { bo = gs_cat(body, 0, "[]" as *u8) } else { bo = tg_ids_for_tag_s(log, szp[0], tag, tl, body) }
1865 }
1866 return gs_okjson(rbuf, body, bo)
1867}
1868// GET /api/alltags -> ["tag",...] all distinct tag names ever used (the picker / tag-cloud).
1869func gs_alltags_get(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1870 let body: *u8 = sys_mmap(VIEW_MAGIC_65536); var bo: i64 = 0
1871 let szp: *i64 = sys_mmap(16) as *i64; let log: *u8 = sys_read_file(TAGS_LOG, szp)
1872 if (log as i64) == 0 { bo = gs_cat(body, 0, "[]" as *u8) } else { bo = tg_all_tags_s(log, szp[0], body) }
1873 return gs_okjson(rbuf, body, bo)
1874}
1875// scan a cid<TAB>v log for the LAST v of cid; returns -1 if absent.
1876func gs_last_v(path: *u8, cid: *u8) -> i64 {
1877 let szp: *i64 = sys_mmap(16) as *i64
1878 let b: *u8 = sys_read_file(path, szp)
1879 let sz: i64 = szp[0]
1880 if (b as i64) == 0 { return 0 - 1 }
1881 var i: i64 = 0; var ls: i64 = 0; var last: i64 = 0 - 1
1882 while i <= sz {
1883 var nl: i64 = 0
1884 if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
1885 if nl == 1 {
1886 if i - ls >= 69 { var eq: i64=1; var k: i64=0; while k<69 { if b[ls+k]!=cid[k]{eq=0} k=k+1 }
1887 if eq == 1 { if b[ls+70]==(48 as u8) { last = 0 } else { last = 1 } } }
1888 ls = i + 1
1889 }
1890 i = i + 1
1891 }
1892 return last
1893}
1894// is cid present anywhere as a line-start in a plain-cid log?
1895func gs_present(path: *u8, cid: *u8) -> i64 {
1896 let szp: *i64 = sys_mmap(16) as *i64
1897 let b: *u8 = sys_read_file(path, szp)
1898 let sz: i64 = szp[0]
1899 if (b as i64) == 0 { return 0 }
1900 var i: i64 = 0; var ls: i64 = 0
1901 while i <= sz {
1902 var nl: i64 = 0
1903 if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
1904 if nl == 1 { if i-ls >= 69 { var eq: i64=1; var k: i64=0; while k<69 { if b[ls+k]!=cid[k]{eq=0} k=k+1 } if eq==1 { return 1 } } ls=i+1 }
1905 i = i + 1
1906 }
1907 return 0
1908}
1909// last rating score for cid from eval log (EVAL img=<cid> lane=O score=<N>); -1 if none.
1910func gs_last_score(cid: *u8) -> i64 {
1911 let szp: *i64 = sys_mmap(16) as *i64
1912 let b: *u8 = sys_read_file(EVAL_LOG, szp)
1913 let sz: i64 = szp[0]
1914 if (b as i64) == 0 { return 0 - 1 }
1915 var i: i64 = 0; var last: i64 = 0 - 1
1916 while i + 4 < sz {
1917 if b[i]==(105 as u8) { if b[i+1]==(109 as u8) { if b[i+2]==(103 as u8) { if b[i+3]==(61 as u8) {
1918 let cat: i64 = i + 4
1919 var eq: i64 = 1; var k: i64 = 0
1920 while k < 69 { if cat+k >= sz { eq=0; k=69 } else { if b[cat+k]!=cid[k] { eq=0; k=69 } else { k=k+1 } } }
1921 if eq == 1 {
1922 var sp: i64 = cat + 69
1923 var sat: i64 = 0 - 1
1924 while sp + 6 <= sz { if b[sp]==(115 as u8) { if b[sp+1]==(99 as u8) { if b[sp+2]==(111 as u8) { if b[sp+3]==(114 as u8) { if b[sp+4]==(101 as u8) { if b[sp+5]==(61 as u8) { sat=sp+6; sp=sz } } } } } } if b[sp]==(10 as u8) { sp=sz } else { sp=sp+1 } }
1925 if sat >= 0 { var v: i64=0; var q: i64=sat; while q<sz { if b[q]>=(48 as u8) { if b[q]<=(57 as u8) { v=v*10+((b[q] as i64)-48); q=q+1 } else { q=sz } } else { q=sz } } last=v }
1926 }
1927 } } } }
1928 i = i + 1
1929 }
1930 return last
1931}
1932func gs_state(rbuf: *u8, req: *u8, rn: i64) -> i64 {
1933 let cid: *u8 = sys_mmap(96)
1934 if gs_peel_pat(req, rn, "GET /state/" as *u8, 11, cid) == 0 { return gs_404(rbuf) }
1935 let fav: i64 = gs_last_v(FAV_LOG, cid)
1936 let viewed: i64 = gs_present(VIEW_LOG, cid)
1937 let score: i64 = gs_last_score(cid)
1938 let body: *u8 = sys_mmap(128)
1939 var bo: i64 = 0
1940 bo = gs_cat(body, bo, "{\"fav\":" as *u8); if fav == 1 { bo = gs_cat(body, bo, "1" as *u8) } else { bo = gs_cat(body, bo, "0" as *u8) }
1941 bo = gs_cat(body, bo, ",\"viewed\":" as *u8); bo = gs_u(body, bo, viewed)
1942 bo = gs_cat(body, bo, ",\"rating\":" as *u8); if score < 0 { bo = gs_cat(body, bo, "-1" as *u8) } else { bo = gs_u(body, bo, score) }
1943 bo = gs_cat(body, bo, "}" as *u8)
1944 return gs_okjson(rbuf, body, bo)
1945}
1946
1947// ===== BM25 RANKED q= SEARCH over the durable gallery index (replaces the 465MB/query substring scan) =====
1948// Loaded ONCE in the parent before the accept loop; every request-child inherits it via fork copy-on-write,
1949// so per-request cost is just an index shortlist + BM25 over the matched candidates -- no re-read, no 465MB scan.
1950struct NxGalIdx {
1951 idx: *NxInvIndex
1952 up: *i64 // per-doc url pointer (cid = url+5, the 69 bytes after "/img/")
1953 xp: *i64 // per-doc text pointer (the searchable prompt)
1954 xl: *i64 // per-doc text length
1955 ndocs: i64
1956 valid: i64
1957}
1958
1959// count tokens (len>=2) in text[0..n) -- the doc length BM25 normalizes by
1960func gs_bm_doclen(text: *u8, n: i64) -> i64 {
1961 var cnt: i64 = 0; var i: i64 = 0
1962 while i < n {
1963 if nx_inv_is_token_char(text[i] as i64) == 0 { i = i + 1 } else {
1964 let s: i64 = i; var go: i64 = 1
1965 while go == 1 { if i >= n { go = 0 } else { if nx_inv_is_token_char(text[i] as i64) == 1 { i = i + 1 } else { go = 0 } } }
1966 if i - s >= 2 { cnt = cnt + 1 }
1967 }
1968 }
1969 return cnt
1970}
1971// term-frequency of token-hash `th` in text (FNV-lowercased tokenization -- matches the index, case-insensitive)
1972func gs_bm_tf(text: *u8, n: i64, th: i64) -> i64 {
1973 var cnt: i64 = 0; var i: i64 = 0
1974 while i < n {
1975 if nx_inv_is_token_char(text[i] as i64) == 0 { i = i + 1 } else {
1976 let s: i64 = i; var go: i64 = 1
1977 while go == 1 { if i >= n { go = 0 } else { if nx_inv_is_token_char(text[i] as i64) == 1 { i = i + 1 } else { go = 0 } } }
1978 let len: i64 = i - s
1979 if len >= 2 { if nx_inv_hash_bytes_lower(((text as i64) + s) as *u8, len) == th { cnt = cnt + 1 } }
1980 }
1981 }
1982 return cnt
1983}
1984// load idx + parse the manifest (url<TAB>title<TAB>text) into per-doc slice arrays. 1=ok (gal.valid=1), 0=fall back to substring.
1985func gs_galidx_load(gal: *NxGalIdx, idx_path: *u8, manifest_path: *u8) -> i64 {
1986 gal.valid = 0
1987 let idx: *NxInvIndex = nx_inv_load(idx_path)
1988 if idx == 0 as *NxInvIndex { return 0 }
1989 let mbox: *i64 = sys_mmap(16) as *i64
1990 let mbuf: *u8 = sys_read_file(manifest_path, mbox)
1991 if mbuf == 0 as *u8 { return 0 }
1992 let mn: i64 = mbox[0]
1993 var nl: i64 = 0; var i: i64 = 0
1994 while i < mn { if mbuf[i] == (10 as u8) { nl = nl + 1 } i = i + 1 }
1995 if nl <= 0 { return 0 }
1996 let up: *i64 = sys_mmap(8 * (nl + 8)) as *i64
1997 let xp: *i64 = sys_mmap(8 * (nl + 8)) as *i64
1998 let xl: *i64 = sys_mmap(8 * (nl + 8)) as *i64
1999 var d: i64 = 0; var ls: i64 = 0; i = 0
2000 while i < mn {
2001 if mbuf[i] == (10 as u8) {
2002 let le: i64 = i
2003 var t1: i64 = 0 - 1; var t2: i64 = 0 - 1; var k: i64 = ls
2004 while k < le { if mbuf[k] == (9 as u8) { if t1 < 0 { t1 = k } else { if t2 < 0 { t2 = k } } } k = k + 1 }
2005 up[d] = (mbuf as i64) + ls
2006 if t2 > t1 { if t1 >= ls { xp[d] = (mbuf as i64) + t2 + 1; xl[d] = le - t2 - 1 } else { xp[d] = (mbuf as i64) + ls; xl[d] = 0 } }
2007 if t2 <= t1 { xp[d] = (mbuf as i64) + ls; xl[d] = 0 }
2008 d = d + 1; ls = i + 1
2009 }
2010 i = i + 1
2011 }
2012 if idx.n_rows != d { return 0 } // stale idx/manifest pair -> substring fallback
2013 gal.idx = idx; gal.up = up; gal.xp = xp; gal.xl = xl; gal.ndocs = d; gal.valid = 1
2014 return 1
2015}
2016// BM25-ranked q= search -> {"items":[cid...]} for page [o0, o0+ncount). Multi-word: union shortlist, corpus-IDF.
2017func gs_bm25_emit(rbuf: *u8, gal: *NxGalIdx, qbuf: *u8, qlen: i64, o0: i64, ncount: i64) -> i64 {
2018 let idx: *NxInvIndex = gal.idx
2019 let ndocs: i64 = gal.ndocs
2020 let body: *u8 = sys_mmap(VIEW_MAGIC_262144)
2021 var bo: i64 = gs_cat(body, 0, "{\"items\":[" as *u8)
2022 let seen: *u8 = sys_mmap(ndocs + 8)
2023 let res: *NxInvQueryResult = sys_mmap(64) as *NxInvQueryResult
2024 let rowids: *i64 = sys_mmap(8 * VIEW_MAGIC_262144) as *i64 // was VIEW_MAGIC_16384 -> a common term in a 200k library overflowed it and truncated matches (a "not full results" cause)
2025 let qh: *i64 = sys_mmap(8 * 16) as *i64 // term hashes
2026 let qdf: *i64 = sys_mmap(8 * 16) as *i64 // corpus document-frequency per term
2027 var nq: i64 = 0; var i: i64 = 0
2028 while i < qlen {
2029 if nx_inv_is_token_char(qbuf[i] as i64) == 0 { i = i + 1 } else {
2030 let s: i64 = i; var go: i64 = 1
2031 while go == 1 { if i >= qlen { go = 0 } else { if nx_inv_is_token_char(qbuf[i] as i64) == 1 { i = i + 1 } else { go = 0 } } }
2032 let tl: i64 = i - s
2033 if tl >= 2 { if nq < 16 {
2034 nx_inv_query_term(idx, ((qbuf as i64) + s) as *u8, tl, rowids, VIEW_MAGIC_262144, res)
2035 if res.verdict == NX_INV_OK {
2036 qh[nq] = nx_inv_hash_bytes_lower(((qbuf as i64) + s) as *u8, tl)
2037 qdf[nq] = res.postings_count
2038 nq = nq + 1
2039 var r: i64 = 0
2040 while r < res.n_rowids_filled { let rid: i64 = rowids[r]; if rid >= 0 { if rid < ndocs { seen[rid] = 1 as u8 } } r = r + 1 }
2041 }
2042 } }
2043 }
2044 }
2045 let CAP: i64 = VIEW_MAGIC_60000
2046 let cand: *i64 = sys_mmap(8 * (CAP + 8)) as *i64
2047 let cdl: *i64 = sys_mmap(8 * (CAP + 8)) as *i64
2048 var ncand: i64 = 0; var total_dl: i64 = 0; var d: i64 = 0
2049 while d < ndocs {
2050 if seen[d] == (1 as u8) { if ncand < CAP {
2051 cand[ncand] = d; let dl: i64 = gs_bm_doclen(gal.xp[d] as *u8, gal.xl[d]); cdl[ncand] = dl; total_dl = total_dl + dl; ncand = ncand + 1
2052 } }
2053 d = d + 1
2054 }
2055 if ncand == 0 { bo = gs_cat(body, bo, "]}" as *u8); return gs_okjson(rbuf, body, bo) }
2056 var avgdl: i64 = total_dl / ncand; if avgdl <= 0 { avgdl = 1 }
2057 let score: *i64 = sys_mmap(8 * (ncand + 8)) as *i64
2058 var c: i64 = 0
2059 while c < ncand {
2060 let dd: i64 = cand[c]; var sc: i64 = 0; var t: i64 = 0
2061 while t < nq {
2062 let tf: i64 = gs_bm_tf(gal.xp[dd] as *u8, gal.xl[dd], qh[t])
2063 if tf > 0 { let idf: i64 = bm_idf_micro(ndocs, qdf[t]); let sat: i64 = bm_sat_milli(tf, cdl[c], avgdl); sc = sc + (idf * sat) / 1000 }
2064 t = t + 1
2065 }
2066 score[c] = sc; c = c + 1
2067 }
2068 // partial selection sort by descending score. Sort a DEDUP MARGIN beyond the page (ncount*2+64) so
2069 // that after dropping duplicate-cid docs the page still fills with UNIQUE results -- the root of the
2070 // "search returns repeat results AND not full results" bug (the index can hold >1 doc per cid).
2071 let want: i64 = o0 + ncount * 2 + 64
2072 var a: i64 = 0
2073 while a < ncand {
2074 if a >= want { a = ncand } else {
2075 var best: i64 = a; var b: i64 = a + 1
2076 while b < ncand { if score[b] > score[best] { best = b } b = b + 1 }
2077 if best != a { let ts: i64 = score[a]; score[a] = score[best]; score[best] = ts; let tc: i64 = cand[a]; cand[a] = cand[best]; cand[best] = tc }
2078 a = a + 1
2079 }
2080 }
2081 // emit page [o0, o0+ncount) of UNIQUE cids: walk score-sorted cands, dedup by 69-byte cid, emit window.
2082 let uptr: *i64 = sys_mmap(8 * (o0 + ncount + 16)) as *i64 // cid pointers of uniques seen so far
2083 var uniq: i64 = 0; var first: i64 = 1; var e: i64 = 0
2084 while e < want {
2085 if uniq >= o0 + ncount { e = want } else { if e >= ncand { e = want } else {
2086 let cptr: *u8 = (gal.up[cand[e]] + 5) as *u8
2087 var dup: i64 = 0; var u: i64 = 0
2088 while u < uniq { if gs_cideq2(cptr, 0, (uptr[u]) as *u8, 0) == 1 { dup = 1; u = uniq } else { u = u + 1 } }
2089 if dup == 0 {
2090 if uniq >= o0 { bo = gs_emit_cid(body, bo, first, cptr, 0); first = 0 }
2091 uptr[uniq] = cptr as i64; uniq = uniq + 1
2092 }
2093 e = e + 1
2094 } }
2095 }
2096 bo = gs_cat(body, bo, "]}" as *u8)
2097 return gs_okjson(rbuf, body, bo)
2098}
2099
2100// ---- /api/list ----
2101func gs_emit_cid(body: *u8, o0: i64, first: i64, buf: *u8, pos: i64) -> i64 {
2102 var o: i64 = o0
2103 if first == 0 { body[o]=44 as u8; o=o+1 }
2104 body[o]=34 as u8; o=o+1
2105 var c: i64 = 0
2106 while c < 69 { body[o]=buf[pos+c]; o=o+1; c=c+1 }
2107 body[o]=34 as u8; o=o+1
2108 return o
2109}
2110// compare 69-byte cids at two buffer offsets
2111func gs_cideq2(a: *u8, ao: i64, b: *u8, bo: i64) -> i64 { var k: i64=0; while k<69 { if a[ao+k]!=b[bo+k] { return 0 } k=k+1 } return 1 }
2112// FILTER list from a state log. kind: 0=plain cid lines(views) ; 1=cid TAB v (fav, keep v==1) ; 2=EVAL img=cid (rated).
2113// Emits matching cids most-recent-first, de-duped, paginated [o,o+n).
2114func gs_filter_list(rbuf: *u8, logpath: *u8, kind: i64, o0: i64, n: i64) -> i64 {
2115 let szp: *i64 = sys_mmap(16) as *i64
2116 let b: *u8 = sys_read_file(logpath, szp)
2117 let sz: i64 = szp[0]
2118 let body: *u8 = sys_mmap(VIEW_MAGIC_131072)
2119 var bo: i64 = 0
2120 bo = gs_cat(body, bo, "{\"items\":[" as *u8)
2121 if (b as i64) != 0 {
2122 let CAP: i64 = VIEW_MAGIC_100000
2123 let coff: *i64 = sys_mmap(8 * CAP) as *i64 // offset of each occurrence's cid
2124 let cv: *i64 = sys_mmap(8 * CAP) as *i64 // v (kind1) else 1
2125 var m: i64 = 0
2126 var i: i64 = 0; var ls: i64 = 0
2127 while i <= sz {
2128 var nl: i64 = 0
2129 if i == sz { nl = 1 } else { if b[i]==(10 as u8) { nl = 1 } }
2130 if nl == 1 {
2131 let llen: i64 = i - ls
2132 if kind == 2 {
2133 // find img= in line
2134 var p: i64 = ls
2135 var cat: i64 = 0 - 1
2136 while p + 4 <= i { if b[p]==(105 as u8) { if b[p+1]==(109 as u8) { if b[p+2]==(103 as u8) { if b[p+3]==(61 as u8) { cat=p+4; p=i } } } } p=p+1 }
2137 if cat >= 0 { if cat + 69 <= i { if m < CAP { coff[m]=cat; cv[m]=1; m=m+1 } } }
2138 } else {
2139 if llen >= 69 { if m < CAP { coff[m]=ls; if kind==1 { if b[ls+70]==(48 as u8) { cv[m]=0 } else { cv[m]=1 } } else { cv[m]=1 } m=m+1 } }
2140 }
2141 ls = i + 1
2142 }
2143 i = i + 1
2144 }
2145 // walk backward (recent first), dedup, collect results (offsets) into res[]
2146 let res: *i64 = sys_mmap(8 * CAP) as *i64
2147 var rn2: i64 = 0
2148 var x: i64 = m - 1
2149 while x >= 0 {
2150 let off: i64 = coff[x]
2151 var seen: i64 = 0
2152 var y: i64 = 0
2153 while y < rn2 { if gs_cideq2(b, res[y], b, off) == 1 { seen = 1; y = rn2 } else { y = y + 1 } }
2154 // also must dedup against earlier-excluded (v==0) cids; track those too
2155 if seen == 0 {
2156 // first (most recent) occurrence of this cid: decide include by its v
2157 if cv[x] == 1 { res[rn2] = off; rn2 = rn2 + 1 }
2158 else { res[rn2] = off; rn2 = rn2 + 1 } // record to mark seen; but excluded from emit below via a parallel flag
2159 }
2160 x = x - 1
2161 }
2162 // res[] now holds first-seen (recent-first) cids INCLUDING v==0; re-walk to emit only those whose first-seen v==1.
2163 // Simpler correct pass: recompute include set honoring most-recent v.
2164 // (res already recent-first & deduped.) Determine each res cid's most-recent v by scanning coff/cv recent-first.
2165 var emitted: i64 = 0
2166 var first: i64 = 1
2167 var skipped: i64 = 0
2168 var r: i64 = 0
2169 while r < rn2 {
2170 let off: i64 = res[r]
2171 // most-recent v for this cid:
2172 var vv: i64 = 1
2173 var z: i64 = m - 1
2174 var found: i64 = 0
2175 while z >= 0 { if found == 0 { if gs_cideq2(b, coff[z], b, off) == 1 { vv = cv[z]; found = 1; z = 0 - 1 } else { z = z - 1 } } else { z = 0 - 1 } }
2176 if vv == 1 {
2177 if skipped >= o0 { if emitted < n { bo = gs_emit_cid(body, bo, first, b, off); first = 0; emitted = emitted + 1 } }
2178 skipped = skipped + 1
2179 }
2180 r = r + 1
2181 }
2182 }
2183 bo = gs_cat(body, bo, "]}" as *u8)
2184 return gs_okjson(rbuf, body, bo)
2185}
2186// GET /api/suggest?q=<prefix> -> {"suggestions":["term",...]} (ADDITIVE; existing search untouched). As-you-type
2187// autocomplete over the sorted gallery vocab strings (knowledge/index/gallery.vocab, built by nx_vocab_extract --
2188// the hash-index has token HASHES not strings, so this string vocab is the missing piece). Binary-search lower
2189// bound = O(log V + k). Per-request load of the 1.6MB sorted vocab (load-once-in-parent is the noted optimization).
2190func gs_api_suggest(rbuf: *u8, req: *u8, rn: i64) -> i64 {
2191 var li: i64 = 0; var qpos: i64 = 0 - 1; var stop: i64 = rn
2192 while li < stop { if req[li]==(10 as u8) { stop=li } else { if qpos<0 { if li+2<=rn { if req[li]==(113 as u8) { if req[li+1]==(61 as u8) { qpos=li+2 } } } } li=li+1 } }
2193 let qbuf: *u8 = sys_mmap(64)
2194 var qlen: i64 = 0
2195 if qpos >= 0 { var p: i64=qpos; var go: i64=1; while go==1 { if p>=rn { go=0 } else { let ch: i64=req[p] as i64; if ch==38 { go=0 } else { if ch==32 { go=0 } else { if ch==10 { go=0 } else { if ch==13 { go=0 } else { if qlen<48 { qbuf[qlen]=gs_lower(ch) as u8; qlen=qlen+1 } p=p+1 } } } } } } }
2196 qbuf[qlen] = 0 as u8
2197 let body: *u8 = sys_mmap(VIEW_MAGIC_8192)
2198 var bo: i64 = gs_cat(body, 0, "{\"suggestions\":[" as *u8)
2199 if qlen >= 1 {
2200 let vbox: *i64 = sys_mmap(16) as *i64
2201 let vbuf: *u8 = sys_read_file("knowledge/index/gallery.vocab" as *u8, vbox)
2202 if (vbuf as i64) != 0 {
2203 let vn: i64 = vbox[0]
2204 let ptrs: *i64 = sys_mmap(8 * VIEW_MAGIC_200008) as *i64
2205 var nl: i64 = 0; var ls: i64 = 0; var i: i64 = 0
2206 while i < vn { if vbuf[i]==(10 as u8) { vbuf[i]=0 as u8; if i>ls { if nl<VIEW_MAGIC_200000 { ptrs[nl]=(vbuf as i64)+ls; nl=nl+1 } } ls=i+1 } i=i+1 }
2207 let out: *i64 = sys_mmap(8 * 64) as *i64
2208 let m: i64 = vr_prefix_collect(ptrs, nl, qbuf, qlen, out)
2209 var k: i64 = 0; var first: i64 = 1
2210 while k < m { if k < 10 {
2211 if first==0 { body[bo]=44 as u8; bo=bo+1 }
2212 body[bo]=34 as u8; bo=bo+1
2213 let t: *u8 = ptrs[out[k]] as *u8
2214 var c: i64 = 0; while t[c]!=(0 as u8) { body[bo]=t[c]; bo=bo+1; c=c+1 }
2215 body[bo]=34 as u8; bo=bo+1
2216 first=0
2217 } k=k+1 }
2218 }
2219 }
2220 bo = gs_cat(body, bo, "]}" as *u8)
2221 return gs_okjson(rbuf, body, bo)
2222}
2223
2224// GET /api/list?filter=gens -> ONLY LLM-generated images (this gallery's historical default view).
2225// Reads the gens cid index galx_view_index.txt (70-byte records: 69-byte cid + newline), newest-first.
2226func gs_gens_list(rbuf: *u8, req: *u8, rn: i64, isold: i64, o0: i64, n: i64) -> i64 {
2227 let fd: i64 = sys_openat_rd(VIEW_IDX)
2228 if fd < 0 { return gs_cat(rbuf, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: 22\r\n\r\n{\"total\":0,\"items\":[]}" as *u8) }
2229 let endpos: i64 = sys_lseek(fd, 0, 2)
2230 let total: i64 = endpos / 70
2231 let eo0: i64 = gs_eff_offset(req, rn, total, o0)
2232 let body: *u8 = sys_mmap(VIEW_MAGIC_131072)
2233 var bo: i64 = 0
2234 bo = gs_cat(body, bo, "{\"total\":" as *u8); bo = gs_u(body, bo, total); bo = gs_cat(body, bo, ",\"items\":[" as *u8)
2235 let line: *u8 = sys_mmap(80)
2236 var k: i64 = 0; var first: i64 = 1
2237 while k < n {
2238 let disp: i64 = eo0 + k
2239 if disp >= total { k = n } else {
2240 var fl: i64 = disp
2241 if isold == 1 { fl = total - 1 - disp }
2242 sys_lseek(fd, fl * 70, 0)
2243 let got: i64 = sys_read(fd, line, 69)
2244 if got == 69 { bo = gs_emit_cid(body, bo, first, line, 0); first = 0 }
2245 k = k + 1
2246 }
2247 }
2248 sys_close(fd)
2249 bo = gs_cat(body, bo, "]}" as *u8)
2250 return gs_okjson(rbuf, body, bo)
2251}
2252// GET /api/list (default / filter=all) -> ALL media: every video id (galx_vid_paths.tsv) + every gen cid
2253// (galx_view_index.txt), newest-first, paginated [o,o+n). Video line-ids use gs_collection_list semantics
2254// (a real line is len>3) so /vid/ and /vidthumb/ resolve identically; the frontend addCell renders mixed ids.
2255func gs_allmedia_list(rbuf: *u8, req: *u8, rn: i64, isold: i64, o0: i64, n: i64) -> i64 {
2256 let gfd: i64 = sys_openat_rd(VIEW_IDX)
2257 var gtotal: i64 = 0
2258 if gfd >= 0 { let gep: i64 = sys_lseek(gfd, 0, 2); gtotal = gep / 70 }
2259 let szp: *i64 = sys_mmap(16) as *i64
2260 let vb: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
2261 let vsz: i64 = szp[0]
2262 let vids: *i64 = sys_mmap(8 * VIEW_MAGIC_1000000) as *i64
2263 var nvid: i64 = 0
2264 if (vb as i64) != 0 {
2265 var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
2266 while i <= vsz {
2267 var nl: i64 = 0
2268 if i == vsz { nl = 1 } else { if vb[i] == (10 as u8) { nl = 1 } }
2269 if nl == 1 {
2270 if i - ls > 3 { if nvid < VIEW_MAGIC_1000000 { vids[nvid] = lineno; nvid = nvid + 1 } }
2271 lineno = lineno + 1; ls = i + 1
2272 }
2273 i = i + 1
2274 }
2275 }
2276 let total: i64 = gtotal + nvid
2277 let eo0: i64 = gs_eff_offset(req, rn, total, o0)
2278 let body: *u8 = sys_mmap(VIEW_MAGIC_131072)
2279 var bo: i64 = 0
2280 bo = gs_cat(body, bo, "{\"total\":" as *u8); bo = gs_u(body, bo, total); bo = gs_cat(body, bo, ",\"items\":[" as *u8)
2281 let line: *u8 = sys_mmap(80)
2282 var k: i64 = 0; var first: i64 = 1
2283 while k < n {
2284 let disp: i64 = eo0 + k
2285 if disp >= total { k = n } else {
2286 var sp: i64 = disp
2287 if isold == 1 { sp = total - 1 - disp }
2288 if sp < nvid {
2289 let vidid: i64 = vids[nvid - 1 - sp]
2290 if first == 0 { body[bo] = 44 as u8; bo = bo + 1 }
2291 first = 0
2292 body[bo] = 34 as u8; bo = bo + 1
2293 bo = gs_u(body, bo, vidid)
2294 body[bo] = 34 as u8; bo = bo + 1
2295 } else {
2296 let gi: i64 = sp - nvid
2297 if gfd >= 0 {
2298 sys_lseek(gfd, gi * 70, 0)
2299 let got: i64 = sys_read(gfd, line, 69)
2300 if got == 69 { bo = gs_emit_cid(body, bo, first, line, 0); first = 0 }
2301 }
2302 }
2303 k = k + 1
2304 }
2305 }
2306 if gfd >= 0 { sys_close(gfd) }
2307 bo = gs_cat(body, bo, "]}" as *u8)
2308 return gs_okjson(rbuf, body, bo)
2309}
2310func gs_api_list(rbuf: *u8, req: *u8, rn: i64, gal: *NxGalIdx) -> i64 {
2311 let o0: i64 = gs_qint(req, rn, "o=" as *u8, 0)
2312 var n: i64 = gs_qint(req, rn, "n=" as *u8, 100)
2313 if n > 400 { n = 400 }
2314 if n < 1 { n = 1 }
2315 let isold: i64 = eh_find(req, rn, "s=old" as *u8, 5)
2316 // R2 sort-by-size for the video collections (lazily self-builds galx_sort_s_<c>.bin; composes pct=/rand=).
2317 // dir defaults to desc (largest first); &dir=asc flips it. Image/fav/rated/viewed paths ignore sort= (no-op).
2318 // R2 sort -> the sovereignly-gated handlers in nx_galx_sortindex.nx (ss_emit_*); gate = nx_galx_sortserve_gate.
2319 if eh_find(req, rn, "sort=size" as *u8, 9) == 1 {
2320 var sdir: i64 = 1; if eh_find(req, rn, "dir=asc" as *u8, 7) == 1 { sdir = 0 }
2321 if eh_find(req, rn, "filter=recordings" as *u8, 17) == 1 { return ss_emit_size(rbuf, req, rn, 114, sdir, o0, n) }
2322 if eh_find(req, rn, "filter=media" as *u8, 12) == 1 { return ss_emit_size(rbuf, req, rn, 109, sdir, o0, n) }
2323 if eh_find(req, rn, "filter=videos" as *u8, 13) == 1 { return ss_emit_size(rbuf, req, rn, 118, sdir, o0, n) }
2324 }
2325 if eh_find(req, rn, "sort=runtime" as *u8, 12) == 1 {
2326 var rdir: i64 = 1; if eh_find(req, rn, "dir=asc" as *u8, 7) == 1 { rdir = 0 }
2327 if eh_find(req, rn, "filter=recordings" as *u8, 17) == 1 { return ss_emit_runtime(rbuf, req, rn, 114, rdir, o0, n) }
2328 if eh_find(req, rn, "filter=media" as *u8, 12) == 1 { return ss_emit_runtime(rbuf, req, rn, 109, rdir, o0, n) }
2329 if eh_find(req, rn, "filter=videos" as *u8, 13) == 1 { return ss_emit_runtime(rbuf, req, rn, 118, rdir, o0, n) }
2330 }
2331 if eh_find(req, rn, "sort=name" as *u8, 9) == 1 {
2332 var ndir: i64 = 0; if eh_find(req, rn, "dir=desc" as *u8, 8) == 1 { ndir = 1 } // name defaults A->Z (asc)
2333 if eh_find(req, rn, "filter=recordings" as *u8, 17) == 1 { return ss_emit_name(rbuf, req, rn, 114, ndir, o0, n) }
2334 if eh_find(req, rn, "filter=media" as *u8, 12) == 1 { return ss_emit_name(rbuf, req, rn, 109, ndir, o0, n) }
2335 if eh_find(req, rn, "filter=videos" as *u8, 13) == 1 { return ss_emit_name(rbuf, req, rn, 118, ndir, o0, n) }
2336 }
2337 // filter
2338 if eh_find(req, rn, "filter=recordings" as *u8, 17) == 1 { return gs_collection_list(rbuf, req, rn, 114, o0, n) }
2339 if eh_find(req, rn, "filter=media" as *u8, 12) == 1 { return gs_collection_list(rbuf, req, rn, 109, o0, n) }
2340 if eh_find(req, rn, "filter=videos" as *u8, 13) == 1 { return gs_collection_list(rbuf, req, rn, 118, o0, n) }
2341 if eh_find(req, rn, "filter=fav" as *u8, 10) == 1 { return gs_filter_list(rbuf, FAV_LOG, 1, o0, n) }
2342 if eh_find(req, rn, "filter=rated" as *u8, 12) == 1 { return gs_filter_list(rbuf, EVAL_LOG, 2, o0, n) }
2343 if eh_find(req, rn, "filter=viewed" as *u8, 13) == 1 { return gs_filter_list(rbuf, VIEW_LOG, 0, o0, n) }
2344 if eh_find(req, rn, "filter=gens" as *u8, 11) == 1 { return gs_gens_list(rbuf, req, rn, isold, o0, n) }
2345 // q=
2346 var li: i64 = 0; var qpos: i64 = 0 - 1; var stop: i64 = rn
2347 while li < stop { if req[li]==(10 as u8) { stop=li } else { if qpos<0 { if li+2<=rn { if req[li]==(113 as u8) { if req[li+1]==(61 as u8) { qpos=li+2 } } } } li=li+1 } }
2348 let qbuf: *u8 = sys_mmap(512)
2349 var qlen: i64 = 0
2350 if qpos >= 0 { var p: i64=qpos; var go: i64=1; while go==1 { if p>=rn { go=0 } else { let ch: i64=req[p] as i64; if ch==38 { go=0 } else { if ch==32 { go=0 } else { if ch==10 { go=0 } else { if ch==13 { go=0 } else { if qlen<500 { qbuf[qlen]=gs_lower(ch) as u8; qlen=qlen+1 } p=p+1 } } } } } } }
2351 let body: *u8 = sys_mmap(VIEW_MAGIC_131072)
2352 var bo: i64 = 0
2353 if qlen == 0 {
2354 return gs_allmedia_list(rbuf, req, rn, isold, o0, n)
2355 } else {
2356 // BM25 RANKED path (durable index loaded once in the parent; inherited via COW). Falls through to the
2357 // legacy substring scan only if the index is absent/stale -- so search is never worse than before.
2358 if gal.valid == 1 { return gs_bm25_emit(rbuf, gal, qbuf, qlen, o0, n) }
2359 let szp: *i64 = sys_mmap(16) as *i64
2360 let sb: *u8 = sys_read_file(SEARCH_IDX, szp)
2361 let sz: i64 = szp[0]
2362 bo = gs_cat(body, bo, "{\"items\":[" as *u8)
2363 if (sb as i64) == 0 { bo = gs_cat(body, bo, "],\"building\":1}" as *u8) } else {
2364 var i: i64 = 0; var ls: i64 = 0; var matched: i64 = 0; var emitted: i64 = 0; var first: i64 = 1
2365 while i < sz {
2366 if sb[i] == (10 as u8) {
2367 if i - ls > 70 { if sb[ls+69]==(9 as u8) {
2368 var hit: i64=0; var p: i64=ls+70
2369 while p + qlen <= i { var j: i64=0; var ok: i64=1; while j<qlen { if sb[p+j]!=qbuf[j]{ok=0; j=qlen} else { j=j+1 } } if ok==1 { hit=1; p=i } else { p=p+1 } }
2370 if hit==1 { if matched>=o0 { if emitted<n { bo=gs_emit_cid(body,bo,first,sb,ls); first=0; emitted=emitted+1 } } matched=matched+1 }
2371 } }
2372 ls = i + 1
2373 }
2374 i = i + 1
2375 }
2376 bo = gs_cat(body, bo, "]}" as *u8)
2377 }
2378 }
2379 return gs_okjson(rbuf, body, bo)
2380}
2381
2382// ---- /rate ----
2383func gs_rate(rbuf: *u8, req: *u8, rn: i64) -> i64 {
2384 if gs_same_origin(req, rn) == 0 { return gs_forbidden(rbuf) }
2385 let cid: *u8 = sys_mmap(96)
2386 if gs_body_cid(req, rn, cid) == 0 { return gs_cat(rbuf, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nContent-Length: 3\r\nConnection: close\r\n\r\nbad" as *u8) }
2387 var bodyat: i64 = 0 - 1
2388 var i: i64 = 0
2389 while i + 4 <= rn { if req[i]==(13 as u8) { if req[i+1]==(10 as u8) { if req[i+2]==(13 as u8) { if req[i+3]==(10 as u8) { if bodyat<0 { bodyat=i+4 } } } } } i=i+1 }
2390 let score: *u8 = sys_mmap(32)
2391 var sm: i64 = 0
2392 if bodyat >= 0 {
2393 var sat: i64 = 0 - 1; var ps: i64 = bodyat
2394 while ps + 6 <= rn { var oks: i64=1; let sp: *u8="score=" as *u8; var jj: i64=0; while jj<6 { if req[ps+jj]!=sp[jj]{oks=0} jj=jj+1 } if oks==1 { if sat<0 { sat=ps+6 } } ps=ps+1 }
2395 if sat >= 0 { var q: i64=sat; var god: i64=1; while q<rn { if god==1 { if req[q]>=(48 as u8) { if req[q]<=(57 as u8) { score[sm]=req[q]; sm=sm+1 } else { god=0 } } else { god=0 } } q=q+1 } }
2396 }
2397 score[sm] = 0 as u8
2398 let line: *u8 = sys_mmap(256)
2399 var lo: i64 = gs_cat(line, 0, "EVAL img=" as *u8)
2400 var c: i64 = 0
2401 while c < 69 { line[lo] = cid[c]; lo = lo + 1; c = c + 1 }
2402 lo = gs_cat(line, lo, " lane=O score=" as *u8)
2403 var s2: i64 = 0
2404 while score[s2] != (0 as u8) { line[lo] = score[s2]; lo = lo + 1; s2 = s2 + 1 }
2405 line[lo] = 10 as u8; lo = lo + 1
2406 gs_append_line(EVAL_LOG, line, lo)
2407 return gs_okrate(rbuf)
2408}
2409
2410// POST /lora/export -> run the sovereign export organ (favorites -> training manifest), return the count.
2411func gs_lora_export(rbuf: *u8, req: *u8, rn: i64) -> i64 {
2412 if gs_same_origin(req, rn) == 0 { return gs_forbidden(rbuf) }
2413 let wpath: *u8 = "/volume1/ai/galx/nx_galx_lora_export.elf" as *u8
2414 let argv: *i64 = sys_mmap(16) as *i64
2415 argv[0] = wpath as i64; argv[1] = 0
2416 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
2417 let pid: i64 = sys_fork()
2418 if pid == 0 { sys_execve_clean(wpath, argv, envp); sys_exit(127) }
2419 let st: *i64 = sys_mmap(16) as *i64
2420 sys_wait4(pid, st, 0)
2421 let szp: *i64 = sys_mmap(16) as *i64
2422 let b: *u8 = sys_read_file("knowledge/lora/favorites/manifest.tsv" as *u8, szp)
2423 let sz: i64 = szp[0]
2424 var cnt: i64 = 0
2425 if (b as i64) != 0 { var i: i64 = 0; while i < sz { if b[i]==(10 as u8) { cnt = cnt + 1 } i = i + 1 } }
2426 let body: *u8 = sys_mmap(128)
2427 var bo: i64 = gs_cat(body, 0, "{\"exported\":" as *u8)
2428 bo = gs_u(body, bo, cnt)
2429 bo = gs_cat(body, bo, "}" as *u8)
2430 return gs_okjson(rbuf, body, bo)
2431}
2432
2433// ---- gallery shell ----
2434func gs_shell(rbuf: *u8) -> i64 {
2435 // 256KB: the shell outgrew 128KB when the lightbox gained history-close + anchored pinch (2026-07-14)
2436 let body: *u8 = sys_mmap(VIEW_MAGIC_262144)
2437 var bo: i64 = 0
2438 bo = gs_cat(body, bo, "<!doctype html><html lang=en><meta charset=utf-8><title>Nishi Gallery</title><meta name=viewport content='width=device-width,initial-scale=1'><style>:root{--bg:#0b0d10;--fg:#e6e8eb;--mut:#8b929c;--card:#15181d;--acc:#5b9dff;--fav:#ff5a7a;--bd:#222831}*{box-sizing:border-box}html,body{margin:0;height:100%}body{background:var(--bg);color:var(--fg);font:14px/1.5 Inter,system-ui,sans-serif}header{position:sticky;top:0;z-index:5;display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:10px 14px;background:rgba(11,13,16,.95);border-bottom:1px solid var(--bd);backdrop-filter:blur(8px)}header h1{font-size:15px;font-weight:600;margin:0}header input{flex:1;min-width:160px;max-width:460px;background:#0f1318;border:1px solid var(--bd);color:var(--fg);border-radius:9px;padding:8px 12px}header select,.fbtn{background:#0f1318;border:1px solid var(--bd);color:var(--fg);border-radius:9px;padding:8px 11px;cursor:pointer;font-size:13px}.fbtn.on{background:var(--acc);color:#06101f;border-color:var(--acc)}.count{color:var(--mut);font-size:12px;margin-left:auto}#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;padding:10px}.cell{position:relative;display:block;border-radius:10px;overflow:hidden;background:var(--card);border:1px solid var(--bd);cursor:pointer;aspect-ratio:1;content-visibility:auto;contain-intrinsic-size:220px}.cell img{width:100%;height:100%;object-fit:cover;display:block}.cell.f::after{content:'\\2665';position:absolute;top:6px;right:8px;color:var(--fav);font-size:16px;text-shadow:0 1px 3px #000}#st{color:var(--mut);text-align:center;padding:18px;font-size:13px}#lb{position:fixed;inset:0;background:rgba(0,0,0,.95);display:none;z-index:50}#lb.on{display:block}#lbimg{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:18px;overflow:auto;touch-action:pan-y}#pbar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:10px 14px;background:linear-gradient(transparent,rgba(0,0,0,.8))}#pbar button,#pbar a{background:none;border:0;color:#fff;font-size:20px;cursor:pointer;padding:0 4px;line-height:1;text-decoration:none}#pbar input[type=range]{flex:1;cursor:pointer}#pbar .tt{color:#fff;font-size:12px;white-space:nowrap;min-width:92px;text-align:right}#lbimg img{max-width:100%;max-height:100%;object-fit:contain;border-radius:8px;touch-action:none;transform-origin:0 0;will-change:transform}#lbv{max-width:100%;max-height:100%;border-radius:8px;display:none;touch-action:pan-y}.cell .play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:38px;color:#fff;opacity:.92;text-shadow:0 2px 10px #000;pointer-events:none}.dur{position:absolute;bottom:6px;right:6px;background:rgba(0,0,0,.78);color:#fff;font-size:11px;line-height:1.3;padding:1px 5px;border-radius:4px;font-weight:600;pointer-events:none}.dur:empty{display:none}.daband{position:absolute;top:calc(50% - 3px);height:6px;background:rgba(255,90,90,.6);pointer-events:none;border-radius:2px;z-index:1}.daband.t2{background:rgba(90,200,255,.95);width:2px;z-index:3}.daband.t3{background:rgba(120,220,120,.45);z-index:1}.daband.t4{background:rgba(200,120,255,.5);z-index:1}#tagbar{position:absolute;left:50%;transform:translateX(-50%);bottom:58px;display:none;flex-wrap:wrap;gap:6px;justify-content:center;max-width:88vw;z-index:6}.tg{background:#2a2f3a;color:#cdd3df;border-radius:12px;padding:3px 10px;font-size:12px;display:inline-flex;align-items:center;gap:6px}.tg b{cursor:pointer;color:#8a93a6;font-weight:700;font-style:normal}.tg b:hover{color:#ff6b6b}#tgin{background:#1a1d24;border:1px solid var(--bd);color:#fff;border-radius:12px;padding:3px 10px;font-size:12px;width:84px;outline:none}#tagsfab{position:fixed;left:16px;bottom:16px;z-index:20;background:var(--card);border:1px solid var(--bd);color:#cdd3df;border-radius:50%;width:46px;height:46px;font-size:20px;cursor:pointer;box-shadow:0 2px 10px #0008}#tagcloud{position:fixed;left:16px;bottom:72px;z-index:20;display:none;flex-wrap:wrap;gap:8px;max-width:80vw;max-height:50vh;overflow:auto;background:var(--card);border:1px solid var(--bd);border-radius:12px;padding:12px}.seld{outline:3px solid #4a9eff;outline-offset:-3px}#selfab{position:fixed;left:74px;bottom:16px;z-index:20;background:var(--card);border:1px solid var(--bd);color:#cdd3df;border-radius:50%;width:46px;height:46px;font-size:20px;cursor:pointer;box-shadow:0 2px 10px #0008}#selfab.on{background:#4a9eff;color:#fff}#selbar{position:fixed;left:50%;transform:translateX(-50%);bottom:16px;z-index:21;display:none;gap:10px;align-items:center;background:var(--card);border:1px solid var(--bd);border-radius:12px;padding:8px 14px;box-shadow:0 2px 14px #000a}#selbar button{background:#2a2f3a;color:#fff;border:none;border-radius:8px;padding:5px 12px;cursor:pointer;font-size:13px}#selcount{color:#cdd3df;font-size:13px}#side{position:absolute;left:0;right:0;bottom:0;background:var(--card);border-top:1px solid var(--bd);border-radius:16px 16px 0 0;padding:26px 18px 18px;overflow:auto;max-height:72vh;transform:translateY(calc(100% - 46px));transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:0 -10px 34px rgba(0,0,0,.55);z-index:70;touch-action:none}#side.up{transform:translateY(0)}#side::before{content:'';position:absolute;top:10px;left:50%;transform:translateX(-50%);width:46px;height:5px;border-radius:3px;background:var(--mut);opacity:.85;cursor:pointer}#side h2{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--mut);margin:14px 0 8px}#side h2:first-child{margin-top:0}#favbtn{width:100%;background:#1a1f27;border:1px solid var(--bd);color:var(--fav);border-radius:8px;padding:10px;font-weight:600;cursor:pointer;font-size:14px}#favbtn.on{background:var(--fav);color:#1a0a0f;border-color:var(--fav)}.fac{font-size:12px;display:flex;justify-content:space-between;gap:12px;padding:5px 0;border-bottom:1px solid var(--bd)}.fac b{color:var(--mut);font-weight:500;white-space:nowrap}.fac span{text-align:right;word-break:break-word}#rng{width:100%}#rval{font-size:15px;font-weight:700;color:var(--acc)}#rbtn{width:100%;background:var(--acc);color:#06101f;border:0;border-radius:8px;padding:9px;font-weight:600;cursor:pointer;margin-top:6px}#rmsg{font-size:12px;color:#5ad08a;min-height:15px;margin-top:6px}#x{position:absolute;top:8px;right:14px;color:#fff;font-size:30px;cursor:pointer;opacity:.7;z-index:60}.nav{position:absolute;top:50%;transform:translateY(-50%);font-size:40px;color:#fff;opacity:.4;cursor:pointer;padding:12px;user-select:none;z-index:60}.nav:hover{opacity:1}#pv{left:6px}#nx{right:14px}#x{font-size:36px;padding:2px 14px}.spin{position:absolute;top:50%;left:50%;width:46px;height:46px;margin:-23px 0 0 -23px;border:4px solid rgba(255,255,255,.25);border-top-color:#fff;border-radius:50%;animation:sp .8s linear infinite;z-index:55}@keyframes sp{to{transform:rotate(360deg)}}@media(max-width:760px){#grid{grid-template-columns:repeat(auto-fill,minmax(108px,1fr));gap:5px;padding:6px}#lb.on{grid-template-columns:1fr;grid-template-rows:1fr}#lbimg{padding:0;width:100vw;height:100dvh}#side{position:fixed;left:0;right:0;bottom:0;top:auto;width:100%;max-height:84dvh;border-left:none;border-top:1px solid var(--bd);border-radius:16px 16px 0 0;padding:26px 16px calc(16px + env(safe-area-inset-bottom));transform:translateY(calc(100% - 48px));transition:transform .3s cubic-bezier(.32,.72,0,1);z-index:70;box-shadow:0 -10px 34px rgba(0,0,0,.55);touch-action:none}#side.up{transform:translateY(0)}#side::before{content:'';position:absolute;top:9px;left:50%;transform:translateX(-50%);width:42px;height:5px;border-radius:3px;background:var(--mut);opacity:.85}.nav{display:none}#x{font-size:38px;top:6px;right:14px;opacity:.95;z-index:80}header h1{font-size:14px}header input{min-width:110px}}</style>" as *u8)
2439 bo = gs_cat(body, bo, "<body><header><h1>Nishi Gallery</h1><input id=q placeholder='search prompts...' autocomplete=off><button class=fbtn on data-f=all>All</button><button class=fbtn onclick=nx4() title=Browse-4chan-via-the-sovereign-nx_4chan-adapter>🖼 4chan</button><button class=fbtn data-f=gens>Gens</button><button class=fbtn data-f=fav>♥ Favorites</button><button class=fbtn data-f=rated>Rated</button><button class=fbtn data-f=viewed>Viewed</button><button class=fbtn data-f=recordings>► Recordings</button><button class=fbtn data-f=media>📺 Media</button><button class=fbtn data-f=videos>🎬 Videos</button><select id=modelsel style=display:none><option value=>All people</option></select><button id=loraexp class=fbtn title='Export favorited images as a LoRA training dataset'>♥→LoRA</button><select id=sort><option value=new>Newest</option><option value=old>Oldest</option><option value=lg>Largest file</option><option value=sm>Smallest file</option><option value=lng>Longest</option><option value=sht>Shortest</option><option value=naz>Name A→Z</option><option value=nza>Name Z→A</option></select><button id=rnd class=fbtn title='Jump to a random spot in the library'>🎲 Random</button><label style='display:flex;align-items:center;gap:5px;color:var(--mut);font-size:12px' title='Drag to jump anywhere in the library (0%=newest, 100%=oldest)'>📍<input id=pos type=range min=0 max=100 value=0 style='flex:0 0 110px;max-width:110px'></label><span class=count id=count></span></header><div id=grid></div><div id=sentinel></div><button id=tagsfab onclick=toggleTagCloud() title=Tags>🏷</button><div id=tagcloud></div><button id=selfab onclick=toggleSelMode() title=Select>☑</button><div id=selbar><span id=selcount>0 selected</span><button onclick=batchHide()>Hide</button><button onclick=batchTag()>Tag</button><button onclick=toggleSelMode()>Done</button></div><div id=st>loading...</div>" as *u8)
2440 bo = gs_cat(body, bo, "<script>function nx4e(s){return (s||'').replace(/[<>&]/g,function(c){return c=='<'?'<':c=='>'?'>':'&'})}function nx4img(u){return '/gallery/api/4chan/img/'+(''+u).replace('https://i.4cdn.org/','')}function nx4b(){var d=document.getElementById('nx4d');if(!d){d=document.createElement('div');d.id='nx4d';d.style.cssText='position:fixed;inset:0;background:#0e0e0e;overflow:auto;z-index:200;padding:14px';document.body.appendChild(d)}d.style.display='block';if(!window._nx4Pushed){history.pushState({nx4:1},'');window._nx4Pushed=1}return d}function nx4x(){var d=document.getElementById('nx4d');if(d)d.style.display='none';if(window._nx4Pushed){window._nx4Pushed=0;history.back()}}function nx4(){var b=prompt('4chan board (e.g. s, gif, hr, po, a)','po');if(b)nx4c(b)}function nx4c(b){var d=nx4b();d.innerHTML='<div style=color:#888>loading /'+nx4e(b)+'/ ...</div>';fetch('/gallery/api/4chan/catalog/'+encodeURIComponent(b)).then(function(r){return r.json()}).then(function(j){if(j.error){d.innerHTML='<div style=color:#f66>'+nx4e(j.error)+'</div>';return}var h='<div style=margin-bottom:10px><button class=fbtn onclick=nx4x()>close</button> <button class=fbtn onclick=nx4()>board...</button> <b style=color:#eee>/'+nx4e(b)+'/ '+j.count+' threads</b></div><div style=display:flex;flex-wrap:wrap;gap:8px>';(j.threads||[]).forEach(function(t){h+='<div class=nx4t data-b='+b+' data-n='+t.no+' style=width:186px;background:#1b1b1b;padding:6px;border-radius:6px;cursor:pointer>'+(t.thumb?'<img src='+nx4img(t.thumb)+' loading=lazy style=width:100%;border-radius:4px>':'')+'<div style=color:#ddd;font-size:12px;margin-top:4px><b>'+nx4e(t.sub)+'</b></div><div style=color:#888;font-size:11px>'+nx4e((t.com||'').slice(0,90))+'</div><div style=color:#6af;font-size:11px>'+t.replies+'R / '+t.images+'I</div></div>'});d.innerHTML=h+'</div>';Array.prototype.forEach.call(d.querySelectorAll('.nx4t'),function(el){el.onclick=function(){nx4t(el.getAttribute('data-b'),el.getAttribute('data-n'))}})}).catch(function(){d.innerHTML='<div style=color:#f66>fetch failed</div>'})}function nx4t(b,no){var d=nx4b();d.innerHTML='<div style=color:#888>loading thread '+no+' ...</div>';fetch('/gallery/api/4chan/thread/'+encodeURIComponent(b)+'/'+encodeURIComponent(no)).then(function(r){return r.json()}).then(function(j){if(j.error){d.innerHTML='<div style=color:#f66>'+nx4e(j.error)+'</div>';return}var h='<div style=margin-bottom:10px><button class=fbtn onclick=nx4x()>close</button> <button class=fbtn id=nx4bk>back</button> <a class=fbtn href=/gallery/api/4chan/panel/'+encodeURIComponent(b)+'/'+encodeURIComponent(no)+' style=text-decoration:none>download all to library</a> <b style=color:#eee>'+j.count+' media</b></div><div style=display:flex;flex-wrap:wrap;gap:8px>';window._nx4m=(j.media||[]);window._nx4m.forEach(function(m,ix){h+='<div class=nx4it data-ix='+ix+' style=width:300px;color:#888;font-size:11px;cursor:zoom-in><img src='+nx4img(m.thumb)+' loading=lazy style=width:100%;border-radius:4px><div>'+nx4e(m.filename)+' '+m.w+'x'+m.h+'</div></div>'});d.innerHTML=h+'</div>';var bk=document.getElementById('nx4bk');if(bk)bk.onclick=function(){nx4c(b)};Array.prototype.forEach.call(d.querySelectorAll('.nx4it'),function(el){el.onclick=function(){nx4open(parseInt(el.getAttribute('data-ix')))}})}).catch(function(){d.innerHTML='<div style=color:#f66>fetch failed</div>'})}window.addEventListener('popstate',function(){var v=document.getElementById('nx4v');if(v&&v.style.display!='none'&&window._nx4vPushed){window._nx4vPushed=0;v.style.display='none';return}var d=document.getElementById('nx4d');if(d&&d.style.display!='none'&&window._nx4Pushed){window._nx4Pushed=0;d.style.display='none'}});</script>" as *u8)
2441 bo = gs_cat(body, bo, "<script>function nx4open(i){var M=window._nx4m||[];if(!M[i])return;var v=document.getElementById('nx4v');if(!v){v=document.createElement('div');v.id='nx4v';v.style.cssText='position:fixed;inset:0;background:#000;z-index:300;display:none;align-items:center;justify-content:center;touch-action:pan-y';document.body.appendChild(v);v.addEventListener('click',function(e){if(e.target===v)nx4vx()})}window._nx4i=i;if(!window._nx4vPushed){history.pushState({nx4v:1},'');window._nx4vPushed=1}v.style.display='flex';nx4vs()}function nx4vs(){var M=window._nx4m||[],i=window._nx4i||0,m=M[i];if(!m)return;var v=document.getElementById('nx4v');var iv=/[.](webm|mp4)$/i.test(m.full);var md=iv?('<video src='+nx4img(m.full)+' controls autoplay loop playsinline style=max-width:100%;max-height:100%></video>'):('<img src='+nx4img(m.full)+' style=max-width:100%;max-height:100%;object-fit:contain>');v.innerHTML='<span onclick=nx4vx() style=position:absolute;top:8px;right:16px;color:#fff;font-size:34px;cursor:pointer;z-index:2>×</span><span onclick=nx4vn(-1) style=position:absolute;left:6px;top:50%;transform:translateY(-50%);color:#fff;font-size:42px;opacity:.55;cursor:pointer;padding:14px;z-index:2>‹</span><span onclick=nx4vn(1) style=position:absolute;right:6px;top:50%;transform:translateY(-50%);color:#fff;font-size:42px;opacity:.55;cursor:pointer;padding:14px;z-index:2>›</span>'+md+'<div style=position:absolute;bottom:8px;left:50%;transform:translateX(-50%);color:#bbb;font-size:12px>'+(i+1)+' / '+M.length+'</div>'}function nx4vn(d){var M=window._nx4m||[];if(!M.length)return;var n=(window._nx4i||0)+d;if(n<0)n=M.length-1;if(n>=M.length)n=0;window._nx4i=n;nx4vs()}function nx4vx(){if(window._nx4vPushed){history.back()}else{var v=document.getElementById('nx4v');if(v)v.style.display='none'}}document.addEventListener('keydown',function(e){var v=document.getElementById('nx4v');if(!v||v.style.display=='none')return;if(e.key=='Escape')nx4vx();else if(e.key=='ArrowLeft')nx4vn(-1);else if(e.key=='ArrowRight')nx4vn(1)});(function(){var sx=0,sy=0;document.addEventListener('touchstart',function(e){var v=document.getElementById('nx4v');if(v&&v.style.display!='none'&&e.touches.length==1){sx=e.touches[0].clientX;sy=e.touches[0].clientY}},{passive:true});document.addEventListener('touchend',function(e){var v=document.getElementById('nx4v');if(!v||v.style.display=='none')return;var dx=e.changedTouches[0].clientX-sx,dy=e.changedTouches[0].clientY-sy;if(Math.abs(dx)>50&&Math.abs(dx)>Math.abs(dy))nx4vn(dx<0?1:-1)},{passive:true})})();</script>" as *u8)
2442 bo = gs_cat(body, bo, "<script>(function(){var B=(location.pathname.indexOf('/gallery')===0)?'/gallery':'';var grid=document.getElementById('grid'),sent=document.getElementById('sentinel'),st=document.getElementById('st'),q=document.getElementById('q'),sortsel=document.getElementById('sort'),countEl=document.getElementById('count');var off=0,N=60,loading=false,done=false,cids=[],favs={},hidden={},filter='all',cols=[],colH=[],ncols=0,gridIsRec=false,model='',modelsLoaded=false;var lastTotal=0,rndb=document.getElementById('rnd'),posb=document.getElementById('pos');var TB=function(o){try{var d=JSON.stringify(o);if(navigator.sendBeacon){navigator.sendBeacon(B+'/telemetry',d)}else{fetch(B+'/telemetry',{method:'POST',body:d,keepalive:true})}}catch(_){}};window.addEventListener('error',function(e){TB({k:'jserr',m:((e.message||'')+'').slice(0,300),s:((e.filename||'')+'').slice(0,160),l:e.lineno||0,c:e.colno||0,p:location.pathname})});window.addEventListener('unhandledrejection',function(e){TB({k:'reject',m:(((e.reason&&e.reason.message)||e.reason||'')+'').slice(0,300)})});addEventListener('load',function(){setTimeout(function(){try{var nv=performance.getEntriesByType('navigation')[0],fp=performance.getEntriesByType('paint');TB({k:'perf',dcl:nv?Math.round(nv.domContentLoadedEventEnd):0,ld:nv?Math.round(nv.loadEventEnd):0,fcp:(fp&&fp[0])?Math.round(fp[0].startTime):0,n:cids.length,vp:innerWidth+'x'+innerHeight})}catch(_){}},0)});try{new PerformanceObserver(function(pl){pl.getEntries().forEach(function(en){if(en.duration>150){TB({k:'longtask',d:Math.round(en.duration),p:location.pathname})}})}).observe({type:'longtask',buffered:true})}catch(_){}function esc(s){return String(s).replace(/[<&>]/g,function(c){return c=='<'?'<':c=='>'?'>':'&'})}function smap(){var v=sortsel.value;if(v=='lg')return 'sort=size&dir=desc';if(v=='sm')return 'sort=size&dir=asc';if(v=='lng')return 'sort=runtime&dir=desc';if(v=='sht')return 'sort=runtime&dir=asc';if(v=='naz')return 'sort=name&dir=asc';if(v=='nza')return 'sort=name&dir=desc';return 's='+v}function qs(){return 'o='+off+'&n='+N+'&'+smap()+'&filter='+filter+'&model='+encodeURIComponent(model)+'&q='+encodeURIComponent(q.value)}function buildcols(){grid.innerHTML=''}function shortest(){var mi=0;for(var i=1;i<ncols;i++){if(colH[i]<colH[mi])mi=i}return mi}var selMode=false,sel={},selN=0;function toggleSelMode(){selMode=!selMode;if(!selMode){for(var k in sel){var e=grid.querySelector('[data-cid='+k+']');if(e)e.classList.remove('seld')}sel={};selN=0}selfab.classList.toggle('on',selMode);selbar.style.display=selMode?'flex':'none';updSel()}function toggleSel(cid,el){if(sel[cid]){delete sel[cid];el.classList.remove('seld');selN=selN-1}else{sel[cid]=1;el.classList.add('seld');selN=selN+1}updSel()}function updSel(){selcount.textContent=selN+' selected'}function selIds(){var a=[];for(var k in sel){a.push(k)}return a}function batchHide(){var ids=selIds();if(!ids.length)return;if(!confirm('Hide '+ids.length+' videos? (reversible)'))return;var n=0;ids.forEach(function(id){fetch(B+'/hide',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'id='+id+'&v=1'}).then(function(){hidden[id]=1;n=n+1;if(n==ids.length){toggleSelMode();reset()}})})}function batchTag(){var ids=selIds();if(!ids.length)return;var t=prompt('Tag '+ids.length+' videos with:');if(!t)return;t=t.replace(/[^a-zA-Z0-9 -]/g,'').replace(/ /g,'+');if(!t)return;var n=0;ids.forEach(function(id){fetch(B+'/tag',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'id='+id+'&tag='+t+'&v=1'}).then(function(){n=n+1;if(n==ids.length){toggleSelMode()}})})}function toggleTagCloud(){if(tagcloud.style.display=='flex'){tagcloud.style.display='none';return}tagcloud.style.display='flex';tagcloud.innerHTML='loading...';fetch(B+'/api/alltags').then(function(r){return r.json()}).then(function(ts){if(!ts.length){tagcloud.innerHTML='no tags yet';return}var h='';for(var i=0;i<ts.length;i++){h+='<span class=tg>'+ts[i]+'</span>'}tagcloud.innerHTML=h;var cs=tagcloud.querySelectorAll('.tg');for(var i=0;i<cs.length;i++){(function(nm){cs[i].onclick=function(){tagcloud.style.display='none';renderTagged(nm)}})(ts[i])}}).catch(function(){tagcloud.innerHTML='error'})}function renderTagged(t){lb.classList.remove('on');cur=-1;lbv.pause();done=true;loading=false;cids=[];gridIsRec=1;buildcols();countEl.textContent='#'+t;st.textContent='';fetch(B+'/api/tagged?tag='+encodeURIComponent(t).replace(/%20/g,'+')).then(function(r){return r.json()}).then(function(ids){for(var i=0;i<ids.length;i++){if(!hidden[ids[i]]){cids.push(ids[i]);addCell(ids[i])}}loadDurs(ids);if(!ids.length){st.textContent='no videos tagged '+t}}).catch(function(){st.textContent='error'})}function postTag(cid,tag,v){var b='id='+cid+'&tag='+tag.replace(/[^a-zA-Z0-9 -]/g,'').replace(/ /g,'+')+'&v='+v;fetch(B+'/tag',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:b}).then(function(){showTags(cid)})}function showTags(cid){tagbar.style.display='flex';fetch(B+'/api/tags?id='+cid).then(function(r){return r.json()}).then(function(ts){var h='';for(var i=0;i<ts.length;i++){h+='<span class=tg>'+ts[i]+'<b>×</b></span>'}tagbar.innerHTML=h+'<input id=tgin placeholder=+tag autocomplete=off>';var cs=tagbar.querySelectorAll('.tg');for(var i=0;i<cs.length;i++){(function(nm){cs[i].onclick=function(){renderTagged(nm)};cs[i].querySelector('b').onclick=function(e){e.stopPropagation();postTag(cid,nm,0)}})(ts[i])}var inp=document.getElementById('tgin');inp.onkeydown=function(e){if(e.key=='Enter'){e.preventDefault();var x=inp.value.trim();if(x){postTag(cid,x,1)}}}}).catch(function(){})}function fmtDur(ms){var s=Math.floor(ms/1000),h=Math.floor(s/3600),m=Math.floor((s%3600)/60),ss=s%60;function p(n){return(n<10?'0':'')+n}return h>0?h+':'+p(m)+':'+p(ss):m+':'+p(ss)}function loadDurs(ids){if(!ids||!ids.length)return;fetch(B+'/api/durs?ids='+ids.join(',')).then(function(r){return r.json()}).then(function(d){for(var k in d){var e=grid.querySelector('[data-durid='+k+']');if(e&&d[k]>0)e.textContent=fmtDur(d[k])}}).catch(function(){})}function addCell(cid){var isRec=(''+cid).length<=12;var el=document.createElement('a');el.className='cell'+(favs[cid]?' f':'');el.href=isRec?B+'/vid/'+cid:B+'/img/'+cid;el.dataset.cid=cid;var im=document.createElement('img');im.loading='lazy';im.decoding='async';im.src=isRec?B+'/vidthumb/'+cid:B+'/thumb/'+cid;el.appendChild(im);if(isRec){var pl=document.createElement('span');pl.className='play';pl.innerHTML='►';el.appendChild(pl);var dvb=document.createElement('span');dvb.className='dur';dvb.dataset.durid=cid;el.appendChild(dvb)}el.addEventListener('click',function(e){e.preventDefault();if(selMode){toggleSel(cid,el)}else{openLb(cids.indexOf(cid))}});grid.appendChild(el)}function reset(){off=0;done=false;cids=[];gridIsRec=(filter=='recordings'||filter=='media'||filter=='videos');buildcols();st.textContent='loading...';load()}function maybeMore(){if(loading||done)return;var vh=window.innerHeight||document.documentElement.clientHeight;if(sent.getBoundingClientRect().top < vh+1400){load()}}function load(){if(loading||done)return;loading=true;fetch(B+'/api/list?'+qs()).then(function(r){if(r.status===401){location.href=B+'/login';return new Promise(function(){})}return r.json()}).then(function(j){var a=j.items||[];if(j.total!=null){countEl.textContent=j.total.toLocaleString()+(gridIsRec?(' '+filter):' images');lastTotal=j.total}if(j.building)st.textContent='search index still building...';if(!a.length){done=true;loading=false;st.textContent=cids.length?'':'nothing here';return}a.forEach(function(cid){if(gridIsRec&&hidden[cid])return;cids.push(cid);addCell(cid)});if(gridIsRec)loadDurs(a);off+=a.length;loading=false;st.textContent='';setTimeout(maybeMore,90)}).catch(function(){loading=false;st.textContent='error'})}var io=new IntersectionObserver(function(es){if(es[0].isIntersecting)maybeMore()},{rootMargin:'1400px'});io.observe(sent);window.addEventListener('scroll',maybeMore,{passive:true});window.addEventListener('resize',maybeMore,{passive:true});var t;q.addEventListener('input',function(){clearTimeout(t);t=setTimeout(reset,350)});sortsel.addEventListener('change',reset);var msel=document.getElementById('modelsel');msel.addEventListener('change',function(){model=msel.value;reset()});[].forEach.call(document.querySelectorAll('.fbtn'),function(btn){btn.addEventListener('click',function(){[].forEach.call(document.querySelectorAll('.fbtn'),function(b){b.classList.remove('on')});btn.classList.add('on');filter=btn.dataset.f;if(filter=='recordings'){msel.style.display='';if(!modelsLoaded){modelsLoaded=true;fetch(B+'/api/models').then(function(r){return r.json()}).then(function(a){a.forEach(function(o){var op=document.createElement('option');op.value=o.m;op.textContent=o.m+' ('+o.c+')';msel.appendChild(op)})})}}else{msel.style.display='none';model=''}reset()})});function jumpOff(o2){off=o2<0?0:o2;done=false;loading=false;cids=[];gridIsRec=(filter=='recordings'||filter=='media'||filter=='videos');buildcols();st.textContent='loading...';load()}if(rndb){rndb.onclick=function(){var t=lastTotal||0;if(t<1)return;var span=t-N;if(span<1)span=1;jumpOff(Math.floor(Math.random()*span))}}if(posb){posb.onchange=function(){var t=lastTotal||0;if(t<1)return;var o2=Math.floor(t*(parseInt(posb.value)||0)/100);if(o2>t-1)o2=t-1;if(o2<0)o2=0;jumpOff(o2)}}" as *u8)
2443 bo = gs_cat(body, bo, "var lb=document.createElement('div');lb.id='lb';lb.innerHTML='<div id=lbimg><img id=lbi><video id=lbv playsinline preload=auto></video><div id=spin class=spin style=display:none></div><div id=pbar style=display:none><button id=pp>►</button><span id=skw style=position:relative;flex:1;display:flex;align-items:center><input type=range min=0 value=0 id=seek style=flex:1></span><span id=seekt class=tt></span><span id=pres class=tt></span><button id=pfs title=fullscreen>⛶</button><a id=pdl href=# download title=download>⇩</a><button id=phide title=hide>🗑</button></div><div id=ibar style=display:none;position:absolute;bottom:14px;left:50%;transform:translateX(-50%);gap:16px;background:rgba(0,0,0,.62);padding:8px;border-radius:9px;z-index:6;align-items:center><a id=iorig target=_blank style=color:#fff;text-decoration:none;font-size:14px>↗ Original size</a><a id=idl download style=color:#fff;text-decoration:none;font-size:14px>⇩ Download</a><a id=iup target=_blank style=color:#fff;text-decoration:none;font-size:14px>⤢ 2× Upscale</a></div></div><div id=tagbar></div><span id=x>×</span><span class=nav id=pv>‹</span><span class=nav id=nx>›</span><div id=side><button id=favbtn>♥ Favorite</button><h2>Generation factors</h2><div id=fac></div><div id=ratewrap><h2>Rate</h2><div id=rval>500</div><input type=range min=0 max=1000 value=500 id=rng><button id=rbtn>Submit rating</button><div id=rmsg></div></div></div>';document.body.appendChild(lb);var lbi=document.getElementById('lbi'),lbv=document.getElementById('lbv'),ratewrap=document.getElementById('ratewrap'),fac=document.getElementById('fac'),rng=document.getElementById('rng'),rval=document.getElementById('rval'),rmsg=document.getElementById('rmsg'),favbtn=document.getElementById('favbtn'),pbar=document.getElementById('pbar'),tagbar=document.getElementById('tagbar'),tagcloud=document.getElementById('tagcloud'),selfab=document.getElementById('selfab'),selbar=document.getElementById('selbar'),selcount=document.getElementById('selcount'),pp=document.getElementById('pp'),seek=document.getElementById('seek'),seekt=document.getElementById('seekt'),pfs=document.getElementById('pfs'),pdl=document.getElementById('pdl'),phide=document.getElementById('phide'),pres=document.getElementById('pres'),ibar=document.getElementById('ibar'),iorig=document.getElementById('iorig'),idl=document.getElementById('idl'),iup=document.getElementById('iup'),curDur=0,seekT0=0,dragging=0,mseOn=0,mms=null,msb=null,mcid=-1,mdur=0,mfetch=0,mseekv=-1,mremoved=0,minit=0,nativeSeek=0,mseg=0,cur=-1,curRec=0;function fmt(ms){var s=Math.floor(ms/1000),m=Math.floor(s/60),h=Math.floor(m/60);s=s%60;m=m%60;function p(x){return(x<10?'0':'')+x}return(h>0?h+':'+p(m):m)+':'+p(s)}function post(u,b){return fetch(u,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:b})}function setfav(cid,on){favs[cid]=on?1:0;favbtn.classList.toggle('on',!!on);var c=grid.querySelector('.cell[data-cid=\"'+cid+'\"]');if(c)c.classList.toggle('f',!!on)}" as *u8)
2444 bo = gs_cat(body, bo, "function mseFail(cid){mseOn=0;lbv.src=B+'/vid/'+cid;lbv.load();lbv.play()}function mseFetch(tms){if(mfetch||!msb)return;mfetch=1;fetch(B+'/vid/'+mcid+'/seg?t='+Math.floor(tms)).then(function(r){return r.arrayBuffer()}).then(function(a){mfetch=0;if(msb&&!msb.updating){try{msb.appendBuffer(new Uint8Array(a))}catch(e){}}}).catch(function(){mfetch=0})}function msePump(){if(!msb||msb.updating)return;if(mseekv>=0){if(mremoved==0){mremoved=1;try{msb.remove(0,1000000000)}catch(e){}return}var s=mseekv;mseekv=-1;try{lbv.currentTime=s/1000}catch(e){}mseFetch(s);return}if(minit==0){minit=1;mseFetch(0);return}if(msb.buffered.length){var en=msb.buffered.end(msb.buffered.length-1);if((en-lbv.currentTime<12)&&(en*1000<mdur-400))mseFetch(en*1000)}}function mseStart(cid,dur,codec){mseOn=1;mcid=cid;mdur=dur;minit=0;mseekv=-1;mremoved=0;mfetch=0;try{mms=new MediaSource()}catch(e){mseFail(cid);return}lbv.src=URL.createObjectURL(mms);mms.addEventListener('sourceopen',function(){if(msb)return;try{msb=mms.addSourceBuffer('video/mp4; codecs=\"'+codec+',mp4a.40.2\"')}catch(e){mseFail(cid);return}try{mms.duration=dur/1000}catch(e){}msb.addEventListener('updateend',msePump);fetch(B+'/vid/'+cid+'/init.mp4').then(function(r){return r.arrayBuffer()}).then(function(a){if(msb&&!msb.updating){try{msb.appendBuffer(new Uint8Array(a))}catch(e){mseFail(cid)}}}).catch(function(){mseFail(cid)})});lbv.play()}function mseSeekTo(tms){if(!msb)return;mseekv=tms;mremoved=0;if(!msb.updating)msePump()}function mseCovered(t){if(!msb)return 0;for(var i=0;i<msb.buffered.length;i++){if(t>=msb.buffered.start(i)&&t<msb.buffered.end(i)-0.1)return 1}return 0}function mseSeekFetch(tms){if(!mseOn||!msb)return;var g=++mseg;var tt=Math.max(0,Math.floor(tms));var ap=function(a){if(g!=mseg||!msb)return;if(msb.updating){setTimeout(function(){ap(a)},30);return}try{msb.appendBuffer(new Uint8Array(a))}catch(e){if(e&&e.name=='QuotaExceededError'){try{msb.remove(0,Math.max(0,lbv.currentTime-20))}catch(_){}setTimeout(function(){ap(a)},60)}}};fetch(B+'/vid/'+mcid+'/seg?t='+tt).then(function(r){return r.arrayBuffer()}).then(function(a){if(g!=mseg)return;ap(a)}).catch(function(){})}function msePollForReady(cid,n){if(cids[cur]!=cid)return;fetch(B+'/vid/'+cid+'/segs').then(function(r){return r.json()}).then(function(j){if(cids[cur]!=cid)return;if(j.dur&&j.dur>0){curDur=j.dur;mdur=j.dur}if(j.ready&&j.codec){seekt.textContent='0:00'+(curDur>0?' / '+fmt(curDur):'');mseStart(cid,j.dur||curDur,j.codec);return}if(j.native){nativeSeek=1;seekt.textContent='0:00'+(curDur>0?' / '+fmt(curDur):'');lbv.src=B+'/vid/'+cid;lbv.load();lbv.play();return}if(n<40){seekt.textContent='preparing...';setTimeout(function(){msePollForReady(cid,n+1)},1200);return}nativeSeek=0;lbv.src=B+'/vid/'+cid;lbv.load();lbv.play();if(!curDur)segDurPoll(cid,0)}).catch(function(){if(cids[cur]!=cid)return;if(n<40){setTimeout(function(){msePollForReady(cid,n+1)},1500)}else{lbv.src=B+'/vid/'+cid;lbv.load();lbv.play();segDurPoll(cid,0)}})}" as *u8)
2445 bo = gs_cat(body, bo, "function vidAnalysis(cid){fetch(B+'/vid/'+cid+'/analysis').then(function(r){return r.json()}).then(function(a){if(!a||!a.label)return;var d=a.dur||1;function pc(x){return Math.round((x||0)/d*100)}fac.innerHTML+='<div class=fac><b>content</b><span>'+a.label+'</span></div><div class=fac><b>profile</b><span>'+a.scenes+' scenes / '+pc(a.active_ms)+'% active / '+pc(a.rhythmic_ms)+'% rhythmic / '+pc(a.quiet_ms)+'% quiet</span></div>'}).catch(function(){})}var daMarks=[];function daClear(){var w=document.getElementById('skw');daMarks=[];if(w){var b=w.querySelectorAll('.daband');for(var i=0;i<b.length;i++)b[i].parentNode.removeChild(b[i])}}function daRender(){var w=document.getElementById('skw');if(!w)return;var old=w.querySelectorAll('.daband');for(var i=0;i<old.length;i++)old[i].parentNode.removeChild(old[i]);if(!curDur||curDur<=0)return;for(var j=0;j<daMarks.length;j++){var m=daMarks[j];var el=document.createElement('div');el.className='daband t'+m.type;el.style.left=(m.start/curDur*100)+'%';var wp=(m.end-m.start)/curDur*100;if(m.type==2){wp=0.4}else if(wp<0.6){wp=0.6}el.style.width=wp+'%';var nm=m.type==1?'dead air':(m.type==2?'scene cut':(m.type==3?'high motion':(m.type==4?'rhythm/dance':'mark')));el.title=nm+' '+Math.round((m.end-m.start)/1000)+'s';w.appendChild(el)}}function segDurPoll(cid,n){if(cids[cur]!=cid)return;fetch(B+'/vid/'+cid+'/segs').then(function(r){return r.json()}).then(function(j){if(cids[cur]!=cid)return;if(j.dur&&j.dur>0){curDur=j.dur;mdur=j.dur;var now=mseOn?Math.floor(lbv.currentTime*1000):seekT0+Math.floor(lbv.currentTime*1000);seekt.textContent=fmt(now)+' / '+fmt(curDur)}else if(n<40){setTimeout(function(){segDurPoll(cid,n+1)},1500)}}).catch(function(){})}" as *u8)
2446 bo = gs_cat(body, bo, "function openLb(i){cur=i;var cid=cids[i];lb.classList.add('on');if(!window._lbPushed){history.pushState({lb:1},'');window._lbPushed=1}lbZreset();spin.style.display='block';rmsg.textContent='';tagbar.style.display='none';showTags(cid);curRec=(''+cid).length<=12;if(curRec){lbi.style.display='none';lbv.style.display='block';favbtn.style.display='none';ratewrap.style.display='none';side.style.display='none';pbar.style.display='flex';ibar.style.display='none';pdl.href=B+'/vid/'+cid;pp.innerHTML='❙❙';seekT0=0;seek.max=1000000;seek.value=0;curDur=0;mseOn=0;msb=null;nativeSeek=0;seekt.textContent='0:00';lbv.poster=B+'/vidthumb/'+cid;daClear();fetch(B+'/vid/'+cid+'/marks').then(function(r){return r.json()}).then(function(mk){daMarks=mk;daRender();setTimeout(daRender,2600)}).catch(function(){});fetch(B+'/vid/'+cid+'/segs').then(function(r){return r.json()}).then(function(j){curDur=j.dur||0;mdur=j.dur||0;nativeSeek=j.native?1:0;if(j.ready&&j.codec){mseStart(cid,j.dur,j.codec);seekt.textContent='0:00'+(curDur>0?' / '+fmt(curDur):'')}else if(j.native){lbv.src=B+'/vid/'+cid;lbv.load();lbv.play();seekt.textContent='0:00'+(curDur>0?' / '+fmt(curDur):'')}else{seekt.textContent='preparing...';msePollForReady(cid,0)}}).catch(function(){lbv.src=B+'/vid/'+cid;lbv.load();lbv.play();segDurPoll(cid,0)});fac.innerHTML='<div class=fac><b>recording</b><span>#'+cid+'</span></div><div class=fac><a href='+B+'/vid/'+cid+' download>download .ts</a></div>';vidAnalysis(cid)}else{lbv.pause();lbv.removeAttribute('src');lbv.style.display='none';pbar.style.display='none';lbi.style.display='block';favbtn.style.display='block';ratewrap.style.display='block';side.style.display='block';try{side.classList.toggle('up',localStorage.getItem('nsidey')==='1')}catch(_){}lbi.src=B+'/img/'+cid;iorig.href=B+'/img/'+cid;idl.href=B+'/img/'+cid;idl.download=cid+'.png';iup.href=B+'/upscale/'+cid;ibar.style.display='flex';rng.value=500;rval.textContent='500';fac.innerHTML='<div class=fac><b>loading</b></div>';post(B+'/view','img='+cid);fetch(B+'/state/'+cid).then(function(r){return r.json()}).then(function(s){setfav(cid,s.fav);if(s.rating>=0){rng.value=s.rating;rval.textContent=s.rating}});fetch(B+'/meta/'+cid).then(function(r){return r.json()}).then(function(m){var k=Object.keys(m);fac.innerHTML=k.length?k.map(function(x){return '<div class=fac><b>'+esc(x)+'</b><span>'+esc(m[x])+'</span></div>'}).join(''):'<div class=fac><b>no factors</b></div>'}).catch(function(){fac.innerHTML='<div class=fac><b>unavailable</b></div>'})}}function hide(){lb.classList.remove('on');cur=-1;lbv.pause();lbZreset();if(window._lbPushed){window._lbPushed=0;history.back()}}window.addEventListener('popstate',function(){if(lb.classList.contains('on')){window._lbPushed=0;hide()}});var _lbZ=1,_lbTX=0,_lbTY=0,_lbP=new Map(),_lbG=null;function lbZap(){lbi.style.transform=_lbZ>1?('translate('+_lbTX+'px,'+_lbTY+'px) scale('+_lbZ+')'):'';window._lbZ=_lbZ}function lbZreset(){_lbZ=1;_lbTX=0;_lbTY=0;_lbP.clear();_lbG=null;lbZap()}lbi.addEventListener('pointerdown',function(e){if(curRec)return;_lbP.set(e.pointerId,{x:e.clientX,y:e.clientY});var r=lbi.getBoundingClientRect(),ox=r.left-_lbTX,oy=r.top-_lbTY;if(_lbP.size==2){var a=Array.from(_lbP.values());_lbG={d:Math.hypot(a[0].x-a[1].x,a[0].y-a[1].y),cx:(a[0].x+a[1].x)/2,cy:(a[0].y+a[1].y)/2,z:_lbZ,tx:_lbTX,ty:_lbTY,ox:ox,oy:oy}}else if(_lbP.size==1){_lbG={x:e.clientX,y:e.clientY,tx:_lbTX,ty:_lbTY,one:1}}});lbi.addEventListener('pointermove',function(e){if(!_lbP.has(e.pointerId)||!_lbG)return;_lbP.set(e.pointerId,{x:e.clientX,y:e.clientY});if(_lbP.size==2&&_lbG.d){var a=Array.from(_lbP.values());var d=Math.hypot(a[0].x-a[1].x,a[0].y-a[1].y),cx=(a[0].x+a[1].x)/2,cy=(a[0].y+a[1].y)/2;var nz=Math.min(8,Math.max(1,_lbG.z*d/_lbG.d));_lbTX=cx-_lbG.ox-(nz/_lbG.z)*(_lbG.cx-_lbG.ox-_lbG.tx);_lbTY=cy-_lbG.oy-(nz/_lbG.z)*(_lbG.cy-_lbG.oy-_lbG.ty);_lbZ=nz;if(_lbZ<=1.001){_lbZ=1;_lbTX=0;_lbTY=0}lbZap();e.preventDefault()}else if(_lbP.size==1&&_lbG.one&&_lbZ>1){_lbTX=_lbG.tx+(e.clientX-_lbG.x);_lbTY=_lbG.ty+(e.clientY-_lbG.y);lbZap();e.preventDefault()}});function _lbUp(e){_lbP.delete(e.pointerId);if(_lbP.size==0)_lbG=null}lbi.addEventListener('pointerup',_lbUp);lbi.addEventListener('pointercancel',_lbUp);lbi.addEventListener('dblclick',function(e){if(_lbZ>1){lbZreset()}else{var r=lbi.getBoundingClientRect();_lbZ=2.5;_lbTX=(e.clientX-r.left)*(1-2.5);_lbTY=(e.clientY-r.top)*(1-2.5);lbZap()}});" as *u8)
2447 bo = gs_cat(body, bo, "var spin=document.getElementById('spin');document.getElementById('x').onclick=hide;lb.addEventListener('click',function(e){if(e.target.id=='lb'||e.target.id=='lbimg')hide()});lbi.addEventListener('load',function(){spin.style.display='none'});lbv.addEventListener('loadeddata',function(){spin.style.display='none'});lbv.addEventListener('loadedmetadata',function(){if(nativeSeek&&curDur<=0&&isFinite(lbv.duration))curDur=Math.floor(lbv.duration*1000);var h=lbv.videoHeight;pres.textContent=h?(lbv.videoWidth+'x'+h+(h>=4320?' 8K':h>=2160?' 4K':h>=1440?' 1440p':h>=1080?' 1080p':h>=720?' 720p':'')):''});lbv.addEventListener('error',function(){spin.style.display='none';if(gridIsRec&&cur>=0){fac.innerHTML+='<div class=fac><b>codec</b><span>browser cannot decode this video; if HEVC/H.265 install HEVC Video Extensions from the Microsoft Store then reload</span></div>'}});document.getElementById('pv').onclick=function(){if(cur>0)openLb(cur-1)};document.getElementById('nx').onclick=function(){if(cur<cids.length-1)openLb(cur+1)};favbtn.onclick=function(){var cid=cids[cur];var on=favs[cid]?0:1;setfav(cid,on);post(B+'/fav','img='+cid+'&v='+on)};document.getElementById('loraexp').onclick=function(){post(B+'/lora/export','').then(function(r){return r.json()}).then(function(j){alert('Exported '+j.exported+' favorite(s) to the LoRA training dataset:\\nknowledge/lora/favorites/manifest.tsv')}).catch(function(){alert('export failed')})};rng.oninput=function(){rval.textContent=rng.value};seek.oninput=function(){dragging=1;var f=parseInt(seek.value)||0;var ms=curDur>0?Math.floor(f/1000000*curDur):0;seekt.textContent=fmt(ms)+(curDur>0?' / '+fmt(curDur):'')};seek.onchange=function(){dragging=0;if(cur<0||!curRec)return;var f=parseInt(seek.value)||0;if(mseOn){var _w=curDur>0?Math.floor(f/1000000*curDur):0;try{lbv.currentTime=_w/1000}catch(e){}return}if(nativeSeek){var nd=lbv.duration;if(!nd||!isFinite(nd))nd=curDur/1000;if(nd>0)lbv.currentTime=f/1000000*nd;return}var cid=cids[cur];seekT0=curDur>0?Math.floor(f/1000000*curDur):0;spin.style.display='block';lbv.src=B+'/vid/'+cid+'?b='+f;lbv.load();lbv.play()};" as *u8)
2448 bo = gs_cat(body, bo, "lbv.addEventListener('seeking',function(){if(!mseOn||!msb)return;var t=lbv.currentTime;if(mseCovered(t))return;mseSeekFetch(t*1000)});lbv.addEventListener('timeupdate',function(){if(dragging||!curRec)return;if(mseOn){if(mdur>0){var mt=Math.floor(lbv.currentTime*1000);seek.value=Math.floor(mt/mdur*1000000);seekt.textContent=fmt(mt)+' / '+fmt(mdur)}if(msb&&!msb.updating&&msb.buffered.length){var en=msb.buffered.end(msb.buffered.length-1);if((en-lbv.currentTime<10)&&(en*1000<mdur-400))mseFetch(en*1000)}return}var ms=seekT0+Math.floor(lbv.currentTime*1000);if(curDur>0){seek.value=Math.floor(ms/curDur*1000000);seekt.textContent=fmt(ms)+' / '+fmt(curDur)}else{seekt.textContent=fmt(ms)}});pp.onclick=function(){if(lbv.paused)lbv.play();else lbv.pause()};lbv.addEventListener('play',function(){pp.innerHTML='❙❙'});lbv.addEventListener('pause',function(){pp.innerHTML='►'});pfs.onclick=function(){if(lbv.requestFullscreen)lbv.requestFullscreen();else if(lbv.webkitEnterFullscreen)lbv.webkitEnterFullscreen()};phide.onclick=function(){if(cur<0)return;var cid=cids[cur];post(B+'/hide','id='+cid+'&v=1');hidden[cid]=1;var c=grid.querySelector('.cell[data-cid=\"'+cid+'\"]');if(c&&c.parentNode)c.parentNode.removeChild(c);var ix=cids.indexOf(cid);if(ix>=0)cids.splice(ix,1);hide()};lbv.addEventListener('click',function(){if(lbv.paused)lbv.play();else lbv.pause()});document.getElementById('rbtn').onclick=function(){var cid=cids[cur];post(B+'/rate','img='+cid+'&score='+rng.value).then(function(r){rmsg.textContent=r.ok?'saved '+rng.value:'error'})};document.addEventListener('keydown',function(e){if(cur<0)return;if(e.key=='Escape')hide();else if(e.key=='ArrowLeft'&&cur>0)openLb(cur-1);else if(e.key=='ArrowRight'&&cur<cids.length-1)openLb(cur+1);else if(e.key=='f'){favbtn.onclick()}});var lbimg=document.getElementById('lbimg'),side=document.getElementById('side');var _sx=0,_sy=0,_st=0,_sw=0;function _nb(){[cids[cur-1],cids[cur+1]].forEach(function(c){if(c!=null){var i=new Image();i.src=((''+c).length<=12)?B+'/vidthumb/'+c:B+'/img/'+c}})}lbimg.addEventListener('touchstart',function(e){var g=e.target.tagName;if(g=='INPUT'||g=='BUTTON'){_sw=0;return}if(e.touches.length>1||_lbZ>1){_sw=0;return}_sw=1;var t=e.touches[0];_sx=t.clientX;_sy=t.clientY;_st=Date.now()},{passive:true});lbimg.addEventListener('touchend',function(e){if(!_sw)return;var t=e.changedTouches[0],dx=t.clientX-_sx,dy=t.clientY-_sy;if(Date.now()-_st<700&&Math.abs(dx)>46&&Math.abs(dx)>Math.abs(dy)*1.3){if(dx<0&&cur<cids.length-1){openLb(cur+1);_nb()}else if(dx>0&&cur>0){openLb(cur-1);_nb()}}},{passive:true});side.addEventListener('click',function(e){var r=side.getBoundingClientRect();if(e.clientY-r.top<44){side.classList.toggle('up');try{localStorage.setItem('nsidey',side.classList.contains('up')?'1':'0')}catch(_){}}});fetch(B+'/api/hidden').then(function(r){return r.json()}).then(function(j){(j.hidden||[]).forEach(function(x){hidden[x]=1});reset()}).catch(function(){reset()})})();</script></body></html>" as *u8)
2449 var o: i64 = 0
2450 o = gs_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nCache-Control: no-store, must-revalidate\r\nConnection: close\r\nContent-Length: " as *u8)
2451 o = gs_u(rbuf, o, bo)
2452 o = gs_cat(rbuf, o, "\r\n\r\n" as *u8)
2453 var i: i64 = 0
2454 while i < bo { rbuf[o] = body[i]; o = o + 1; i = i + 1 }
2455 return o
2456}
2457
2458// R2: spawn the duration-index batch (nx_galx_durindex -> galx_dur.raw) DETACHED at boot so runtime-sort
2459// populates without blocking the serve. Double-fork + nx_setsid: the grandchild reparents to init (no zombie
2460// here, survives gallery restarts/SSH close). The durindex flock-guards itself + is resumable, so re-spawning
2461// each boot is safe (a 2nd instance just exits; a complete one scans + exits fast). Inherits cwd /volume1/ai/galx
2462// (the elfs + knowledge/status/ data live there). Spawn failure is isolated -> serve is unaffected.
2463func gs_spawn_durindex() -> i64 {
2464 let pid: i64 = sys_fork()
2465 if pid == 0 {
2466 let gpid: i64 = sys_fork()
2467 if gpid == 0 {
2468 nx_setsid()
2469 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
2470 let lg: i64 = sys_openat_append("galx_durindex.log" as *u8, 0x1a4)
2471 if dn >= 0 { sys_dup3(dn, 0, 0) }
2472 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0) }
2473 nx_chmod("./nx_galx_durindex.elf" as *u8, 0x1ed) // recv lands files 0644 -> make 0755 (+x) before exec
2474 nx_chmod("./nx_ts_dur.elf" as *u8, 0x1ed) // the durindex execve's this per video, so it too needs +x
2475 let av: *i64 = sys_mmap(64) as *i64
2476 av[0] = "./nx_galx_durindex.elf" as *u8 as i64
2477 av[1] = "knowledge/status/galx_vid_paths.tsv" as *u8 as i64
2478 av[2] = "./nx_ts_dur.elf" as *u8 as i64
2479 av[3] = "knowledge/status/galx_dur.raw" as *u8 as i64
2480 av[4] = 0
2481 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
2482 sys_execve_clean("./nx_galx_durindex.elf" as *u8, av, envp)
2483 sys_exit(127)
2484 }
2485 sys_exit(0)
2486 }
2487 let st: *i64 = sys_mmap(16) as *i64
2488 sys_wait4(pid, st, 0)
2489 return pid
2490}
2491func main(argc: i64, argv: *i64) -> i64 {
2492 // port override via argv[1] (additive: no args -> production's 18090; a test instance can bind e.g. 18091)
2493 var port: i64 = VIEW_MAGIC_18090
2494 if argc >= 2 { var pv: i64 = 0; var pi: i64 = 0; let pa: *u8 = argv[1] as *u8; while pa[pi] != (0 as u8) { if pa[pi] >= (48 as u8) { if pa[pi] <= (57 as u8) { pv = pv * 10 + ((pa[pi] as i64) - 48) } } pi = pi + 1 } if pv > 0 { port = pv } }
2495 let addr: *u8 = sys_mmap(16)
2496 addr[0]=2 as u8; addr[1]=0 as u8; addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
2497 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
2498 addr[8]=0 as u8; addr[9]=0 as u8; addr[10]=0 as u8; addr[11]=0 as u8; addr[12]=0 as u8; addr[13]=0 as u8; addr[14]=0 as u8; addr[15]=0 as u8
2499 let lfd: i64 = sys_socket(2, 1 | 0x80000, 0) // SOCK_CLOEXEC: no exec'd child (per-request remuxers, any batch) inherits the :VIEW_MAGIC_18090 listener -> never a held-port crash-loop
2500 if lfd < 0 { return 10 }
2501 let optval: *u8 = sys_mmap(4); optval[0]=1 as u8; optval[1]=0 as u8; optval[2]=0 as u8; optval[3]=0 as u8
2502 sys_setsockopt(lfd, 1, 2, optval, 4)
2503 if sys_bind(lfd, addr, 16) < 0 { return 20 }
2504 if sys_listen(lfd, 64) < 0 { return 30 }
2505 gs_vidoff_build() // build the O(1) id->path offset index once at startup (gs_vid_line uses it; full-scan fallback if stale)
2506 let cbpid: i64 = sys_fork() // R4: build the per-category (r/m/v) line indexes in a CHILD so its big reads don't persist in the long-lived parent VA
2507 if cbpid == 0 { gs_catidx_build(); sys_exit(0) }
2508 let cbst: *i64 = sys_mmap(16) as *i64
2509 sys_wait4(cbpid, cbst, 0)
2510 // NOTE: the duration-index batch is NOT boot-spawned (it was destabilising the serve on the NAS); re-trigger
2511 // it as a one-shot instead. gs_spawn_durindex() remains available for a deliberate, supervised kick.
2512 // SOVEREIGN O(1) cid->path index: (re)build in a CHILD (the 34MB TSV read stays out of the long-lived parent VA), then load it.
2513 __syscall(83, "knowledge/index" as *u8 as i64, 493, 0, 0, 0, 0) // mkdir knowledge/index (idempotent)
2514 // CONDITIONAL rebuild. This used to run on EVERY boot, which made idx+blob a derived cache of
2515 // the TSV rather than a store -- any native insert was erased by the next restart, so the
2516 // "retired" TSV stayed load-bearing forever. Now the TSV's mtime decides: newer than the index
2517 // -> rebuild (the legacy path is untouched); older -> keep, so native writes survive.
2518 if gs_cidindex_stale("knowledge/status/galx_cid_paths.tsv" as *u8, "knowledge/index/galx_cid.idx" as *u8, "knowledge/index/galx_cid.blob" as *u8) == 1 {
2519 let sm: *u8 = "gallery: cid index STALE vs galx_cid_paths.tsv -> rebuilding
2520" as *u8; sys_write(2, sm, gs_strlen(sm))
2521 let xbpid: i64 = sys_fork()
2522 if xbpid == 0 { gs_cidindex_build_preserving("knowledge/status/galx_cid_paths.tsv" as *u8, "knowledge/index/galx_cid.idx" as *u8, "knowledge/index/galx_cid.blob" as *u8); sys_exit(0) }
2523 let xbst: *i64 = sys_mmap(16) as *i64
2524 sys_wait4(xbpid, xbst, 0)
2525 } else {
2526 let km: *u8 = "gallery: cid index is CURRENT -> kept (native inserts preserved, no 34MB reparse)
2527" as *u8; sys_write(2, km, gs_strlen(km))
2528 }
2529 let cidx: *NxCidx = sys_mmap(64) as *NxCidx
2530 cidx.ready = 0
2531 let cixp: *i64 = sys_mmap(16) as *i64; let cixi: *u8 = sys_read_file("knowledge/index/galx_cid.idx" as *u8, cixp)
2532 if (cixi as i64) != 0 {
2533 let cbxp: *i64 = sys_mmap(16) as *i64; let cixb: *u8 = sys_read_file("knowledge/index/galx_cid.blob" as *u8, cbxp)
2534 if (cixb as i64) != 0 { cidx.idx = cixi; cidx.blob = cixb; cidx.nb = cidx_rd64(cixi, 0); cidx.ready = 1
2535 let cm: *u8 = "gallery: sovereign O(1) cid->path index LOADED (no per-request TSV scan)\n" as *u8; sys_write(2, cm, gs_strlen(cm)) }
2536 }
2537 if cidx.ready == 0 { let cm2: *u8 = "gallery: cid index absent -> legacy TSV scan fallback\n" as *u8; sys_write(2, cm2, gs_strlen(cm2)) }
2538 // SELF-CHECK the WHOLE shell at startup (fail-LOUD in the log; never crashes -> never-brick). No JS engine here,
2539 // so this is the only automatic catch for a broken shell: JS SyntaxError, truncation, or unbalanced CSS. The
2540 // pre-deploy gate nx_galx_shell_jslint_gate is the PRIMARY guard; this is the runtime net. 256KB buffer >> shell.
2541 let shbuf: *u8 = sys_mmap(VIEW_MAGIC_262144)
2542 let shlen: i64 = gs_shell(shbuf)
2543 let sjp: *i64 = sys_mmap(16) as *i64
2544 let sjl: i64 = jslint_find_script(shbuf, shlen, sjp)
2545 var shok: i64 = 1
2546 if sjl < 0 { shok = 0; let lmn: *u8 = "gallery: shell self-check: NO inline <script>\n" as *u8; sys_write(2, lmn, gs_strlen(lmn)) }
2547 if sjl >= 0 { if jslint_scan_script(((shbuf as i64) + sjp[0]) as *u8, sjl) >= 0 { shok = 0; let lmf: *u8 = "gallery: *** SHELL JS INVALID (broken token in inline script) ***\n" as *u8; sys_write(2, lmf, gs_strlen(lmf)) } }
2548 if shell_complete(shbuf, shlen) == 0 { shok = 0; let lmc: *u8 = "gallery: *** SHELL INCOMPLETE (not <!doctype..</html> -- truncated?) ***\n" as *u8; sys_write(2, lmc, gs_strlen(lmc)) }
2549 if shell_style_balanced(shbuf, shlen) == 0 { shok = 0; let lms: *u8 = "gallery: *** SHELL CSS BRACES UNBALANCED ***\n" as *u8; sys_write(2, lms, gs_strlen(lms)) }
2550 if shok == 1 { let lmo: *u8 = "gallery: shell self-check OK (JS parses + page complete + CSS balanced)\n" as *u8; sys_write(2, lmo, gs_strlen(lmo)) }
2551 // load the durable BM25 search index ONCE in the parent; request-children inherit it via fork COW.
2552 let gal: *NxGalIdx = sys_mmap(64) as *NxGalIdx
2553 if gs_galidx_load(gal, "knowledge/index/gallery.idx" as *u8, "knowledge/index/gallery.manifest" as *u8) == 1 {
2554 let m1: *u8 = "gallery: BM25 ranked q= search ENABLED (durable index loaded)\n" as *u8; sys_write(2, m1, gs_strlen(m1))
2555 } else {
2556 let m2: *u8 = "gallery: no/stale index -> legacy substring q= fallback\n" as *u8; sys_write(2, m2, gs_strlen(m2))
2557 }
2558 let req: *u8 = sys_mmap(VIEW_MAGIC_16384)
2559 let reapst: *i64 = sys_mmap(16) as *i64
2560 while 1 == 1 {
2561 let cfd: i64 = sys_accept(lfd)
2562 // DoS-starvation bound (nx_dos_timeout_scan seq321): one peer declaring a body it never finishes
2563 // sending would otherwise starve this accept loop forever. ACCEPT_TMO_S is the shared named bound
2564 // and fires only on ZERO progress, so a client streaming a large image is unaffected.
2565 // NOTE: this daemon is LIVE on :18090 -- the source fix lands on the next /api/deploy, not now.
2566 if cfd >= 0 { sys_set_socket_timeout(cfd, ACCEPT_TMO_S) }
2567 if cfd < 0 { continue }
2568 let pid: i64 = sys_fork()
2569 if pid == 0 {
2570 sys_close(lfd)
2571 let rn: i64 = sys_read(cfd, req, VIEW_MAGIC_16384)
2572 if eh_find(req, rn, "GET /vid/" as *u8, 9) == 1 { gs_vid_stream(cfd, req, rn); sys_close(cfd); sys_exit(0) }
2573 var rbuf: *u8 = sys_mmap(VIEW_MAGIC_4194304)
2574 var rlen: i64 = 0
2575 if eh_find(req, rn, "GET /api/models" as *u8, 15) == 1 { rlen = gs_models(rbuf) }
2576 if rlen == 0 { if eh_find(req, rn, "GET /api/suggest" as *u8, 16) == 1 { rlen = gs_api_suggest(rbuf, req, rn) } }
2577 if rlen == 0 { if eh_find(req, rn, "GET /api/hidden" as *u8, 15) == 1 { rlen = gs_hidden_list(rbuf) } }
2578 if rlen == 0 { if eh_find(req, rn, "GET /api/durs" as *u8, 13) == 1 { rlen = gs_durs_batch(rbuf, req, rn) } }
2579 if rlen == 0 { if eh_find(req, rn, "GET /api/alltags" as *u8, 16) == 1 { rlen = gs_alltags_get(rbuf, req, rn) } }
2580 if rlen == 0 { if eh_find(req, rn, "GET /api/tagged" as *u8, 15) == 1 { rlen = gs_tagged_get(rbuf, req, rn) } }
2581 if rlen == 0 { if eh_find(req, rn, "GET /api/tags" as *u8, 13) == 1 { rlen = gs_tags_get(rbuf, req, rn) } }
2582 if rlen == 0 { if eh_find(req, rn, "GET /api/list" as *u8, 13) == 1 { rlen = gs_api_list(rbuf, req, rn, gal) } }
2583 if rlen == 0 { if eh_find(req, rn, "GET /api/4chan/img/" as *u8, 19) == 1 { rlen = gs_4chan_img(rbuf, req, rn) } }
2584 // ORDER IS LOAD-BEARING: ingest_status is tested BEFORE the generic /api/4chan/ route,
2585 // which would otherwise hand "ingest_status" to the adapter as a VERB and answer
2586 // "unknown verb" -- a routing bug that reads to the caller as a broken endpoint.
2587 if rlen == 0 { if eh_find(req, rn, "GET /api/4chan/panel/" as *u8, 21) == 1 { rlen = gs_4chan_panel(rbuf, req, rn) } }
2588 if rlen == 0 { if eh_find(req, rn, "GET /api/4chan/ingest_status/" as *u8, 29) == 1 { rlen = gs_4chan_ingest_status(rbuf, req, rn) } }
2589 if rlen == 0 { if eh_find(req, rn, "POST /api/4chan/ingest" as *u8, 22) == 1 { rlen = gs_4chan_ingest(rbuf, req, rn) } }
2590 if rlen == 0 { if eh_find(req, rn, "GET /api/4chan/" as *u8, 15) == 1 { rlen = gs_4chan(rbuf, req, rn) } }
2591 if rlen == 0 { if eh_find(req, rn, "GET /img/" as *u8, 9) == 1 { rlen = gs_serve_png(rbuf, req, rn, cidx) } }
2592 if rlen == 0 { if eh_find(req, rn, "GET /thumb/" as *u8, 11) == 1 { rlen = gs_thumb(rbuf, req, rn, cidx) } }
2593 if rlen == 0 { if eh_find(req, rn, "GET /upscale/" as *u8, 13) == 1 { rlen = gs_upscale(rbuf, req, rn, cidx) } }
2594 if rlen == 0 { if eh_find(req, rn, "GET /vidthumb/" as *u8, 14) == 1 { rlen = gs_vidthumb(rbuf, req, rn) } }
2595 if rlen == 0 { if eh_find(req, rn, "GET /meta/" as *u8, 10) == 1 { rlen = gs_meta(rbuf, req, rn, cidx) } }
2596 if rlen == 0 { if eh_find(req, rn, "GET /state/" as *u8, 11) == 1 { rlen = gs_state(rbuf, req, rn) } }
2597 if rlen == 0 { if eh_find(req, rn, "POST /view" as *u8, 10) == 1 { rlen = gs_view(rbuf, req, rn) } }
2598 if rlen == 0 { if eh_find(req, rn, "POST /telemetry" as *u8, 15) == 1 { rlen = gs_telemetry(rbuf, req, rn) } }
2599 if rlen == 0 { if eh_find(req, rn, "POST /fav" as *u8, 9) == 1 { rlen = gs_fav(rbuf, req, rn) } }
2600 if rlen == 0 { if eh_find(req, rn, "POST /hide" as *u8, 10) == 1 { rlen = gs_hide(rbuf, req, rn) } }
2601 if rlen == 0 { if eh_find(req, rn, "POST /tag" as *u8, 9) == 1 { rlen = gs_tag(rbuf, req, rn) } }
2602 if rlen == 0 { if eh_find(req, rn, "POST /rate" as *u8, 10) == 1 { rlen = gs_rate(rbuf, req, rn) } }
2603 if rlen == 0 { if eh_find(req, rn, "POST /lora/export" as *u8, 17) == 1 { rlen = gs_lora_export(rbuf, req, rn) } }
2604 if rlen == 0 { if eh_find(req, rn, "GET / " as *u8, 6) == 1 { rlen = gs_shell(rbuf) } }
2605 if rlen == 0 { if eh_find(req, rn, "GET /?" as *u8, 6) == 1 { rlen = gs_shell(rbuf) } }
2606 if rlen == 0 { rlen = gs_404(rbuf) }
2607 sys_write(cfd, rbuf, rlen)
2608 sys_close(cfd)
2609 sys_exit(0)
2610 }
2611 sys_close(cfd)
2612 var reaped: i64 = 1
2613 while reaped > 0 { reaped = sys_wait4(0 - 1, reapst, 1) }
2614 }
2615 sys_close(lfd)
2616 return 0
2617}