code wiki / _hdl_build / nx_media_state.nx
nx_media_state.nx source
↩ module page · 124 lines · 6091 B
1// nx_media_state.nx -- EMBEDDED-STATE stream extraction (the yt-dlp / hanime-plugin technique): a modern SSR
2// site bakes its initial state as a JSON blob in the HTML (window.__NUXT__, ytInitialPlayerResponse,
3// __INITIAL_STATE__, __NEXT_DATA__, __APOLLO_STATE__), and that blob ALREADY contains the stream URLs -- so
4// you extract it directly, NO bundle execution needed. This is the cheapest, most robust discovery layer
5// (below JS-exec). Balanced-brace + string-aware (a `{` inside a JSON string doesn't break the scan). Learned
6// from cynthia2006/hanime-plugin (deepwiki 2026-07-10): "__NUXT__ state / JSON APIs". license_tier: ORIGINAL
7import "nx_syscalls.nx"
8
9func ms_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
10func ms_find(hay: *u8, hl: i64, needle: *u8, nl: i64, from: i64) -> i64 {
11 if nl == 0 { return from }
12 var i: i64 = from; let last: i64 = hl - nl
13 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 }
14 return 0 - 1
15}
16func ms_contains(hay: *u8, hl: i64, needle: *u8, nl: i64) -> i64 { if ms_find(hay, hl, needle, nl, 0) >= 0 { return 1 } return 0 }
17// is this URL a media/stream resource? (substring on extensions/manifest markers)
18func ms_is_media(u: *u8, ul: i64) -> i64 {
19 if ms_contains(u, ul, ".m3u8" as *u8, 5) == 1 { return 1 }
20 if ms_contains(u, ul, ".mpd" as *u8, 4) == 1 { return 1 }
21 if ms_contains(u, ul, ".mp4" as *u8, 4) == 1 { return 1 }
22 if ms_contains(u, ul, ".m4s" as *u8, 4) == 1 { return 1 }
23 if ms_contains(u, ul, ".webm" as *u8, 5) == 1 { return 1 }
24 if ms_contains(u, ul, ".mkv" as *u8, 4) == 1 { return 1 }
25 if ms_contains(u, ul, ".mov" as *u8, 4) == 1 { return 1 }
26 if ms_contains(u, ul, ".ts" as *u8, 3) == 1 { return 1 }
27 return 0
28}
29
30// extract the balanced {..} or [..] JSON value that STARTS at html[start]. String-aware (braces inside "..."
31// are ignored; \-escapes handled). Copies verbatim into out; returns length.
32func ms_extract_json_value(html: *u8, start: i64, hlen: i64, out: *u8, cap: i64) -> i64 {
33 let open: i64 = html[start] & 0xff
34 var close: i64 = 125 // '}'
35 if open == 91 { close = 93 } // '[' -> ']'
36 var depth: i64 = 0
37 var instr: i64 = 0
38 var esc: i64 = 0
39 var w: i64 = 0
40 var i: i64 = start
41 var go: i64 = 1
42 while go == 1 {
43 if i >= hlen { go = 0 }
44 else {
45 let c: i64 = html[i] & 0xff
46 if w < (cap - 1) { out[w] = c as u8; w = w + 1 }
47 if instr == 1 {
48 if esc == 1 { esc = 0 }
49 else { if c == 92 { esc = 1 } else { if c == 34 { instr = 0 } } }
50 } else {
51 if c == 34 { instr = 1 }
52 else { if c == open { depth = depth + 1 } else { if c == close { depth = depth - 1; if depth == 0 { go = 0 } } } }
53 }
54 i = i + 1
55 }
56 }
57 out[w] = 0 as u8
58 return w
59}
60// find `<marker>` in html, then the following `= {..}` / `= [..]` and extract the balanced JSON value. The
61// marker is e.g. "__NUXT__" / "ytInitialPlayerResponse" / "__INITIAL_STATE__". Returns value length or 0.
62func ms_extract_state(html: *u8, hlen: i64, marker: *u8, out: *u8, cap: i64) -> i64 {
63 let ml: i64 = ms_slen(marker)
64 let mpos: i64 = ms_find(html, hlen, marker, ml, 0)
65 if mpos < 0 { return 0 }
66 var i: i64 = mpos + ml
67 // skip to '=' (bounded; the marker must be an assignment target)
68 var guard: i64 = 0
69 var feq: i64 = 0
70 while feq == 0 {
71 if i >= hlen { return 0 }
72 if guard > 60 { return 0 }
73 if (html[i] & 0xff) == 61 { feq = 1; i = i + 1 } else { i = i + 1; guard = guard + 1 }
74 }
75 // skip to the opening '{' or '['
76 guard = 0
77 var fop: i64 = 0
78 while fop == 0 {
79 if i >= hlen { return 0 }
80 if guard > 60 { return 0 }
81 let c: i64 = html[i] & 0xff
82 if c == 123 { fop = 1 } else { if c == 91 { fop = 1 } else { i = i + 1; guard = guard + 1 } }
83 }
84 return ms_extract_json_value(html, i, hlen, out, cap)
85}
86
87// scan a state blob for MEDIA URLs (http/https ... media-ext), unescaping JSON `\/` -> `/`, dedup, newline-sep
88// into `out`. Returns count. This turns an extracted __NUXT__/player-response blob into a download plan.
89func ms_find_streams(blob: *u8, blen: i64, out: *u8, cap: i64) -> i64 {
90 let url: *u8 = sys_mmap(4096)
91 var count: i64 = 0
92 var wp: i64 = 0
93 var i: i64 = 0
94 while i < blen {
95 var hit: i64 = 0
96 if ms_find(blob, i + 6, "https:" as *u8, 6, i) == i { hit = 1 } // match `https:` (not `//`) so
97 if hit == 0 { if ms_find(blob, i + 5, "http:" as *u8, 5, i) == i { hit = 1 } } // JSON-escaped `\/\/` still matches
98 if hit == 1 {
99 var k: i64 = 0
100 var j: i64 = i
101 var scan: i64 = 1
102 while scan == 1 {
103 if j >= blen { scan = 0 }
104 else {
105 let c: i64 = blob[j] & 0xff
106 if c == 92 { if (j + 1) < blen { if (blob[j+1]&0xff) == 47 { if k<4095 { url[k]=47 as u8; k=k+1 } j=j+2 } else { scan=0 } } else { scan=0 } } // \/ -> /
107 else {
108 if c == 34 { scan=0 } else { if c == 39 { scan=0 } else { if c == 32 { scan=0 } else { if c == 60 { scan=0 } else { if c == 44 { scan=0 } else { if c == 125 { scan=0 } else { if c == 93 { scan=0 } else { if c == 9 { scan=0 } else { if c == 10 { scan=0 } else {
109 if k < 4095 { url[k] = c as u8; k = k + 1 } j = j + 1
110 } } } } } } } } } }
111 }
112 }
113 url[k] = 0 as u8
114 if ms_is_media(url, k) == 1 {
115 if ms_contains(out, wp, url, k) == 0 {
116 if (wp + k + 1) < cap { var a: i64=0; while a<k { out[wp]=url[a]; wp=wp+1; a=a+1 } out[wp]=10 as u8; wp=wp+1; count=count+1 }
117 }
118 }
119 i = j
120 } else { i = i + 1 }
121 }
122 out[wp] = 0 as u8
123 return count
124}