nx_torrent_extract.nx source
↩ module page · 201 lines · 11732 B
1// nx_torrent_extract.nx -- split a completed/partial download.part into the REAL files the torrent
2// contains, per the metadata "files" list (multi-file) or "name" (single-file). This is what turns the
3// opaque "download.part" the operator sees into a playable REBDB-1026...mkv. Reuses nx_bencode. STREAMING
4// copy (1 MiB chunks -- never mmaps the multi-GB payload). For a PARTIAL download the missing pieces are
5// zero-gaps in download.part, so the extracted media still plays ("most of a film" = the exceed axis).
6// argv[1] = the torrent folder (must contain download.meta + download.part). license_tier: ORIGINAL
7//
8// module: nishi-core.torrent.extract
9// depends: nishi-core.torrent.bencode
10// capability: TORRENT_FILE_EXTRACT
11import "nx_bencode.nx"
12const EX_MAGIC_1048576: i64 = 1048576
13const EX_MAGIC_65536: i64 = 65536
14const EX_MAGIC_2048: i64 = 2048
15
16func ex_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
17func ex_wn(v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(1,"-" as *u8,1)} 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(1,b,k); return 0 }
18func ex_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
19func ex_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+ex_slen(s) }
20
21// create every directory prefix of a file path (so nested multi-file torrents work). Temporarily
22// NUL-terminates at each '/', mkdir's the prefix (EEXIST is harmless), restores.
23func ex_mkdirs(path: *u8) -> i64 {
24 var i: i64 = 0
25 while path[i] != (0 as u8) {
26 if path[i] == (47 as u8) { if i > 0 { path[i]=0 as u8; sys_mkdir(path, 0x1ff); path[i]=47 as u8 } }
27 i = i + 1
28 }
29 return 0
30}
31
32// streaming copy: srcfd bytes [start, start+len) -> a fresh file at dstpath, in 1 MiB chunks.
33func ex_copy_range(srcfd: i64, start: i64, len: i64, dstpath: *u8) -> i64 {
34 let dfd: i64 = sys_openat_wr(dstpath, 0x1a4); if dfd<0 { return 0-1 }
35 sys_lseek(srcfd, start, 0)
36 let buf: *u8 = sys_mmap(EX_MAGIC_1048576)
37 var rem: i64 = len
38 while rem > 0 {
39 var want: i64 = EX_MAGIC_1048576; if rem < want { want = rem }
40 let got: i64 = sys_read(srcfd, buf, want)
41 if got <= 0 { rem = 0 } else { sys_write(dfd, buf, got); rem = rem - got }
42 }
43 sys_close(dfd)
44 return 0
45}
46
47const EX_NAME_BONUS: i64 = 1099511627776
48func ex_lcname_has(name: *u8, sub: *u8) -> i64 {
49 let n: i64 = ex_slen(name); let m: i64 = ex_slen(sub)
50 var i: i64 = 0
51 while i + m <= n {
52 var j: i64 = 0; var ok: i64 = 1
53 while j < m { var c: i64 = name[i+j] as i64; if c>=65 { if c<=90 { c=c+32 } } if c != (sub[j] as i64) { ok=0; j=m } else { j=j+1 } }
54 if ok == 1 { return 1 }
55 i = i + 1
56 }
57 return 0
58}
59func ex_ends(name: *u8, suf: *u8) -> i64 {
60 let n: i64 = ex_slen(name); let m: i64 = ex_slen(suf)
61 if n < m { return 0 }
62 var i: i64 = 0
63 while i < m { var c: i64 = name[n-m+i] as i64; if c>=65 { if c<=90 { c=c+32 } } if c != (suf[i] as i64) { return 0 } i=i+1 }
64 return 1
65}
66func ex_is_vid(name: *u8) -> i64 { if ex_ends(name,".mp4" as *u8)==1 {return 1} if ex_ends(name,".mkv" as *u8)==1 {return 1} if ex_ends(name,".ts" as *u8)==1 {return 1} if ex_ends(name,".flv" as *u8)==1 {return 1} return 0 }
67func ex_is_img(name: *u8) -> i64 { if ex_ends(name,".jpg" as *u8)==1 {return 1} if ex_ends(name,".jpeg" as *u8)==1 {return 1} if ex_ends(name,".png" as *u8)==1 {return 1} return 0 }
68func ex_fsize(path: *u8) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0 {return 0-1} let s: i64=sys_lseek(fd,0,2); sys_close(fd); return s }
69func ex_copy_file(src: *u8, dst: *u8) -> i64 {
70 let sfd: i64 = sys_openat_rd(src); if sfd<0 { return 0-1 }
71 let dfd: i64 = sys_openat_wr(dst, 0x1a4); if dfd<0 { sys_close(sfd); return 0-1 }
72 let buf: *u8 = sys_mmap(EX_MAGIC_1048576)
73 var go: i64 = 1
74 while go==1 { let r: i64=sys_read(sfd,buf,EX_MAGIC_1048576); if r<=0 {go=0} else { sys_write(dfd,buf,r) } }
75 sys_close(sfd); sys_close(dfd); return 0
76}
77func ex_poster_walk(dir: *u8, depth: i64, mode: i64, best: *u8, bestscore: *i64, madep: *i64) -> i64 {
78 if depth > 4 { return 0 }
79 let fd: i64 = sys_openat_rd(dir); if fd < 0 { return 0 }
80 let gbuf: *u8 = sys_mmap(EX_MAGIC_65536)
81 let child: *u8 = sys_mmap(EX_MAGIC_2048)
82 let dlen: i64 = ex_slen(dir)
83 var go: i64 = 1
84 while go == 1 {
85 let n: i64 = sys_getdents64(fd, gbuf, EX_MAGIC_65536)
86 if n <= 0 { go = 0 } else {
87 var off: i64 = 0
88 while off < n {
89 let rec: *u8 = ((gbuf as i64) + off) as *u8
90 let reclen: i64 = dirent_reclen(rec)
91 let typ: i64 = dirent_type(rec)
92 let name: *u8 = dirent_name(rec)
93 var skip: i64 = 0
94 if name[0]==(46 as u8) { if name[1]==(0 as u8) {skip=1} else { if name[1]==(46 as u8) { if name[2]==(0 as u8) {skip=1} } } }
95 if skip == 0 {
96 var co: i64 = 0
97 while co < dlen { child[co]=dir[co]; co=co+1 }
98 child[co]=47 as u8; co=co+1
99 var ni: i64 = 0
100 while name[ni]!=(0 as u8) { child[co]=name[ni]; co=co+1; ni=ni+1 }
101 child[co]=0 as u8
102 if typ != DT_REG {
103 ex_poster_walk(child, depth+1, mode, best, bestscore, madep)
104 } else {
105 if mode == 1 { if ex_is_img(name)==1 { if ex_ends(name,"-poster.jpg" as *u8)==0 {
106 var sc: i64 = ex_fsize(child)
107 if ex_lcname_has(name,"poster" as *u8)==1 { sc = sc + EX_NAME_BONUS }
108 if ex_lcname_has(name,"cover" as *u8)==1 { sc = sc + EX_NAME_BONUS }
109 if sc > bestscore[0] { bestscore[0]=sc; var bi: i64=0; while child[bi]!=(0 as u8){best[bi]=child[bi];bi=bi+1} best[bi]=0 as u8 }
110 } } }
111 if mode == 2 { if ex_is_vid(name)==1 {
112 let pp3: *u8 = sys_mmap(EX_MAGIC_2048)
113 let cl: i64 = ex_slen(child)
114 var dot: i64 = 0-1; var sl2: i64 = 0-1; var q: i64 = 0
115 while q < cl { if child[q]==(47 as u8){sl2=q}; if child[q]==(46 as u8){dot=q}; q=q+1 }
116 if dot > sl2 {
117 var w: i64 = 0
118 while w < dot { pp3[w]=child[w]; w=w+1 }
119 let z: i64 = ex_cat(pp3, w, "-poster.jpg" as *u8); pp3[z]=0 as u8
120 let pfd: i64 = sys_openat_rd(pp3)
121 if pfd >= 0 { sys_close(pfd) } else {
122 if ex_copy_file(best, pp3) == 0 { madep[0]=madep[0]+1 }
123 }
124 }
125 } }
126 }
127 }
128 if reclen <= 0 { off = n } else { off = off + reclen }
129 }
130 }
131 }
132 sys_close(fd)
133 return 0
134}
135
136func main(argc: i64, argv: *i64) -> i64 {
137 var outdir: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents/REBDB-1026" as *u8
138 if argc >= 2 { outdir = argv[1] as *u8 }
139 let metapath: *u8 = sys_mmap(640); var mp: i64=ex_cat(metapath,0,outdir); mp=ex_cat(metapath,mp,"/download.meta" as *u8); metapath[mp]=0 as u8
140 let partpath: *u8 = sys_mmap(640); var pp: i64=ex_cat(partpath,0,outdir); pp=ex_cat(partpath,pp,"/download.part" as *u8); partpath[pp]=0 as u8
141
142 let mfd: i64 = sys_openat_rd(metapath); if mfd<0 { ex_w("EXTRACT verdict=RED reason=no-meta\n" as *u8); sys_exit(1); return 1 }
143 let meta: *u8 = sys_mmap(EX_MAGIC_1048576); var msize: i64=0; var r: i64=1
144 while r>0 { r=sys_read(mfd, ((meta as i64) + msize) as *u8, EX_MAGIC_1048576-msize); if r>0 {msize=msize+r} }
145 sys_close(mfd)
146 let srcfd: i64 = sys_openat_rd(partpath); if srcfd<0 { ex_w("EXTRACT verdict=RED reason=no-part\n" as *u8); sys_exit(1); return 1 }
147
148 var nfiles: i64 = 0
149 let filo: i64 = nx_bc_dict_get(meta, 0, msize, "files" as *u8, 5)
150 if filo >= 0 {
151 if (meta[filo] as i64) == 0x6C { // 'l' -- MULTI-FILE
152 var p: i64 = filo + 1
153 var off: i64 = 0
154 var run: i64 = 1
155 while run == 1 {
156 if p >= msize { run = 0 } else { if (meta[p] as i64) == 0x65 { run = 0 } else {
157 let lo: i64 = nx_bc_dict_get(meta, p, msize, "length" as *u8, 6); let lv: *i64 = sys_mmap(8) as *i64; lv[0]=0; if lo>=0 { nx_bc_int(meta, lo, msize, lv) }
158 let flen: i64 = lv[0]
159 let outp: *u8 = sys_mmap(EX_MAGIC_2048); var oo: i64 = ex_cat(outp, 0, outdir); outp[oo]=47 as u8; oo=oo+1
160 let pao: i64 = nx_bc_dict_get(meta, p, msize, "path" as *u8, 4)
161 if pao>=0 { if (meta[pao] as i64)==0x6C {
162 var pp2: i64 = pao+1; var prun: i64=1; var first: i64=1
163 while prun==1 { if pp2>=msize {prun=0} else { if (meta[pp2] as i64)==0x65 {prun=0} else {
164 let so: *i64=sys_mmap(8) as *i64; let sl: *i64=sys_mmap(8) as *i64; let np: i64=nx_bc_str(meta,pp2,msize,so,sl)
165 if np<0 {prun=0} else {
166 if first==0 { outp[oo]=47 as u8; oo=oo+1 }
167 var k: i64=0; while k<sl[0] { outp[oo]=meta[so[0]+k]; oo=oo+1; k=k+1 }
168 first=0; pp2=np
169 }
170 } } }
171 } }
172 outp[oo]=0 as u8
173 ex_mkdirs(outp)
174 ex_w(" extract " as *u8); ex_w(outp); ex_w(" (" as *u8); ex_wn(flen); ex_w(" bytes @ " as *u8); ex_wn(off); ex_w(")\n" as *u8)
175 ex_copy_range(srcfd, off, flen, outp)
176 off = off + flen
177 nfiles = nfiles + 1
178 p = nx_bc_skip(meta, p, msize)
179 } }
180 }
181 }
182 } else { // SINGLE-FILE
183 let nmo: i64 = nx_bc_dict_get(meta, 0, msize, "name" as *u8, 4); let so: *i64=sys_mmap(8) as *i64; let sl: *i64=sys_mmap(8) as *i64
184 let lno: i64 = nx_bc_dict_get(meta, 0, msize, "length" as *u8, 6); let lv: *i64=sys_mmap(8) as *i64; lv[0]=0; if lno>=0 { nx_bc_int(meta,lno,msize,lv) }
185 if nmo>=0 { nx_bc_str(meta,nmo,msize,so,sl)
186 let outp: *u8 = sys_mmap(EX_MAGIC_2048); var oo: i64=ex_cat(outp,0,outdir); outp[oo]=47 as u8; oo=oo+1; var k: i64=0; while k<sl[0]{outp[oo]=meta[so[0]+k];oo=oo+1;k=k+1} outp[oo]=0 as u8
187 ex_w(" extract " as *u8); ex_w(outp); ex_w(" (" as *u8); ex_wn(lv[0]); ex_w(" bytes)\n" as *u8)
188 ex_copy_range(srcfd, 0, lv[0], outp)
189 nfiles=1
190 }
191 }
192 sys_close(srcfd)
193 let best: *u8 = sys_mmap(EX_MAGIC_2048); best[0]=0 as u8
194 let bestscore: *i64 = sys_mmap(16) as *i64; bestscore[0]=0
195 let made: *i64 = sys_mmap(16) as *i64; made[0]=0
196 ex_poster_walk(outdir, 0, 1, best, bestscore, made)
197 if best[0] != (0 as u8) { ex_poster_walk(outdir, 0, 2, best, bestscore, made) }
198 ex_w("POSTER-BRIDGE posters=" as *u8); ex_wn(made[0]); ex_w(" cover=" as *u8); if best[0]==(0 as u8) { ex_w("none-shipped" as *u8) } else { ex_w(best) } ex_w("\n" as *u8)
199 ex_w("EXTRACT files=" as *u8); ex_wn(nfiles); ex_w(" verdict=GREEN\n" as *u8)
200 sys_exit(0); return 0
201}