ipull
Version:
The only file downloader you'll ever need. For node.js and the browser, CLI and library for fast and reliable file downloads.
94 lines • 3.23 kB
JavaScript
import { ChunkStatus } from "../../types.js";
import { promiseWithResolvers } from "../../utils/promiseWithResolvers.js";
export default class BaseDownloadProgram {
savedProgress;
_downloadSlice;
_aborted = false;
_parallelStreams;
_reload;
_activeDownloads = [];
constructor(_savedProgress, _downloadSlice) {
this._downloadSlice = _downloadSlice;
this.savedProgress = _savedProgress;
this._parallelStreams = this.savedProgress.parallelStreams;
}
get parallelStreams() {
return this._parallelStreams;
}
set parallelStreams(value) {
const needReload = value > this._parallelStreams;
this._parallelStreams = value;
if (needReload) {
this._reload?.();
}
}
incParallelStreams() {
this.parallelStreams = this._activeDownloads.length + 1;
}
decParallelStreams() {
this.parallelStreams = this._activeDownloads.length - 1;
}
waitForStreamToEnd() {
return Promise.race(this._activeDownloads);
}
async download() {
if (this._parallelStreams === 1) {
return await this._downloadSlice(0, this.savedProgress.chunks.length);
}
this._createFirstSlices();
while (!this._aborted) {
if (this._activeDownloads.length >= this._parallelStreams) {
await this._waitForStreamEndWithReload();
continue;
}
const slice = this._createOneSlice();
if (slice == null) {
if (this._activeDownloads.length === 0) {
break;
}
await this._waitForStreamEndWithReload();
continue;
}
this._createDownload(slice);
}
}
async _waitForStreamEndWithReload() {
const promiseResolvers = promiseWithResolvers();
this._reload = promiseResolvers.resolve;
return await Promise.race(this._activeDownloads.concat([promiseResolvers.promise]));
}
_createDownload(slice) {
const promise = this._downloadSlice(slice.start, slice.end);
this._activeDownloads.push(promise);
promise.then(() => {
this._activeDownloads.splice(this._activeDownloads.indexOf(promise), 1);
});
}
/**
* Create all the first slices at one - make sure they will not overlap to reduce stream aborts at later stages
*/
_createFirstSlices() {
const slices = [];
for (let i = 0; i < this.parallelStreams; i++) {
const slice = this._createOneSlice();
if (slice) {
const lastSlice = slices.find(x => x.end > slice.start && x.start < slice.start);
if (lastSlice) {
lastSlice.end = slice.start;
}
this.savedProgress.chunks[slice.start] = ChunkStatus.IN_PROGRESS;
slices.push(slice);
}
else {
break;
}
}
for (const slice of slices) {
this._createDownload(slice);
}
}
abort() {
this._aborted = true;
}
}
//# sourceMappingURL=base-download-program.js.map