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}