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>&#128444; 4chan</button><button class=fbtn data-f=gens>Gens</button><button class=fbtn data-f=fav>&#9829; Favorites</button><button class=fbtn data-f=rated>Rated</button><button class=fbtn data-f=viewed>Viewed</button><button class=fbtn data-f=recordings>&#9658; Recordings</button><button class=fbtn data-f=media>&#128250; Media</button><button class=fbtn data-f=videos>&#127916; 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'>&#9829;&#8594;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&#8594;Z</option><option value=nza>Name Z&#8594;A</option></select><button id=rnd class=fbtn title='Jump to a random spot in the library'>&#127922; 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)'>&#128205;<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>&#127991;</button><div id=tagcloud></div><button id=selfab onclick=toggleSelMode() title=Select>&#9745;</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=='<'?'&lt;':c=='>'?'&gt;':'&amp;'})}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>&times;</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>&#8249;</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>&#8250;</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=='<'?'&lt;':c=='>'?'&gt;':'&amp;'})}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>&times;</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='&#9658;';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>&#9658;</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>&#9974;</button><a id=pdl href=# download title=download>&#8681;</a><button id=phide title=hide>&#128465;</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>&#8599; Original size</a><a id=idl download style=color:#fff;text-decoration:none;font-size:14px>&#8681; Download</a><a id=iup target=_blank style=color:#fff;text-decoration:none;font-size:14px>&#10530; 2&#215; Upscale</a></div></div><div id=tagbar></div><span id=x>&times;</span><span class=nav id=pv>&#8249;</span><span class=nav id=nx>&#8250;</span><div id=side><button id=favbtn>&#9829; 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='&#10073;&#10073;';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='&#10073;&#10073;'});lbv.addEventListener('pause',function(){pp.innerHTML='&#9658;'});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}