code wiki / _hdl_build / nx_kvs.nx

nx_kvs.nx source

↩ module page · 226 lines · 12680 B

1// nx_kvs.nx -- KVS / kt_player URL de-obfuscator. A huge share of "legal personal download" tube sites run the 2// Kernel Video Sharing CMS, which hides the real .mp4 behind an obfuscated flashvars field: 3// video_url: 'function/0/https://host/get_file/3/<SCRAMBLED-32-hex>/aaa/bbb/name_1080p.mp4/' 4// license_code: '$<digits>' 5// The player's kt_player JS un-scrambles the 32-char hash at runtime using license_code -- which is why the real 6// URL only appears in the browser Network tab and our static harvester correctly skips the invalid string. This 7// module reproduces that de-obfuscation EXACTLY (ported verbatim from yt-dlp GenericIE._kvs_get_real_url / 8// _kvs_get_license_token) so we recover the direct URL ourselves. Faithful port -- verified by nx_kvs_gate against 9// a hand-computed token vector + the real bitchesgirls page. license_tier: ORIGINAL 10import "nx_syscalls.nx" 11const K_MAGIC_10000: i64 = 10000 12const K_MAGIC_4096: i64 = 4096 13 14func kvs_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 15func kvs_find(hay: *u8, hl: i64, needle: *u8, nl: i64, from: i64) -> i64 { 16 if nl==0 { return from } 17 var i: i64=from; let last: i64=hl-nl 18 while i<=last { var j: i64=0; var m: i64=1; while j<nl { if (hay[i+j]&0xff)!=(needle[j]&0xff){m=0;j=nl} else {j=j+1} } if m==1 {return i} i=i+1 } 19 return 0-1 20} 21func kvs_starts(s: *u8, slen: i64, pre: *u8, prelen: i64) -> i64 { 22 if slen<prelen { return 0 } 23 var i: i64=0; while i<prelen { if (s[i]&0xff)!=(pre[i]&0xff) { return 0 } i=i+1 } return 1 24} 25func kvs_next_slash(s: *u8, slen: i64, from: i64) -> i64 { 26 var i: i64=from; while i<slen { if (s[i]&0xff)==47 { return i } i=i+1 } return 0-1 27} 28 29// _kvs_get_license_token: license_code -> 32-entry token array (into tok). Returns the token count (32 when valid). 30// Verbatim: strip '$'; values=digits; modlicense=digits with '0'->'1'; center=len//2; front=int(ml[:center+1]); 31// back=int(ml[center:]); ml=str(4*abs(front-back))[:center+1]; token = [(values[i+off]+ml_digit_i)%10] for i,off. 32func kvs_license_token(lic: *u8, llen: i64, tok: *i64) -> i64 { 33 let lv: *i64 = sys_mmap(8*64) as *i64 // license_values (digit values) 34 let ml: *u8 = sys_mmap(64) // modlicense (digit chars, '0'->'1') 35 var n: i64 = 0 36 var i: i64 = 0 37 while i < llen { 38 let c: i64 = lic[i]&0xff 39 if c >= 48 { if c <= 57 { 40 lv[n] = c - 48 41 if c == 48 { ml[n] = 49 as u8 } else { ml[n] = c as u8 } 42 n = n + 1 43 } } 44 i = i + 1 45 } 46 var z: i64 = 0; while z < 32 { tok[z] = 0; z = z + 1 } // zero-fill (fallback if short) 47 if n == 0 { return 0 } 48 let center: i64 = n / 2 49 var fronthalf: i64 = 0 50 var j: i64 = 0 51 while j <= center { fronthalf = fronthalf*10 + ((ml[j]&0xff) - 48); j = j + 1 } // ml[:center+1] 52 var backhalf: i64 = 0 53 j = center 54 while j < n { backhalf = backhalf*10 + ((ml[j]&0xff) - 48); j = j + 1 } // ml[center:] 55 var diff: i64 = fronthalf - backhalf 56 if diff < 0 { diff = 0 - diff } 57 let val: i64 = 4 * diff 58 // stringify val 59 let vs: *u8 = sys_mmap(64) 60 var vk: i64 = 0 61 if val == 0 { vs[0] = 48 as u8; vk = 1 } else { 62 let t: *u8 = sys_mmap(64); var m: i64 = val; var tk: i64 = 0 63 while m > 0 { t[tk] = (48 + (m%10)) as u8; m = m/10; tk = tk + 1 } 64 var w: i64 = 0; while w < tk { vs[w] = t[tk-1-w]; w = w + 1 } vk = tk 65 } 66 var mlen: i64 = center + 1 67 if mlen > vk { mlen = vk } // str(...)[:center+1] (short-string safe) 68 var ti: i64 = 0 69 var idx: i64 = 0 70 while idx < mlen { 71 let current: i64 = (vs[idx]&0xff) - 48 72 var off: i64 = 0 73 while off < 4 { 74 let li: i64 = idx + off 75 var lvv: i64 = 0 76 if li < n { lvv = lv[li] } 77 tok[ti] = (lvv + current) % 10 78 ti = ti + 1 79 off = off + 1 80 } 81 idx = idx + 1 82 } 83 return ti 84} 85 86// _kvs_get_real_url: de-obfuscate a 'function/0/...' video_url using license_code. Non-obfuscated urls copy through. 87// The 32-char hash at path segment [3] (i.e. after /get_file/3/) is permuted by the license token's swap schedule. 88func kvs_real_url(vurl: *u8, vlen: i64, lic: *u8, llen: i64, out: *u8, cap: i64) -> i64 { 89 if kvs_starts(vurl, vlen, "function/0/" as *u8, 11) == 0 { 90 var a: i64=0; while a<vlen { if a<(cap-1) { out[a]=vurl[a] } a=a+1 } out[vlen]=0 as u8; return vlen 91 } 92 let inner: *u8 = ((vurl as i64) + 11) as *u8 93 let ilen: i64 = vlen - 11 94 let sp: i64 = kvs_find(inner, ilen, "://" as *u8, 3, 0) 95 if sp < 0 { return 0-1 } 96 let s1: i64 = kvs_next_slash(inner, ilen, sp + 3) // path slash #1 (start of path) 97 if s1 < 0 { return 0-2 } 98 let s2: i64 = kvs_next_slash(inner, ilen, s1 + 1) // #2 (after get_file) 99 if s2 < 0 { return 0-3 } 100 let s3: i64 = kvs_next_slash(inner, ilen, s2 + 1) // #3 (after the '3') 101 if s3 < 0 { return 0-4 } 102 let s4: i64 = kvs_next_slash(inner, ilen, s3 + 1) // #4 (end of hash segment) 103 if s4 < 0 { return 0-5 } 104 let hstart: i64 = s3 + 1 105 if (s4 - hstart) < 32 { return 0-6 } 106 let tok: *i64 = sys_mmap(8*32) as *i64 107 kvs_license_token(lic, llen, tok) 108 let idxs: *i64 = sys_mmap(8*32) as *i64 109 var k: i64 = 0; while k < 32 { idxs[k] = k; k = k + 1 } 110 var accum: i64 = 0 111 var src: i64 = 31 112 while src >= 0 { 113 accum = accum + tok[src] 114 let dest: i64 = (src + accum) % 32 115 let tmp: i64 = idxs[src]; idxs[src] = idxs[dest]; idxs[dest] = tmp 116 src = src - 1 117 } 118 let nh: *u8 = sys_mmap(40) 119 var h: i64 = 0; while h < 32 { nh[h] = inner[hstart + idxs[h]]; h = h + 1 } 120 // rebuild: inner[0..hstart) + permuted-hash(32) + inner[hstart+32..ilen) 121 var o: i64 = 0 122 var a: i64 = 0 123 while a < hstart { if o<(cap-1) { out[o]=inner[a]; o=o+1 } a=a+1 } 124 var b: i64 = 0 125 while b < 32 { if o<(cap-1) { out[o]=nh[b]; o=o+1 } b=b+1 } 126 var c: i64 = hstart + 32 127 while c < ilen { if o<(cap-1) { out[o]=inner[c]; o=o+1 } c=c+1 } 128 out[o]=0 as u8 129 return o 130} 131 132// value of a single-quoted flashvars field: find `key`, then copy from the next ' to the closing '. 0 if absent. 133func kvs_qval(html: *u8, hlen: i64, key: *u8, out: *u8, cap: i64) -> i64 { 134 let kl: i64 = kvs_slen(key) 135 let p: i64 = kvs_find(html, hlen, key, kl, 0) 136 if p < 0 { out[0]=0 as u8; return 0 } 137 var i: i64 = p + kl 138 var q: i64 = 0 - 1 139 while i < hlen { if (html[i]&0xff)==39 { q = i; i = hlen } else { i = i + 1 } } 140 if q < 0 { out[0]=0 as u8; return 0 } 141 var o: i64 = 0 142 var j: i64 = q + 1 143 while j < hlen { let c: i64 = html[j]&0xff; if c==39 { j = hlen } else { if o < (cap-1) { out[o]=c as u8; o=o+1 } j = j + 1 } } 144 out[o]=0 as u8 145 return o 146} 147 148// resolution score = the PARSED pixel HEIGHT from a string (URL or label). No hardcoded ladder, so 4320p (8K), 149// 2160p (4K), 1440p, or any future value all rank correctly. Recognizes "<H>p" (guarded to a sane video height 150// 100..10000 so it ignores IDs/params), "<W>x<H>" (takes the height), and small "<N>k/K" UHD aliases (height = 151// N*540: 4K->2160, 8K->4320). Returns 1 for "present but no resolution found" (still a valid fallback candidate). 152func kvs_res_score(s: *u8, slen: i64) -> i64 { 153 var best: i64 = 0 154 var i: i64 = 0 155 while i < slen { 156 let c: i64 = s[i]&0xff 157 var startrun: i64 = 0 158 if c >= 48 { if c <= 57 { 159 if i == 0 { startrun = 1 } else { let pc: i64 = s[i-1]&0xff; var pd: i64=0; if pc>=48 { if pc<=57 { pd=1 } } if pd==0 { startrun = 1 } } 160 } } 161 if startrun == 1 { 162 var v: i64 = 0 163 var j: i64 = i 164 var run: i64 = 1 165 while run == 1 { if j>=slen { run=0 } else { let d: i64=s[j]&0xff; if d>=48 { if d<=57 { v=v*10+(d-48); j=j+1 } else { run=0 } } else { run=0 } } } 166 var term: i64 = 0 167 if j < slen { term = s[j]&0xff } 168 if term == 112 { if v >= 100 { if v <= K_MAGIC_10000 { if v > best { best = v } } } } // 'p' 169 if term == 80 { if v >= 100 { if v <= K_MAGIC_10000 { if v > best { best = v } } } } // 'P' 170 if term == 120 { var v2: i64=0; var k: i64=j+1; var r2: i64=1; while r2==1 { if k>=slen { r2=0 } else { let d: i64=s[k]&0xff; if d>=48 { if d<=57 { v2=v2*10+(d-48); k=k+1 } else { r2=0 } } else { r2=0 } } } if v2>=100 { if v2<=K_MAGIC_10000 { if v2>best { best=v2 } } } } // 'x': WxH -> take the H 171 if term == 88 { var v2: i64=0; var k: i64=j+1; var r2: i64=1; while r2==1 { if k>=slen { r2=0 } else { let d: i64=s[k]&0xff; if d>=48 { if d<=57 { v2=v2*10+(d-48); k=k+1 } else { r2=0 } } else { r2=0 } } } if v2>=100 { if v2<=K_MAGIC_10000 { if v2>best { best=v2 } } } } // 'X' 172 if term == 107 { if v >= 1 { if v <= 16 { let hh: i64=v*540; if hh > best { best = hh } } } } // 'k' (4K/8K...) 173 if term == 75 { if v >= 1 { if v <= 16 { let hh: i64=v*540; if hh > best { best = hh } } } } // 'K' 174 i = j 175 } else { i = i + 1 } 176 } 177 if best == 0 { return 1 } 178 return best 179} 180// pull one flashvars field, de-obfuscate it -> out. Returns len (0 if the field is absent). 181func kvs_try_field(html: *u8, hlen: i64, field: *u8, lic: *u8, llen: i64, out: *u8, cap: i64) -> i64 { 182 let raw: *u8 = sys_mmap(K_MAGIC_4096) 183 let rl: i64 = kvs_qval(html, hlen, field, raw, K_MAGIC_4096) 184 if rl <= 0 { out[0]=0 as u8; return 0 } 185 return kvs_real_url(raw, rl, lic, llen, out, cap) 186} 187// resolution the site LABELS a field with (e.g. video_url_text: '2160p' or '4K') -- used when the filename itself 188// carries no _Np. Builds "<field>_text" and scores its value. 0 if no label. 189func kvs_label_score(html: *u8, hlen: i64, field: *u8) -> i64 { 190 let nm: *u8 = sys_mmap(64) 191 var o: i64 = 0 192 var i: i64 = 0; while field[i]!=(0 as u8) { nm[o]=field[i]; o=o+1; i=i+1 } 193 nm[o]=95 as u8; o=o+1 // '_' 194 let sfx: *u8 = "text" as *u8; var k: i64=0; while sfx[k]!=(0 as u8) { nm[o]=sfx[k]; o=o+1; k=k+1 } 195 nm[o]=0 as u8 196 let lab: *u8 = sys_mmap(64) 197 let ll: i64 = kvs_qval(html, hlen, nm, lab, 64) 198 if ll <= 0 { return 0 } 199 return kvs_res_score(lab, ll) 200} 201// progressive (directly playable in native <video>)? -- reject a manifest that snuck into video_url, since we 202// can't play/assemble HLS/DASH yet (the assembler will handle those on its own path). 203func kvs_is_prog(u: *u8, ul: i64) -> i64 { 204 if kvs_find(u, ul, ".m3u8" as *u8, 5, 0) >= 0 { return 0 } 205 if kvs_find(u, ul, ".mpd" as *u8, 4, 0) >= 0 { return 0 } 206 return 1 207} 208// PAGE-LEVEL: de-obfuscate EVERY quality (video_url / video_alt_url / video_alt_url2) and return the HIGHEST-res 209// PROGRESSIVE one -- resolution from the parsed height in the URL OR the site's _text label, whichever is larger 210// (4K/8K ranked by real pixels, no ladder). Manifest (.m3u8/.mpd) candidates are rejected here (assembler's job). 211// Audio is muxed into these progressive .mp4s (one file = video+audio). 0 if no progressive KVS media. 212func kvs_extract(html: *u8, hlen: i64, out: *u8, cap: i64) -> i64 { 213 let lic: *u8 = sys_mmap(256) 214 let llen: i64 = kvs_qval(html, hlen, "license_code" as *u8, lic, 256) 215 let cand: *u8 = sys_mmap(K_MAGIC_4096) 216 var bestlen: i64 = 0 217 var bestscore: i64 = 0 - 1 218 var cl: i64 = kvs_try_field(html, hlen, "video_url" as *u8, lic, llen, cand, K_MAGIC_4096) 219 if cl > 0 { var s: i64 = kvs_res_score(cand, cl); let ls: i64 = kvs_label_score(html, hlen, "video_url" as *u8); if ls > s { s = ls } if kvs_is_prog(cand, cl) == 0 { s = 0 - 1 } if s > bestscore { bestscore = s; bestlen = cl; var a: i64=0; while a<cl { out[a]=cand[a]; a=a+1 } out[cl]=0 as u8 } } 220 cl = kvs_try_field(html, hlen, "video_alt_url" as *u8, lic, llen, cand, K_MAGIC_4096) 221 if cl > 0 { var s: i64 = kvs_res_score(cand, cl); let ls: i64 = kvs_label_score(html, hlen, "video_alt_url" as *u8); if ls > s { s = ls } if kvs_is_prog(cand, cl) == 0 { s = 0 - 1 } if s > bestscore { bestscore = s; bestlen = cl; var a: i64=0; while a<cl { out[a]=cand[a]; a=a+1 } out[cl]=0 as u8 } } 222 cl = kvs_try_field(html, hlen, "video_alt_url2" as *u8, lic, llen, cand, K_MAGIC_4096) 223 if cl > 0 { var s: i64 = kvs_res_score(cand, cl); let ls: i64 = kvs_label_score(html, hlen, "video_alt_url2" as *u8); if ls > s { s = ls } if kvs_is_prog(cand, cl) == 0 { s = 0 - 1 } if s > bestscore { bestscore = s; bestlen = cl; var a: i64=0; while a<cl { out[a]=cand[a]; a=a+1 } out[cl]=0 as u8 } } 224 if bestlen == 0 { out[0]=0 as u8; return 0 } 225 return bestlen 226}