108 lines
2.5 KiB
TypeScript
108 lines
2.5 KiB
TypeScript
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;
|
|
} |