torrent-online 1.5.6 → 1.5.8
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 +38 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torrent-online",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.8",
|
|
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
|
@@ -565,6 +565,7 @@ button:hover{background:#2a3c58}
|
|
|
565
565
|
.fin .play{margin-left:6px;padding:0 8px;font-size:12px}
|
|
566
566
|
.picks{margin:6px 0 2px}
|
|
567
567
|
.picks button{font-size:12px;padding:3px 10px}
|
|
568
|
+
body.drop{outline:3px dashed #3b82f6;outline-offset:-10px;background:rgba(59,130,246,.06)}
|
|
568
569
|
.fin .play{margin-left:6px;padding:0 8px;font-size:12px}
|
|
569
570
|
.picks{margin:6px 0 2px}
|
|
570
571
|
.picks button{font-size:12px;padding:3px 10px}
|
|
@@ -575,7 +576,7 @@ video{width:100vw;height:100vh;background:#000;display:block;margin:0}
|
|
|
575
576
|
</style></head><body>
|
|
576
577
|
<header>🦀 <b>TorrentOnline</b></header>
|
|
577
578
|
<div class=wrap>
|
|
578
|
-
<div class=search><input id=q placeholder="Поиск: пацаны 4 сезон · breaking bad · magnet:…" autocomplete=off><button id=go>Искать</button><button id=mag>+ magnet</button><button id=tpath>+ .torrent файл</button></div>
|
|
579
|
+
<div class=search><input id=q placeholder="Поиск: пацаны 4 сезон · breaking bad · magnet:…" autocomplete=off><button id=go>Искать</button><button id=mag>+ magnet</button><button id=tpath>+ .torrent файл</button><input type=file id=tt accept=".torrent,application/x-bittorrent" style=display:none></div>
|
|
579
580
|
<div id=notes></div>
|
|
580
581
|
<div id=results></div>
|
|
581
582
|
<h3>Торренты</h3>
|
|
@@ -596,7 +597,18 @@ box.innerHTML=h;
|
|
|
596
597
|
document.getElementById('go').onclick=doSearch;
|
|
597
598
|
document.getElementById('q').addEventListener('keydown',function(e){if(e.key==='Enter')doSearch();});
|
|
598
599
|
document.getElementById('mag').onclick=function(){var m=prompt('magnet:');if(m&&m.indexOf('magnet:')===0){api('/api/add',{source:m}).then(function(){poll();toTorrents();});}};
|
|
599
|
-
document.getElementById('tpath').onclick=function(){
|
|
600
|
+
document.getElementById('tpath').onclick=function(){document.getElementById('tt').click();};
|
|
601
|
+
document.getElementById('tt').onchange=function(){if(this.files&&this.files[0])uploadFile(this.files[0]);this.value='';};
|
|
602
|
+
function uploadFile(f){var nb=document.getElementById('notes');nb.innerHTML='<div class=note>⏳ Загружаю '+esc(f.name)+'…</div>';
|
|
603
|
+
fetch(BASE+'/api/add-file',{method:'POST',headers:{'content-type':'application/octet-stream'},body:f}).then(function(r){return r.json()}).then(function(d){nb.innerHTML=d.error?'<div class=note>⚠ '+esc(d.error)+'</div>':'';poll();toTorrents();}).catch(function(){nb.innerHTML='<div class=note>⚠ Ошибка загрузки</div>';});}
|
|
604
|
+
document.addEventListener('dragover',function(e){e.preventDefault();document.body.classList.add('drop');});
|
|
605
|
+
document.addEventListener('dragleave',function(e){if(e.target===document.body||e.relatedTarget===null)document.body.classList.remove('drop');});
|
|
606
|
+
document.addEventListener('drop',function(e){e.preventDefault();document.body.classList.remove('drop');
|
|
607
|
+
var fs=e.dataTransfer.files||[];
|
|
608
|
+
for(var i=0;i<fs.length;i++){if(/\.torrent$/i.test(fs[i].name)){uploadFile(fs[i]);return;}}
|
|
609
|
+
var t=(e.dataTransfer.getData('text/uri-list')||'')+(e.dataTransfer.getData('text/plain')||'');
|
|
610
|
+
var mm=t.match(/magnet:\?[^\s,]+/);if(mm){api('/api/add',{source:'magnet:'+mm[0]}).then(function(){poll();toTorrents();});}
|
|
611
|
+
});
|
|
600
612
|
function render(list){var box=document.getElementById('torrents');
|
|
601
613
|
if(!list.length){box.innerHTML='<div class=note>Пусто. Найди торрент и кликни по строке.</div>';return;}
|
|
602
614
|
box.innerHTML=list.map(function(t){
|
|
@@ -659,6 +671,22 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
659
671
|
const { items, notes } = await searchIndexers(q);
|
|
660
672
|
return json(200, { items, notes });
|
|
661
673
|
}
|
|
674
|
+
if (p === '/api/add-file' && req.method === 'POST') {
|
|
675
|
+
const chunks = [];
|
|
676
|
+
let size = 0;
|
|
677
|
+
await new Promise((resolve, reject) => {
|
|
678
|
+
req.on('data', c => { chunks.push(c); size += c.length; if (size > 5e6) { req.destroy(); reject(new Error('файл слишком большой')); } });
|
|
679
|
+
req.on('end', resolve);
|
|
680
|
+
req.on('error', reject);
|
|
681
|
+
});
|
|
682
|
+
const buf = Buffer.concat(chunks);
|
|
683
|
+
if (!buf.length) return json(400, { error: 'пустой файл' });
|
|
684
|
+
const id = st.nextId++;
|
|
685
|
+
st.pending.push(id);
|
|
686
|
+
try { client.add(buf, { path: cacheDir }); }
|
|
687
|
+
catch (e) { st.pending.pop(); return json(400, { error: String((e && e.message) || e) }); }
|
|
688
|
+
return json(200, { id });
|
|
689
|
+
}
|
|
662
690
|
if (p === '/api/add' && req.method === 'POST') {
|
|
663
691
|
const b = await readBody(req);
|
|
664
692
|
const source = String(b.source || '');
|
|
@@ -762,11 +790,13 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
762
790
|
const a = u.searchParams.get('a') || '0';
|
|
763
791
|
const sb = u.searchParams.get('s');
|
|
764
792
|
const url = 'http://127.0.0.1:' + st.port + base + '/s/' + mR[1] + '/' + mR[2];
|
|
765
|
-
const args = ['-hide_banner', '-loglevel', 'error', '-i', url, '-map', '0:v:0', '-map', '0:a:' + a, '-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k', '-max_muxing_queue_size', '9999'];
|
|
793
|
+
const args = ['-hide_banner', '-loglevel', 'error', '-rw_timeout', '30000000', '-reconnect', '1', '-reconnect_streamed', '1', '-reconnect_delay_max', '5', '-i', url, '-map', '0:v:0', '-map', '0:a:' + a, '-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k', '-max_muxing_queue_size', '9999'];
|
|
766
794
|
if (sb !== null && sb !== '') args.push('-map', '0:s:' + sb, '-c:s', 'mov_text');
|
|
767
795
|
args.push('-f', 'mp4', '-movflags', 'frag_keyframe+empty_moov+default_base_moof', '-');
|
|
768
796
|
res.setHeader('Content-Type', 'video/mp4');
|
|
769
|
-
const proc = spawn(ffmpeg, args, { stdio: ['ignore', 'pipe', '
|
|
797
|
+
const proc = spawn(ffmpeg, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
798
|
+
let ffErr = 0;
|
|
799
|
+
proc.stderr.on('data', d => { if (ffErr < 3) { console.error('ffm:', String(d).trim().slice(0, 200)); ffErr++; } });
|
|
770
800
|
proc.stdout.pipe(res);
|
|
771
801
|
proc.on('error', () => { try { res.end(); } catch {} });
|
|
772
802
|
req.on('close', () => { try { proc.kill('SIGKILL'); } catch {} });
|
|
@@ -786,7 +816,7 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
786
816
|
'select{background:#1b1b1f;color:#ddd;border:1px solid #333;border-radius:6px;padding:4px;max-width:340px}' +
|
|
787
817
|
'video{flex:1;width:100%;min-height:0;background:#000}</style>' +
|
|
788
818
|
'<div class=bar><b>' + esc(f.name) + '</b><select id=a style=display:none></select><select id=s style=display:none></select><span id=n></span></div>' +
|
|
789
|
-
'<video id=v controls autoplay src="' + base + '/s/' + mV[1] + '/' + mV[2] + '"></video>' +
|
|
819
|
+
'<video id=v controls autoplay muted playsinline src="' + base + '/s/' + mV[1] + '/' + mV[2] + '"></video>' +
|
|
790
820
|
'<script>var B=' + JSON.stringify(base) + ',ID=' + mV[1] + ',I=' + mV[2] + ';' +
|
|
791
821
|
'var v=document.getElementById("v"),aS=document.getElementById("a"),sS=document.getElementById("s"),n=document.getElementById("n");' +
|
|
792
822
|
'var tries=0;' +
|
|
@@ -799,8 +829,10 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
799
829
|
'aS.value=A[0].a;aS.style.display="";' +
|
|
800
830
|
'if(S.length){var o0=document.createElement("option");o0.value="";o0.textContent="без субтитров";sS.appendChild(o0);' +
|
|
801
831
|
'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=""}' +
|
|
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()};' +
|
|
832
|
+
'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().catch(function(){})};' +
|
|
803
833
|
'aS.onchange=go;sS.onchange=go;go();' +
|
|
834
|
+
'v.addEventListener("playing",function(){n.textContent=""});v.addEventListener("volumechange",function(){if(!v.muted)n.title="звук включён"});' +
|
|
835
|
+
'setInterval(function(){fetch(B+"/api/status").then(function(r){return r.json()}).then(function(l){var t=l.find(function(x){return x.id===ID});if(t&&v.readyState<2)n.textContent="⏬ "+t.speed+" · пиры "+t.peers+(t.progress>0?" · "+t.progress+"%":"")}).catch(function(){})},2000);' +
|
|
804
836
|
'}).catch(function(){if(tries++<15)setTimeout(load,4000)})};load();</script>');
|
|
805
837
|
return;
|
|
806
838
|
}
|