UNPKG

@empathize/framework

Version:

Framework for Neutralino

231 lines (186 loc) 5.67 kB
import path from '../paths/path.js'; import fetch from '../network/fetch.js'; import { DebugThread } from '../meta/Debug.js'; declare const Neutralino; class Stream { protected _id: number = -1; /** * ID of the curl process */ public get id(): number { return this._id; } /** * The interval in ms between progress event calls */ public progressInterval: number = 200; /** * The interval in ms between checking was downloading resumed after pausing */ public pauseInterval: number = 500; protected uri: string; protected output: string; protected total: number; protected previous: number = 0; protected onStart?: () => void; protected onProgress?: (current: number, total: number, difference: number) => void; protected onFinish?: () => void; protected started: boolean = false; protected paused: boolean = true; // true because we call .resume() method at start protected finished: boolean = false; protected debugThread: DebugThread; public constructor(uri: string, output: string, total: number) { this.uri = uri; this.output = output; this.total = total; this.started = true; this.debugThread = new DebugThread('Downloader/Stream', { message: { 'uri': uri, 'output file': output, 'total size': total } }); if (this.onStart) this.onStart(); this.resume(); const updateProgress = () => { if (!this.paused) { Neutralino.filesystem.getStats(output).then((stats) => { if (this.onProgress) this.onProgress(stats.size, this.total, stats.size - this.previous); this.previous = stats.size; if (stats.size >= this.total) { this.finished = true; this.debugThread.log('Downloading finished'); if (this.onFinish) this.onFinish(); } if (!this.finished) setTimeout(updateProgress, this.progressInterval); }).catch(() => { if (!this.finished) setTimeout(updateProgress, this.progressInterval); }); } else setTimeout(updateProgress, this.pauseInterval); }; setTimeout(updateProgress, this.progressInterval); } /** * Specify event that will be called when the download gets started * * @param callback */ public start(callback: () => void) { this.onStart = callback; if (this.started) callback(); } /** * Specify event that will be called every [this.progressInterval] ms while the file is downloading * * @param callback */ public progress(callback: (current: number, total: number, difference: number) => void) { this.onProgress = callback; } /** * Specify event that will be called after the file is downloaded * * @param callback */ public finish(callback: () => void) { this.onFinish = callback; if (this.finished) callback(); } /** * Pause downloading */ public pause() { if (!this.paused) { this.debugThread.log('Downloading paused'); this.close(true); this.paused = true; } } /** * Resume downloading */ public resume() { if (this.paused) { const command = `curl -s -L -N -C - -o "${path.addSlashes(this.output)}" "${this.uri}"`; this.debugThread.log(`Downloading started with command: ${command}`); Neutralino.os.execCommand(command, { background: true }).then((result) => { this._id = result.pid; }); this.paused = false; } } /** * Close downloading stream */ public close(forced: boolean = false) { Neutralino.os.execCommand(`kill ${forced ? '-9' : '-15'} ${this._id}`); } } export default class Downloader { protected static streams: Stream[] = []; /** * Download file * * @param uri file's uri to download * @param output relative or absolute path to the file to save it as * * @returns downloading stream */ public static async download(uri: string, output: string|null = null): Promise<Stream> { return new Promise(async (resolve) => { fetch(uri).then((response) => { const stream = new Stream(uri, output ?? this.fileFromUri(uri), response.length!); this.streams.push(stream); resolve(stream); }); }); } /** * Get a file name from the URI */ public static fileFromUri(uri: string): string { const file = uri.split('/').pop()!.split('#')[0].split('?')[0]; if (file === '') return 'index.html'; else if (`https://${file}` != uri && `http://${file}` != uri) return file; else return 'index.html'; } /** * Close every open downloading stream */ public static closeStreams(forced: boolean = false) { this.streams.forEach((stream) => { stream.close(forced); }); } }; export { Stream };