torrent-online 1.5.5 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/wtui.js +11 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torrent-online",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stream .torrent / magnet to VLC or browser — torrent streaming for macOS/Linux without a torrent client. Стрим торрентов в VLC или браузер.",
|
|
6
6
|
"keywords": [
|
package/wtui.js
CHANGED
|
@@ -641,6 +641,7 @@ function makeWebServer(client, st, opts, cacheDir, base) {
|
|
|
641
641
|
|
|
642
642
|
async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
643
643
|
const u = new URL(req.url, 'http://127.0.0.1');
|
|
644
|
+
const ensureSel = (e, i) => { const f = e?.t.files[i]; if (f && !e.sel.has(i)) { e.sel.add(i); try { f.select(); } catch {} } };
|
|
644
645
|
let p = u.pathname;
|
|
645
646
|
if (base) {
|
|
646
647
|
if (p !== base && !p.startsWith(base + '/')) { res.statusCode = 404; res.end('not found'); return; }
|
|
@@ -724,6 +725,7 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
724
725
|
const e = st.torrents.get(Number(mS[1]));
|
|
725
726
|
const f = e?.t.files[Number(mS[2])];
|
|
726
727
|
if (!f) { res.statusCode = 404; res.end('not found'); return; }
|
|
728
|
+
ensureSel(e, Number(mS[2]));
|
|
727
729
|
return serveFile(req, res, f);
|
|
728
730
|
}
|
|
729
731
|
const mT = /^\/api\/tracks\/(\d+)\/(\d+)$/.exec(p);
|
|
@@ -731,6 +733,7 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
731
733
|
const e = st.torrents.get(Number(mT[1]));
|
|
732
734
|
const f = e?.t.files[Number(mT[2])];
|
|
733
735
|
if (!f) return json(404, { error: 'нет файла' });
|
|
736
|
+
ensureSel(e, Number(mT[2]));
|
|
734
737
|
const ffprobe = detectFFBin('ffprobe');
|
|
735
738
|
if (!ffprobe) return json(200, { ffmpeg: false, tracks: [] });
|
|
736
739
|
const url = 'http://127.0.0.1:' + st.port + base + '/s/' + mT[1] + '/' + mT[2];
|
|
@@ -746,13 +749,14 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
746
749
|
else if (s.codec_type === 'subtitle') subs.push({ s: si++, lang, title, codec: s.codec_name });
|
|
747
750
|
}
|
|
748
751
|
return json(200, { ffmpeg: true, tracks, subs });
|
|
749
|
-
} catch (e2) { return json(200, { ffmpeg: true, tracks: [], subs: [],
|
|
752
|
+
} catch (e2) { return json(200, { ffmpeg: true, tracks: [], subs: [], retry: true }); }
|
|
750
753
|
}
|
|
751
754
|
const mR = /^\/remux\/(\d+)\/(\d+)$/.exec(p);
|
|
752
755
|
if (mR) {
|
|
753
756
|
const e = st.torrents.get(Number(mR[1]));
|
|
754
757
|
const f = e?.t.files[Number(mR[2])];
|
|
755
758
|
if (!f) { res.statusCode = 404; res.end('not found'); return; }
|
|
759
|
+
ensureSel(e, Number(mR[2]));
|
|
756
760
|
const ffmpeg = detectFFBin('ffmpeg');
|
|
757
761
|
if (!ffmpeg) { res.statusCode = 500; res.end('ffmpeg не найден'); return; }
|
|
758
762
|
const a = u.searchParams.get('a') || '0';
|
|
@@ -774,6 +778,7 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
774
778
|
const e = st.torrents.get(Number(mV[1]));
|
|
775
779
|
const f = e?.t.files[Number(mV[2])];
|
|
776
780
|
if (!f) { res.statusCode = 404; res.end('not found'); return; }
|
|
781
|
+
ensureSel(e, Number(mV[2]));
|
|
777
782
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
778
783
|
res.end('<!doctype html><meta charset=utf-8><title>' + esc(f.name) + '</title>' +
|
|
779
784
|
'<style>body{margin:0;background:#000;color:#ddd;font:14px system-ui;display:flex;flex-direction:column;height:100vh;box-sizing:border-box}' +
|
|
@@ -784,7 +789,9 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
784
789
|
'<video id=v controls autoplay src="' + base + '/s/' + mV[1] + '/' + mV[2] + '"></video>' +
|
|
785
790
|
'<script>var B=' + JSON.stringify(base) + ',ID=' + mV[1] + ',I=' + mV[2] + ';' +
|
|
786
791
|
'var v=document.getElementById("v"),aS=document.getElementById("a"),sS=document.getElementById("s"),n=document.getElementById("n");' +
|
|
787
|
-
'
|
|
792
|
+
'var tries=0;' +
|
|
793
|
+
'var load=function(){fetch(B+"/api/tracks/"+ID+"/"+I).then(function(r){return r.json()}).then(function(d){' +
|
|
794
|
+
'if(d.retry&&tries++<15){n.textContent="⏳ готовлю файл к стриму…";setTimeout(load,4000);return;}' +
|
|
788
795
|
'if(d.error)n.textContent="⚠ "+d.error;' +
|
|
789
796
|
'var A=d.tracks||[],S=d.subs||[];' +
|
|
790
797
|
'if(!d.ffmpeg||A.length<2){return;}' +
|
|
@@ -794,7 +801,7 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
794
801
|
'S.forEach(function(t){var o=document.createElement("option");o.value=t.s;o.textContent="💬 "+(t.lang&&t.lang!=="und"?t.lang:"субтитры "+(t.s+1))+(t.title?" — "+t.title:"");sS.appendChild(o)});sS.style.display=""}' +
|
|
795
802
|
'var go=function(){var t=v.currentTime;v.src=B+"/remux/"+ID+"/"+I+"?a="+aS.value+(sS.value!==""?"&s="+sS.value:"");v.load();v.addEventListener("loadedmetadata",function once(){v.currentTime=t;v.removeEventListener("loadedmetadata",once)});v.play()};' +
|
|
796
803
|
'aS.onchange=go;sS.onchange=go;go();' +
|
|
797
|
-
'}).catch(function(){})
|
|
804
|
+
'}).catch(function(){if(tries++<15)setTimeout(load,4000)})};load();</script>');
|
|
798
805
|
return;
|
|
799
806
|
}
|
|
800
807
|
res.statusCode = 404; res.end('not found');
|
|
@@ -814,6 +821,7 @@ async function startWeb(opts, srcArg) {
|
|
|
814
821
|
const dup = [...st.torrents.values()].find(e => e.t.infoHash === t.infoHash);
|
|
815
822
|
if (dup) { try { t.destroy(); } catch {} return; }
|
|
816
823
|
t.files.forEach(f => f.deselect()); // webtorrent по умолчанию выбирает все файлы
|
|
824
|
+
try { t.fileSelector = (tt, index, file) => (file._selections && file._selections.length ? 2 : 0); } catch {}
|
|
817
825
|
const sel = new Set();
|
|
818
826
|
const vids = t.files.map((f, i) => ({ f, i })).filter(x => VIDEO_EXT.has(path.extname(x.f.name).toLowerCase()));
|
|
819
827
|
if (st.autoSelect) {
|