code wiki / (root) / nx_video_get.nx

nx_video_get.nx source

↩ module page · 166 lines · 11317 B

1// nx_video_get.nx -- SOVEREIGN streaming video downloader (X-TORRENT-LIVE-002 video rung). The engine 2// both the Waterfox extension AND the future native nish browser feed: given a media URL (+ Referer/ 3// cookie the sniffer captured), STREAM the body straight to a file (TLS record -> file fd, so multi-GB 4// videos never buffer in RAM), skipping the HTTP headers, following 30x redirects (CDNs redirect a lot). 5// Direct mp4/ts/webm = done here; HLS .m3u8 (fetch playlist -> per-segment) composes this = next rung. 6// Reuses nx_https_get's connect+handshake + the TLS record codec. Sends a real Firefox UA + Referer + 7// Cookie so CDNs that gate on those serve the bytes. argv[1]=url argv[2]=outpath argv[3]=referer argv[4]=cookie. 8// license_tier: ORIGINAL module: nishi-core.media.video_get 9import "nx_https_get.nx" 10import "nx_tls13_record.nx" 11import "nx_tls13_read_record_from_fd.nx" 12import "nx_tls13_client_session.nx" 13import "nx_http_client.nx" 14import "nx_x509_trust_store.nx" 15import "nx_pem_loader.nx" 16import "nx_csprng.nx" 17import "nx_url.nx" 18import "nx_syscalls.nx" 19const K_MAGIC_8192: i64 = 8192 20const K_MAGIC_131072: i64 = 131072 21const K_MAGIC_4096: i64 = 4096 22 23func vg_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 24func vg_wn(fd: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0 {t[0]=48 as u8;k=1} while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(28); var i: i64=0; while i<k {b[i]=t[k-1-i];i=i+1} sys_write(fd,b,k); return 0 } 25func vg_puts(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 } 26func vg_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 27func _vg_write_n(fd: i64, buf: *u8, n: i64) -> i64 { var o: i64=0; while o<n { let w: i64=sys_write(fd,(buf as i64+o) as *u8,n-o); if w<=0 { return 0-1 } o=o+w } return 0 } 28 29// build a media GET: real Firefox UA + Referer + Cookie + Connection: close (so read-to-EOF = the whole body). 30func vg_build(path: *u8, plen: i64, host: *u8, hlen: i64, referer: *u8, rlen: i64, cookie: *u8, clen: i64, out: *u8) -> i64 { 31 var o: i64 = 0 32 o = vg_puts(out, o, "GET " as *u8); var i: i64=0; while i<plen { out[o]=path[i]; o=o+1; i=i+1 } 33 o = vg_puts(out, o, " HTTP/1.1\r\nHost: " as *u8); i=0; while i<hlen { out[o]=host[i]; o=o+1; i=i+1 } 34 o = vg_puts(out, o, "\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0\r\nAccept: */*\r\nAccept-Encoding: identity\r\n" as *u8) 35 if rlen > 0 { o = vg_puts(out, o, "Referer: " as *u8); i=0; while i<rlen { out[o]=referer[i]; o=o+1; i=i+1 } o = vg_puts(out, o, "\r\n" as *u8) } 36 if clen > 0 { o = vg_puts(out, o, "Cookie: " as *u8); i=0; while i<clen { out[o]=cookie[i]; o=o+1; i=i+1 } o = vg_puts(out, o, "\r\n" as *u8) } 37 o = vg_puts(out, o, "Connection: close\r\n\r\n" as *u8) 38 return o 39} 40 41// find "\r\n\r\n" in buf[0..n); return index AFTER it, or -1. 42func vg_hdr_end(buf: *u8, n: i64) -> i64 { var i: i64=0; while i+4<=n { if buf[i]==(13 as u8){if buf[i+1]==(10 as u8){if buf[i+2]==(13 as u8){if buf[i+3]==(10 as u8){return i+4}}}} i=i+1 } return 0-1 } 43// parse the status code from "HTTP/1.1 NNN ..." 44func vg_status(buf: *u8, n: i64) -> i64 { if n<12 { return 0 } return ((buf[9] as i64-48)*100)+((buf[10] as i64-48)*10)+(buf[11] as i64-48) } 45// extract Location: <url> (case-insensitive header) from the header block into out; returns len. 46func vg_location(buf: *u8, n: i64, out: *u8, ocap: i64) -> i64 { 47 var i: i64 = 0 48 while i + 9 <= n { 49 let key: *u8 = "location:" as *u8; var j: i64 = 0; var ok: i64 = 1 50 while j < 9 { var a: i64 = buf[i+j] as i64; if a >= 65 { if a <= 90 { a = a + 32 } } if a != (key[j] as i64) { ok = 0; j = 9 } else { j = j + 1 } } 51 if ok == 1 { 52 var s: i64 = i + 9; var sk: i64 = 1 53 while sk == 1 { if s < n { if buf[s] == (32 as u8) { s = s + 1 } else { sk = 0 } } else { sk = 0 } } 54 var k: i64 = 0; var go: i64 = 1 55 while go == 1 { if s >= n { go = 0 } else { let ch: i64 = buf[s] as i64; if ch == 13 { go = 0 } else { if ch == 10 { go = 0 } else { if k < ocap - 1 { out[k] = buf[s]; k = k + 1 } s = s + 1 } } } } 56 out[k] = 0 as u8; return k 57 } 58 i = i + 1 59 } 60 return 0 61} 62 63// stream one GET to out_fd. returns: >=0 body bytes; -2 = redirect (loc filled); <0 other. 64func vg_one(s: *Tls13ClientSession, fd: i64, path: *u8, plen: i64, host: *u8, hlen: i64, referer: *u8, rlen: i64, cookie: *u8, clen: i64, out_fd: i64, loc: *u8, loc_cap: i64) -> i64 { 65 if s.state != NX_TLS13_CSESSION_STATE_CONNECTED { return 0 - 1 } 66 let req: *u8 = sys_mmap(K_MAGIC_8192); let rl: i64 = vg_build(path, plen, host, hlen, referer, rlen, cookie, clen, req) 67 let rec_buf: *u8 = sys_mmap(rl + 64) 68 let enc_v: i64 = nx_tls13_record_encrypt_v2(s.cipher_suite, s.client_app_traffic_key, s.client_app_iv, s.client_app_seq, req, rl, NX_TLS13_CT_APPLICATION_DATA, 0, rec_buf, rec_buf + NX_TLS13_RECORD_HEADER_LEN, rec_buf + NX_TLS13_RECORD_HEADER_LEN + rl + 1) 69 s.client_app_seq = s.client_app_seq + 1 70 if enc_v != NX_TLS13_REC_VERDICT_OK { return 0 - 1 } 71 if _vg_write_n(fd, rec_buf, NX_TLS13_RECORD_HEADER_LEN + rl + 1 + NX_TLS13_RECORD_TAG_LEN) < 0 { return 0 - 1 } 72 let hbuf: *u8 = sys_mmap(K_MAGIC_131072); var hn: i64 = 0; var hdr_done: i64 = 0; var body: i64 = 0 73 var go: i64 = 1 74 while go == 1 { 75 let rin: *u8 = sys_mmap(NX_HTTPS_GC_RESP_RECORD_BYTES) 76 let rt: i64 = nx_tls13_read_record_from_fd(fd, rin, NX_HTTPS_GC_RESP_RECORD_BYTES) 77 if rt < 0 { go = 0 } else { 78 let ct_len: i64 = rt - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN 79 let pt: *u8 = sys_mmap(ct_len + 16); let pc: *i64 = sys_mmap(16) as *i64; let pl: *i64 = sys_mmap(16) as *i64 80 let dv: i64 = nx_tls13_record_decrypt_v2(s.cipher_suite, s.server_app_traffic_key, s.server_app_iv, s.server_app_seq, rin, rin + NX_TLS13_RECORD_HEADER_LEN, ct_len, rin + rt - NX_TLS13_RECORD_TAG_LEN, pt, pc, pl) 81 s.server_app_seq = s.server_app_seq + 1 82 if dv != NX_TLS13_REC_VERDICT_OK { go = 0 } else { 83 if pc[0] == NX_TLS13_CT_ALERT { go = 0 } else { 84 if pc[0] == NX_TLS13_CT_APPLICATION_DATA { 85 let L: i64 = pl[0] 86 if hdr_done == 0 { 87 var i: i64 = 0; while i < L { if hn < K_MAGIC_131072 { hbuf[hn]=pt[i]; hn=hn+1 } i=i+1 } 88 let he: i64 = vg_hdr_end(hbuf, hn) 89 if he >= 0 { 90 hdr_done = 1 91 let st: i64 = vg_status(hbuf, hn) 92 if st >= 300 { if st < 400 { let ll: i64 = vg_location(hbuf, hn, loc, loc_cap); if ll > 0 { return 0 - 2 } } } 93 if he < hn { _vg_write_n(out_fd, (hbuf as i64 + he) as *u8, hn - he); body = body + (hn - he) } 94 } 95 } else { 96 _vg_write_n(out_fd, pt, L); body = body + L 97 } 98 } 99 } 100 } 101 } 102 } 103 return body 104} 105 106// fetch a media URL to outpath, following up to 4 redirects. returns body bytes. 107func nx_video_fetch(url0: *u8, referer: *u8, cookie: *u8, outpath: *u8, store: *TrustStore, now: i64) -> i64 { 108 let rlen: i64 = vg_strlen(referer); let clen: i64 = vg_strlen(cookie) 109 let out_fd: i64 = sys_openat_wr(outpath, 0x1a4); if out_fd < 0 { return 0 - 9 } 110 var url: *u8 = url0 111 var hops: i64 = 0; var total: i64 = 0; var go: i64 = 1 112 while go == 1 { 113 let url_p: *NxUrl = nx_url_new(); let traw: *u8 = sys_mmap(32); let target: *NxHttpsTarget = traw as *NxHttpsTarget 114 target.url = url_p; target.port = 0 115 if nx_https_url_for_fetch(url, target) != NX_HTTPS_URL_OK { go = 0 } else { 116 let fd_p: *i64 = sys_mmap(16) as *i64 117 if nx_https_url_connect(target, url, now, fd_p) != NX_HTTPS_CONNECT_OK { go = 0 } else { 118 let fd: i64 = *fd_p 119 let vraw: *u8 = sys_mmap(64); let vc: *TlsValidationContext = vraw as *TlsValidationContext 120 vc.store = store; vc.sni_host = url + target.url.host_off; vc.sni_host_len = target.url.host_len; vc.now_epoch = now 121 let sr: i64 = nx_tls13_client_session_run(fd, url + target.url.host_off, target.url.host_len, sys_mmap(32), sys_mmap(32), vc) 122 if sr < 0 { sys_close(fd); go = 0 } else { 123 let session: *Tls13ClientSession = sr as *Tls13ClientSession 124 var poff: i64 = target.url.path_off; var plen: i64 = target.url.path_len 125 let dp: *u8 = sys_mmap(2); dp[0]=0x2F as u8; var pp: *u8 = url + poff; if plen==0 { pp=dp; plen=1 } 126 if target.url.query_len > 0 { 127 let full: *u8 = sys_mmap(plen + target.url.query_len + 4); var fo: i64=0; var z: i64=0 128 while z<plen { full[fo]=pp[z]; fo=fo+1; z=z+1 } full[fo]=0x3F as u8; fo=fo+1 129 let qp: *u8 = url + target.url.query_off; z=0; while z<target.url.query_len { full[fo]=qp[z]; fo=fo+1; z=z+1 } pp=full; plen=fo 130 } 131 let loc: *u8 = sys_mmap(K_MAGIC_4096) 132 let r: i64 = vg_one(session, fd, pp, plen, url + target.url.host_off, target.url.host_len, referer, rlen, cookie, clen, out_fd, loc, K_MAGIC_4096) 133 sys_close(fd) 134 if r == (0 - 2) { hops = hops + 1; if hops > 4 { go = 0 } else { url = loc } } 135 else { if r >= 0 { total = r } go = 0 } 136 } 137 } 138 } 139 } 140 sys_close(out_fd) 141 return total 142} 143 144// best-effort gallery re-index after a direct video download (same idiom as the torrent worker + HLS getter). 145func vg_gallery_refresh() -> i64 { 146 let wpath: *u8 = "/tmp/nx_galx_vidindex.sov.elf" as *u8 147 let argv: *i64 = sys_mmap(16) as *i64; argv[0] = wpath as i64; argv[1] = 0 148 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0 149 let pid: i64 = sys_fork() 150 if pid == 0 { sys_execve(wpath, argv, envp); sys_exit(127) } // fork+WAIT: sequential post-processing (NOT detach) 151 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 152 return 0 153} 154 155func main(argc: i64, argv: *i64) -> i64 { 156 if argc < 3 { vg_w(1, "usage: nx_video_get <url> <outpath> [referer] [cookie]\n" as *u8); sys_exit(2); return 2 } 157 let url: *u8 = argv[1] as *u8; let outpath: *u8 = argv[2] as *u8 158 var referer: *u8 = "" as *u8; if argc >= 4 { referer = argv[3] as *u8 } 159 var cookie: *u8 = "" as *u8; if argc >= 5 { cookie = argv[4] as *u8 } 160 let store: *TrustStore = trust_store_alloc(400) 161 if nx_pem_trust_load_file("/etc/ssl/certs/ca-certificates.crt\x00", store) <= 0 { vg_w(1, "no-ca\n" as *u8); sys_exit(1); return 1 } 162 let n: i64 = nx_video_fetch(url, referer, cookie, outpath, store, sys_now_realtime_sec()) 163 vg_w(1, "VIDEO-GET bytes=" as *u8); vg_wn(1, n); vg_w(1, " -> " as *u8); vg_w(1, outpath); vg_w(1, "\n" as *u8) 164 if n > 0 { vg_gallery_refresh(); sys_exit(0); return 0 } 165 sys_exit(1); return 1 166}