import express from "express"; import { Downloader } from "./downloader"; import { UrlPoolStorage } from "./urlpool"; import { Readable } from 'stream'; let storage = new UrlPoolStorage; export class Webserver { chunk_size = 5*1024*1024; partial_size = this.chunk_size*10; constructor() { const app = express(); app.get("/status/:id", async (req, res) => { let p = storage.get(req.params.id); res.write(JSON.stringify({ streams: { total: p.urls.length, available: p.available().length, generating: p.generating } })); res.end(); }); app.get("/stream/:id", async (req, res) => { let p = storage.get(req.params.id); let d = p.get_downloader(); let range: Range = {from: 0, to: null}; if(req.headers.range) range = this.parse_range(req.headers.range); if(!range.from) range.from = 0; if(!d.ready) await d.init(); let [from, to] = this.from_to([range.from, range.from+this.chunk_size], d.total_size); let contentLength = d.total_size-from; let headers = { "Content-Range": `bytes ${from}-${d.total_size}/${d.total_size+1}`, "Range": `bytes=${from}-${d.total_size}/${d.total_size+1}`, "Accept-Ranges": "bytes", "Content-Length": contentLength, "Content-Type": "application/octet-stream", }; res.writeHead(206, headers); const readable = new Readable() readable._read = async () => { [from, to] = this.from_to([to+1, to+1+this.chunk_size], d.total_size); if(from == d.total_size) { readable.push(null); //res.end(); } else { let stream = await this.download_chunk(d, from, to); readable.push(stream); } } let stream = await this.download_chunk(d, from, to); readable.push(stream); readable.pipe(res); }); app.listen(8000, function () { console.log("Listening on port 8000!"); }); } async download_chunk(d: Downloader, from: number, to: number) { console.log("downloading...", from, to); let stream = await d.download_range(from, to); console.log("downloaded ", from, to); return stream; } parse_range(input: string): Range { let [from, to]: (number|null)[] = [null, null]; let parts = input.split("="); if(parts[1]) [from, to] = parts[1].split("-").map(i => i ? parseInt(i) : null); return {from, to}; } from_to(range: [number, number], max: number) { if(range[0] > max) range[0] = max; if(range[1] > max) range[1] = max; return range; } } interface Range { from: number | null; to: number | null; }