torrent-online 1.5.6 → 1.5.7

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/wtui.js +30 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torrent-online",
3
- "version": "1.5.6",
3
+ "version": "1.5.7",
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(){var p=prompt('Путь к .torrent, напр. /Users/you/Downloads/torrent.torrent');if(p&&p.trim()){api('/api/add',{source:p.trim()}).then(function(){poll();toTorrents();});}};
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 || '');