Spaces:
Paused
Paused
File size: 5,407 Bytes
762fa11 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
import fs from "fs-extra";
import MemoryStore from "memory-chunk-store";
import os from "os";
import path from "path";
import WebTorrent from "webtorrent";
import { getReadableDuration } from "../utils/file.js";
// Directory to store downloaded files (default OS temp directory)
const DOWNLOAD_DIR = process.env.DOWNLOAD_DIR || path.join(os.tmpdir(), "torrent-stream-server");
// Keep downloaded files after all streams are closed (default false)
const KEEP_DOWNLOADED_FILES = process.env.KEEP_DOWNLOADED_FILES
? process.env.KEEP_DOWNLOADED_FILES === "true"
: false;
if (!KEEP_DOWNLOADED_FILES)
fs.emptyDirSync(DOWNLOAD_DIR);
// Maximum number of connections per torrent (default 50)
const MAX_CONNS_PER_TORRENT = Number(process.env.MAX_CONNS_PER_TORRENT) || 50;
// Max download speed (bytes/s) over all torrents (default 20MB/s)
const DOWNLOAD_SPEED_LIMIT = Number(process.env.DOWNLOAD_SPEED_LIMIT) || 20 * 1024 * 1024;
// Max upload speed (bytes/s) over all torrents (default 1MB/s)
const UPLOAD_SPEED_LIMIT = Number(process.env.UPLOAD_SPEED_LIMIT) || 1 * 1024 * 1024;
// Time (ms) to seed torrents after all streams are closed (default 1 minute)
const SEED_TIME = Number(process.env.SEED_TIME) || 60 * 1000;
// Timeout (ms) when adding torrents if no metadata is received (default 5 seconds)
const TORRENT_TIMEOUT = Number(process.env.TORRENT_TIMEOUT) || 5 * 1000;
const infoClient = new WebTorrent();
const streamClient = new WebTorrent({
// @ts-ignore
downloadLimit: DOWNLOAD_SPEED_LIMIT,
uploadLimit: UPLOAD_SPEED_LIMIT,
maxConns: MAX_CONNS_PER_TORRENT,
});
streamClient.on("torrent", (torrent) => {
console.log(`Added torrent: ${torrent.name}`);
});
streamClient.on("error", (error) => {
if (typeof error === "string") {
console.error(`Error: ${error}`);
}
else {
if (error.message.startsWith("Cannot add duplicate torrent"))
return;
console.error(`Error: ${error.message}`);
}
});
infoClient.on("error", () => { });
const launchTime = Date.now();
export const getStats = () => ({
uptime: getReadableDuration(Date.now() - launchTime),
openStreams: [...openStreams.values()].reduce((a, b) => a + b, 0),
downloadSpeed: streamClient.downloadSpeed,
uploadSpeed: streamClient.uploadSpeed,
activeTorrents: streamClient.torrents.map((torrent) => ({
name: torrent.name,
infoHash: torrent.infoHash,
size: torrent.length,
progress: torrent.progress,
downloaded: torrent.downloaded,
uploaded: torrent.uploaded,
downloadSpeed: torrent.downloadSpeed,
uploadSpeed: torrent.uploadSpeed,
peers: torrent.numPeers,
openStreams: openStreams.get(torrent.infoHash) || 0,
files: torrent.files.map((file) => ({
name: file.name,
path: file.path,
size: file.length,
progress: file.progress,
downloaded: file.downloaded,
})),
})),
});
export const getOrAddTorrent = (uri) => new Promise((resolve) => {
const torrent = streamClient.add(uri, {
path: DOWNLOAD_DIR,
destroyStoreOnDestroy: !KEEP_DOWNLOADED_FILES,
// @ts-ignore
deselect: true,
}, (torrent) => {
clearTimeout(timeout);
resolve(torrent);
});
const timeout = setTimeout(() => {
torrent.destroy();
resolve(undefined);
}, TORRENT_TIMEOUT);
});
export const getFile = (torrent, path) => torrent.files.find((file) => file.path === path);
export const getTorrentInfo = async (uri) => {
const getInfo = (torrent) => ({
name: torrent.name,
infoHash: torrent.infoHash,
size: torrent.length,
files: torrent.files.map((file) => ({
name: file.name,
path: file.path,
size: file.length,
})),
});
return await new Promise((resolve) => {
const torrent = infoClient.add(uri, { store: MemoryStore, destroyStoreOnDestroy: true }, (torrent) => {
clearTimeout(timeout);
const info = getInfo(torrent);
console.log(`Fetched info: ${info.name}`);
torrent.destroy();
resolve(info);
});
const timeout = setTimeout(() => {
torrent.destroy();
resolve(undefined);
}, TORRENT_TIMEOUT);
});
};
const timeouts = new Map();
const openStreams = new Map();
export const streamOpened = (hash, fileName) => {
console.log(`Stream opened: ${fileName}`);
const count = openStreams.get(hash) || 0;
openStreams.set(hash, count + 1);
const timeout = timeouts.get(hash);
if (timeout) {
clearTimeout(timeout);
timeouts.delete(hash);
}
};
export const streamClosed = (hash, fileName) => {
console.log(`Stream closed: ${fileName}`);
const count = openStreams.get(hash) || 1;
openStreams.set(hash, count - 1);
if (count > 1)
return;
openStreams.delete(hash);
let timeout = timeouts.get(hash);
if (timeout)
return;
timeout = setTimeout(async () => {
const torrent = await streamClient.get(hash);
// @ts-ignore
torrent?.destroy(undefined, () => {
console.log(`Removed torrent: ${torrent.name}`);
timeouts.delete(hash);
});
}, SEED_TIME);
timeouts.set(hash, timeout);
};
//# sourceMappingURL=webtorrent.js.map |