torrent-online 1.7.10 → 1.7.12
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/CHANGELOG.md +11 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/wtui.js +20 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Формат — [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/), версии — [Semantic Versioning](https://semver.org/lang/ru/).
|
|
4
4
|
|
|
5
|
+
## [1.7.12] — 2026-10-04
|
|
6
|
+
**RU:** Если сервер только что поднялся, браузер открывает новую страницу, а не старую вкладку «сервер остановлен».
|
|
7
|
+
**EN:** When the server has just started, the browser opens a fresh page instead of the old «server stopped» tab.
|
|
8
|
+
|
|
9
|
+
## [1.7.11] — 2026-10-04
|
|
10
|
+
**RU:** После «Закрыть» повторный двойной щелчок снова открывает страницу. Старая вкладка больше не гасит новый сервер.
|
|
11
|
+
**EN:** After Quit, opening the app again brings the page back. The old tab no longer stops the new server.
|
|
12
|
+
- 🐛 Выход держал порт, и следующий запуск не вставал или попадал в уже умирающий процесс
|
|
13
|
+
- 🐛 Вкладка «сервер остановлен» при новом открытии оставалась на экране. Теперь она сама обновляется, когда сервер снова жив
|
|
14
|
+
- 🐛 Запрос «Закрыть» без метки этого запуска игнорируется
|
|
15
|
+
|
|
5
16
|
## [1.7.10] — 2026-10-04
|
|
6
17
|
**RU:** На странице есть кнопка «Закрыть»: она гасит фоновый сервер и не удаляет кэш. Язык переключается кнопками RU и EN.
|
|
7
18
|
**EN:** The page has a Quit button. It stops the background server and does not delete the cache. The language switches with the RU and EN buttons.
|
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ node wtui.js "https://example.com/file.torrent"
|
|
|
63
63
|
```bash
|
|
64
64
|
node wtui.js --web
|
|
65
65
|
```
|
|
66
|
-
Тот же процесс, тот же порт. В браузере: поиск, поле magnet, кнопка файла `.torrent`, карточки с прогрессом, у каждого файла кнопки VLC и Браузер. VLC играет исходник. Браузер играет тот же поток со звуком: обычная шкала, перемотка и смена дорожки. Кнопки RU и EN меняют язык страницы и плеера. «Закрыть» останавливает сервер и не трогает кэш. С телефона в Wi-Fi — через `--lan` (токен в URL).
|
|
66
|
+
Тот же процесс, тот же порт. В браузере: поиск, поле magnet, кнопка файла `.torrent`, карточки с прогрессом, у каждого файла кнопки VLC и Браузер. VLC играет исходник. Браузер играет тот же поток со звуком: обычная шкала, перемотка и смена дорожки. Кнопки RU и EN меняют язык страницы и плеера. «Закрыть» останавливает сервер и не трогает кэш. Следующий двойной щелчок по приложению поднимает страницу снова. С телефона в Wi-Fi — через `--lan` (токен в URL).
|
|
67
67
|
|
|
68
68
|
## Нативное приложение (Tauri, macOS)
|
|
69
69
|
Окошко с иконкой, без терминала: над тем же веб-GUI.
|
|
@@ -174,7 +174,7 @@ Default cache: `~/Movies/WebTorrent`. The window keeps it after exit and after Q
|
|
|
174
174
|
```bash
|
|
175
175
|
node wtui.js --web
|
|
176
176
|
```
|
|
177
|
-
Same process, same port. The browser has search, a magnet field, a `.torrent` file button, progress cards, and VLC / Browser buttons on each file. VLC plays the original. The browser plays one stream with sound: a normal timeline, seek, and an audio-track switch. The RU and EN buttons switch the page and the player. Quit stops the server and leaves the cache. From a phone on your Wi-Fi — use `--lan` (token in URL).
|
|
177
|
+
Same process, same port. The browser has search, a magnet field, a `.torrent` file button, progress cards, and VLC / Browser buttons on each file. VLC plays the original. The browser plays one stream with sound: a normal timeline, seek, and an audio-track switch. The RU and EN buttons switch the page and the player. Quit stops the server and leaves the cache. The next double-click of the app brings the page back. From a phone on your Wi-Fi — use `--lan` (token in URL).
|
|
178
178
|
|
|
179
179
|
## Native app (Tauri, macOS)
|
|
180
180
|
A window with an icon, no terminal — wraps the same web GUI.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torrent-online",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.12",
|
|
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
|
@@ -14,6 +14,7 @@ import { select, checkbox, input, confirm, Separator } from '@inquirer/prompts';
|
|
|
14
14
|
import WebTorrent from 'webtorrent';
|
|
15
15
|
|
|
16
16
|
const require = createRequire(import.meta.url);
|
|
17
|
+
const PKG_VERSION = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version;
|
|
17
18
|
|
|
18
19
|
/*
|
|
19
20
|
torrent-online — WebTorrent → VLC / браузер (ESM)
|
|
@@ -605,9 +606,10 @@ video{width:100vw;height:100vh;background:#000;display:block;margin:0}
|
|
|
605
606
|
</div>
|
|
606
607
|
<script>
|
|
607
608
|
var BASE="__BASE__";
|
|
609
|
+
var BOOT="__BOOT__";
|
|
608
610
|
var DICT={
|
|
609
|
-
ru:{phSearch:'Поиск: пацаны 4 сезон · breaking bad',search:'Искать',phMag:'Вставь magnet:?xt=urn:btih:…',addMag:'Добавить magnet',fileBtn:'Файл .torrent',lead:'Поток идёт сразу, целиком ждать не нужно. У каждого файла кнопки VLC и Браузер. Кэш при выходе не удаляется — только кнопкой «Очистить кэш». «Закрыть» останавливает сервер и не трогает скачанное.',clear:'Очистить кэш',quit:'Закрыть',torrents:'Торренты',adding:'добавляю в клиент…',torrent:'торрент',searching:'Ищу…',emptyFind:'Ничего не найдено',searchErr:'Ошибка поиска',needMag:'Нужна ссылка, которая начинается с magnet:',clearAsk:'Удалить скачанные файлы из кэша? Список торрентов тоже очистится.',cleared:'Кэш очищен',clearFail:'Не удалось очистить кэш',uploading:'Загружаю',uploadErr:'Ошибка загрузки',empty:'Пусто. Найди торрент и кликни по строке.',browser:'Браузер',all:'выбрать все',none:'снять все',waitMeta:'ждём метаданные…',peers:'пиры',hint:'Отметь серии галочками — ▶ VLC / ▶ Браузер запустят выбранное. Одна серия стартует автоматически.',del:'✕ удалить',quitAsk:'Остановить сервер? Скачанные файлы останутся в кэше.',quitDone:'Сервер остановлен.
|
|
610
|
-
en:{phSearch:'Search: the boys season 4 · breaking bad',search:'Search',phMag:'Paste magnet:?xt=urn:btih:…',addMag:'Add magnet',fileBtn:'.torrent file',lead:'Playback starts right away. You do not wait for the full download. Each file has VLC and Browser buttons. Exit does not delete the cache. Only Clear cache does. Quit stops the server and leaves downloads on disk.',clear:'Clear cache',quit:'Quit',torrents:'Torrents',adding:'adding to the client…',torrent:'torrent',searching:'Searching…',emptyFind:'Nothing found',searchErr:'Search failed',needMag:'The link must start with magnet:',clearAsk:'Delete downloaded files from the cache? The torrent list will be cleared too.',cleared:'Cache cleared',clearFail:'Could not clear the cache',uploading:'Uploading',uploadErr:'Upload failed',empty:'Nothing here yet. Search for a torrent and click a row.',browser:'Browser',all:'select all',none:'select none',waitMeta:'waiting for metadata…',peers:'peers',hint:'Tick the episodes. VLC and Browser play the selection. A single episode starts on its own.',del:'✕ remove',quitAsk:'Stop the server? Downloaded files stay in the cache.',quitDone:'Server stopped.
|
|
611
|
+
ru:{phSearch:'Поиск: пацаны 4 сезон · breaking bad',search:'Искать',phMag:'Вставь magnet:?xt=urn:btih:…',addMag:'Добавить magnet',fileBtn:'Файл .torrent',lead:'Поток идёт сразу, целиком ждать не нужно. У каждого файла кнопки VLC и Браузер. Кэш при выходе не удаляется — только кнопкой «Очистить кэш». «Закрыть» останавливает сервер и не трогает скачанное.',clear:'Очистить кэш',quit:'Закрыть',torrents:'Торренты',adding:'добавляю в клиент…',torrent:'торрент',searching:'Ищу…',emptyFind:'Ничего не найдено',searchErr:'Ошибка поиска',needMag:'Нужна ссылка, которая начинается с magnet:',clearAsk:'Удалить скачанные файлы из кэша? Список торрентов тоже очистится.',cleared:'Кэш очищен',clearFail:'Не удалось очистить кэш',uploading:'Загружаю',uploadErr:'Ошибка загрузки',empty:'Пусто. Найди торрент и кликни по строке.',browser:'Браузер',all:'выбрать все',none:'снять все',waitMeta:'ждём метаданные…',peers:'пиры',hint:'Отметь серии галочками — ▶ VLC / ▶ Браузер запустят выбранное. Одна серия стартует автоматически.',del:'✕ удалить',quitAsk:'Остановить сервер? Скачанные файлы останутся в кэше.',quitDone:'Сервер остановлен. Открой TorrentOnline ещё раз — эта вкладка обновится сама. Скачанное на месте.',quitFail:'Сервер всё ещё отвечает.',addErr:'Ошибка /api/add','empty-file':'пустой файл','need-source':'нужна ссылка или файл','no-torrent':'нет торрента','no-file':'нет файла','pick-files':'выбери файлы','no-vlc':'VLC не найден'},
|
|
612
|
+
en:{phSearch:'Search: the boys season 4 · breaking bad',search:'Search',phMag:'Paste magnet:?xt=urn:btih:…',addMag:'Add magnet',fileBtn:'.torrent file',lead:'Playback starts right away. You do not wait for the full download. Each file has VLC and Browser buttons. Exit does not delete the cache. Only Clear cache does. Quit stops the server and leaves downloads on disk.',clear:'Clear cache',quit:'Quit',torrents:'Torrents',adding:'adding to the client…',torrent:'torrent',searching:'Searching…',emptyFind:'Nothing found',searchErr:'Search failed',needMag:'The link must start with magnet:',clearAsk:'Delete downloaded files from the cache? The torrent list will be cleared too.',cleared:'Cache cleared',clearFail:'Could not clear the cache',uploading:'Uploading',uploadErr:'Upload failed',empty:'Nothing here yet. Search for a torrent and click a row.',browser:'Browser',all:'select all',none:'select none',waitMeta:'waiting for metadata…',peers:'peers',hint:'Tick the episodes. VLC and Browser play the selection. A single episode starts on its own.',del:'✕ remove',quitAsk:'Stop the server? Downloaded files stay in the cache.',quitDone:'Server stopped. Open TorrentOnline again and this tab will refresh itself. Downloads stay on disk.',quitFail:'The server is still running.',addErr:'/api/add error','empty-file':'empty file','need-source':'a link or a file is required','no-torrent':'no such torrent','no-file':'no such file','pick-files':'pick files first','no-vlc':'VLC was not found'}
|
|
611
613
|
};
|
|
612
614
|
function lang(){try{return localStorage.getItem('to-lang')==='en'?'en':'ru'}catch(e){return 'ru'}}
|
|
613
615
|
function tr(k){var d=DICT[lang()]||DICT.ru;if(d&&d[k]!=null)return d[k];return (DICT.ru&&DICT.ru[k]!=null)?DICT.ru[k]:k}
|
|
@@ -636,7 +638,8 @@ document.getElementById('q').addEventListener('keydown',function(e){if(e.key==='
|
|
|
636
638
|
document.getElementById('mag').onclick=function(){var m=document.getElementById('magin').value.trim();var nb=document.getElementById('notes');if(m.indexOf('magnet:')!==0){nb.innerHTML='<div class=note>'+esc(tr('needMag'))+'</div>';return;}nb.innerHTML='';addTorrent(m);document.getElementById('magin').value='';};
|
|
637
639
|
document.getElementById('magin').addEventListener('keydown',function(e){if(e.key==='Enter')document.getElementById('mag').click();});
|
|
638
640
|
document.getElementById('clr').onclick=function(){if(!confirm(tr('clearAsk')))return;api('/api/clear-cache',{ok:1}).then(function(){document.getElementById('notes').innerHTML='<div class=note>'+esc(tr('cleared'))+'</div>';poll();}).catch(function(){document.getElementById('notes').innerHTML='<div class=note>'+esc(tr('clearFail'))+'</div>';});};
|
|
639
|
-
|
|
641
|
+
function showStopped(){if(pollTimer){clearInterval(pollTimer);pollTimer=null;}document.body.innerHTML='<p style="margin:0;padding:28px;font:16px/1.45 -apple-system,sans-serif;color:#e8ecf1">'+esc(tr('quitDone'))+'</p>';document.body.style.background='#0f1115';var n=0;var watch=setInterval(function(){n++;if(n>150){clearInterval(watch);return;}fetch(BASE+'/api/health').then(function(r){if(!r.ok)return;clearInterval(watch);location.reload();}).catch(function(){});},800);}
|
|
642
|
+
document.getElementById('quit').onclick=function(){if(!confirm(tr('quitAsk')))return;fetch(BASE+'/api/quit',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({boot:BOOT})}).then(function(r){if(r.status===409){document.getElementById('notes').innerHTML='<div class=note>'+esc(tr('quitFail'))+'</div>';return;}showStopped();}).catch(function(){showStopped();});};
|
|
640
643
|
function setLang(code){try{localStorage.setItem('to-lang',code)}catch(e){}applyLang();poll();}
|
|
641
644
|
document.getElementById('lang-ru').onclick=function(){setLang('ru');};
|
|
642
645
|
document.getElementById('lang-en').onclick=function(){setLang('en');};
|
|
@@ -932,7 +935,8 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
932
935
|
|
|
933
936
|
if (p === '/' || p === '') {
|
|
934
937
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
935
|
-
res.
|
|
938
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
939
|
+
res.end(WEB_HTML.replaceAll('__BASE__', base).replaceAll('__BOOT__', st.boot));
|
|
936
940
|
return;
|
|
937
941
|
}
|
|
938
942
|
if (p === '/hls.min.js') {
|
|
@@ -944,7 +948,11 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
944
948
|
} catch { res.statusCode = 404; res.end('no hls.js'); }
|
|
945
949
|
return;
|
|
946
950
|
}
|
|
951
|
+
if (p === '/api/health') return json(200, { ok: true, version: PKG_VERSION });
|
|
947
952
|
if (p === '/api/quit' && req.method === 'POST') {
|
|
953
|
+
const b = await readBody(req).catch(() => ({}));
|
|
954
|
+
// Старая вкладка после «Закрыть» не должна гасить уже новый процесс.
|
|
955
|
+
if (!b || b.boot !== st.boot) return json(409, { error: 'stale' });
|
|
948
956
|
res.statusCode = 200;
|
|
949
957
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
950
958
|
res.setHeader('Connection', 'close');
|
|
@@ -952,7 +960,7 @@ async function webReq(req, res, client, st, opts, cacheDir, base) {
|
|
|
952
960
|
setTimeout(() => {
|
|
953
961
|
if (typeof st.quit === 'function') st.quit();
|
|
954
962
|
else process.exit(0);
|
|
955
|
-
},
|
|
963
|
+
}, 80);
|
|
956
964
|
return;
|
|
957
965
|
}
|
|
958
966
|
if (p === '/api/search') {
|
|
@@ -1339,7 +1347,7 @@ async function startWeb(opts, srcArg) {
|
|
|
1339
1347
|
const client = new WebTorrent({ destroyStoreOnDestroy: false });
|
|
1340
1348
|
const token = opts.lan ? crypto.randomBytes(8).toString('hex') : '';
|
|
1341
1349
|
const base = token ? '/t/' + token : '';
|
|
1342
|
-
const st = { torrents: new Map(), nextId: 1, vlc: [], port: 0, pending: [], autoSelect: !!srcArg };
|
|
1350
|
+
const st = { torrents: new Map(), nextId: 1, vlc: [], port: 0, pending: [], autoSelect: !!srcArg, boot: crypto.randomBytes(8).toString('hex') };
|
|
1343
1351
|
client.on('torrent', t => {
|
|
1344
1352
|
t.on('error', () => {});
|
|
1345
1353
|
const id = st.pending.shift();
|
|
@@ -1362,6 +1370,9 @@ async function startWeb(opts, srcArg) {
|
|
|
1362
1370
|
const server = makeWebServer(client, st, opts, cacheDir, base);
|
|
1363
1371
|
const host = opts.lan ? '0.0.0.0' : '127.0.0.1';
|
|
1364
1372
|
st.port = await listenOnFreePort(server, host, opts.port);
|
|
1373
|
+
if (process.env.WTUI_PIDFILE) {
|
|
1374
|
+
try { fs.writeFileSync(process.env.WTUI_PIDFILE, String(process.pid)); } catch {}
|
|
1375
|
+
}
|
|
1365
1376
|
const url = 'http://127.0.0.1:' + st.port + base + '/';
|
|
1366
1377
|
console.log('🌐 GUI: ' + url);
|
|
1367
1378
|
if (opts.lan) console.log('📱 В сети: http://' + localIP() + ':' + st.port + base + '/ (токен в URL)');
|
|
@@ -1377,11 +1388,10 @@ async function startWeb(opts, srcArg) {
|
|
|
1377
1388
|
try { server.close(); } catch {}
|
|
1378
1389
|
if (st.hls) for (const h of st.hls.procs.values()) { try { h.proc.kill('SIGKILL'); } catch {} }
|
|
1379
1390
|
try { fs.rmSync(path.join(os.tmpdir(), 'wtui-hls-' + process.pid), { recursive: true, force: true }); } catch {}
|
|
1380
|
-
|
|
1391
|
+
// Порт надо отдать сразу, иначе следующий запуск .app не встаёт. Кэш не стираем.
|
|
1392
|
+
const bye = setTimeout(() => process.exit(code), 200);
|
|
1381
1393
|
bye.unref();
|
|
1382
|
-
|
|
1383
|
-
try { await new Promise(r => client.destroy(r)); } catch {}
|
|
1384
|
-
process.exit(code);
|
|
1394
|
+
try { client.destroy(() => process.exit(code)); } catch { process.exit(code); }
|
|
1385
1395
|
}
|
|
1386
1396
|
st.quit = () => { shutdown(0); };
|
|
1387
1397
|
process.on('SIGINT', () => shutdown(0));
|