Comparing version 3.0.1 to 3.0.2
@@ -45,3 +45,3 @@ import ProgressStatusFile from "./progress-status-file.js"; | ||
.reduce((acc, bytes) => acc + bytes, 0); | ||
return chunksBytes + streamingBytes; | ||
return Math.min(chunksBytes + streamingBytes, this.downloadSize); | ||
} | ||
@@ -70,2 +70,3 @@ _emptyChunksForPart(part) { | ||
for (let i = this._progress.part; i < this.file.parts.length; i++) { | ||
// If we are starting a new part, we need to reset the progress | ||
if (i > this._progress.part || !this._activePart.acceptRange) { | ||
@@ -77,5 +78,9 @@ this._progress.part = i; | ||
} | ||
// If the part does not support range, we can only download it with a single stream | ||
if (!this._activePart.acceptRange) { | ||
this._progress.parallelStreams = 1; | ||
} | ||
// Reset in progress chunks | ||
this._progress.chunks = this._progress.chunks.map(chunk => (chunk === ChunkStatus.COMPLETE ? ChunkStatus.COMPLETE : ChunkStatus.NOT_STARTED)); | ||
// Reset active stream progress | ||
this._activeStreamBytes = {}; | ||
@@ -90,3 +95,3 @@ const downloadProgram = new DownloadProgram(this._progress, this._downloadSlice.bind(this)); | ||
} | ||
async _downloadSlice(startChunk, endChunk) { | ||
_downloadSlice(startChunk, endChunk) { | ||
const fetchState = this.options.fetchStream.withSubState({ | ||
@@ -105,23 +110,32 @@ chunkSize: this._progress.chunkSize, | ||
this._progress.chunks[startChunk] = ChunkStatus.IN_PROGRESS; | ||
await fetchState.fetchChunks((chunks, writePosition, index) => { | ||
if (this._closed) | ||
return; | ||
for (const chunk of chunks) { | ||
this.options.writeStream.write(writePosition, chunk); | ||
writePosition += chunk.length; | ||
} | ||
this._progress.chunks[index] = ChunkStatus.COMPLETE; | ||
const allWrites = []; | ||
return (async () => { | ||
await fetchState.fetchChunks((chunks, writePosition, index) => { | ||
if (this._closed) | ||
return; | ||
for (const chunk of chunks) { | ||
const writePromise = this.options.writeStream.write(writePosition, chunk); | ||
allWrites.push(writePromise); | ||
writePosition += chunk.length; | ||
writePromise?.then(() => { | ||
allWrites.splice(allWrites.indexOf(writePromise), 1); | ||
}); | ||
} | ||
this._progress.chunks[index] = ChunkStatus.COMPLETE; | ||
delete this._activeStreamBytes[startChunk]; | ||
void this._saveProgress(); | ||
const nextChunk = this._progress.chunks[index + 1]; | ||
if (nextChunk == null || nextChunk != ChunkStatus.NOT_STARTED) { | ||
return fetchState.close(); | ||
} | ||
this._progress.chunks[index + 1] = ChunkStatus.IN_PROGRESS; | ||
}); | ||
delete this._activeStreamBytes[startChunk]; | ||
void this._saveProgress(); | ||
if (this._progress.chunks[index + 1] != ChunkStatus.NOT_STARTED) { | ||
return fetchState.close(); | ||
} | ||
this._progress.chunks[index + 1] = ChunkStatus.IN_PROGRESS; | ||
}); | ||
delete this._activeStreamBytes[startChunk]; | ||
await Promise.all(allWrites); | ||
})(); | ||
} | ||
async _saveProgress() { | ||
_saveProgress() { | ||
this.emit("save", this._progress); | ||
this._sendProgressDownloadPart(); | ||
await withLock(this, "_saveLock", async () => { | ||
return withLock(this, "_saveLock", async () => { | ||
await this.options.onSaveProgressAsync?.(this._progress); | ||
@@ -128,0 +142,0 @@ }); |
@@ -12,3 +12,3 @@ import { ChunkStatus } from "../types.js"; | ||
if (this._savedProgress.parallelStreams === 1) { | ||
return this._downloadSlice(0, this._savedProgress.chunks.length); | ||
return await this._downloadSlice(0, this._savedProgress.chunks.length); | ||
} | ||
@@ -15,0 +15,0 @@ const activeDownloads = []; |
@@ -25,3 +25,8 @@ export default class SmartChunkSplit { | ||
_sendChunk() { | ||
while (this._chunks.length && this.savedLength >= this._options.chunkSize) { | ||
while (this.savedLength >= this._options.chunkSize) { | ||
if (this._chunks.length === 0) { | ||
this._callback([], this._bytesWriteLocation, this._options.startChunk++); | ||
this._bytesWriteLocation += this._options.chunkSize; | ||
this._bytesLeftovers -= this._options.chunkSize; | ||
} | ||
let sendLength = this._bytesLeftovers; | ||
@@ -28,0 +33,0 @@ for (let i = 0; i < this._chunks.length; i++) { |
@@ -29,10 +29,15 @@ import DownloadEngineNodejs from "./download-engine/engine/download-engine-nodejs.js"; | ||
cli = createCliProgressForDownloadEngine(options); | ||
cli.loadingAnimation.start(); | ||
cli.startLoading(); | ||
} | ||
options.parallelStreams ??= DEFAULT_PARALLEL_STREAMS_FOR_NODEJS; | ||
const downloader = await DownloadEngineNodejs.createFromOptions(options); | ||
cli?.loadingAnimation.stop(); | ||
downloader.on("progress", () => { | ||
cli?.updateStatues([downloader.status]); | ||
}); | ||
if (cli) { | ||
cli.start(); | ||
downloader.on("progress", () => { | ||
cli?.updateStatues([downloader.status]); | ||
}); | ||
downloader.on("closed", () => { | ||
cli?.stop(); | ||
}); | ||
} | ||
return downloader; | ||
@@ -57,12 +62,17 @@ } | ||
cli = createCliProgressForDownloadEngine(downloadOptions); | ||
cli.loadingAnimation.start(); | ||
cli.startLoading(); | ||
} | ||
const allDownloads = await Promise.all(downloads); | ||
const oneDownloader = new DownloadEngineMultiDownload(allDownloads); | ||
cli?.loadingAnimation.stop(); | ||
oneDownloader.on("progress", () => { | ||
cli?.updateStatues(oneDownloader.downloadStatues); | ||
}); | ||
if (cli) { | ||
cli.start(); | ||
oneDownloader.on("progress", () => { | ||
cli?.updateStatues(oneDownloader.downloadStatues); | ||
}); | ||
oneDownloader.on("closed", () => { | ||
cli?.stop(); | ||
}); | ||
} | ||
return oneDownloader; | ||
} | ||
//# sourceMappingURL=node-download.js.map |
@@ -37,5 +37,6 @@ import prettyBytes from "pretty-bytes"; | ||
const formattedPercentage = fullStatus.percentage.toLocaleString(undefined, { | ||
...NUMBER_FORMAT_OPTIONS, | ||
minimumIntegerDigits: 1 | ||
}) + "%"; | ||
minimumIntegerDigits: 1, | ||
minimumFractionDigits: 4 | ||
}) | ||
.slice(0, 5) + "%"; | ||
return { | ||
@@ -42,0 +43,0 @@ ...fullStatus, |
@@ -0,1 +1,2 @@ | ||
import UpdateManager from "stdout-update"; | ||
export type BaseLoadingAnimationOptions = { | ||
@@ -9,2 +10,3 @@ updateIntervalMs?: number; | ||
protected options: BaseLoadingAnimationOptions; | ||
protected stdoutManager: UpdateManager; | ||
protected constructor(options?: BaseLoadingAnimationOptions); | ||
@@ -11,0 +13,0 @@ protected _render(): void; |
@@ -1,2 +0,2 @@ | ||
import logUpdate from "log-update"; | ||
import UpdateManager from "stdout-update"; | ||
export const DEFAULT_LOADING_ANIMATION_OPTIONS = { | ||
@@ -9,9 +9,13 @@ loadingText: "Gathering information" | ||
options; | ||
stdoutManager = UpdateManager.getInstance(); | ||
constructor(options = DEFAULT_LOADING_ANIMATION_OPTIONS) { | ||
this.options = options; | ||
this.stop = this.stop.bind(this); | ||
} | ||
_render() { | ||
logUpdate(this.createFrame()); | ||
this.stdoutManager.update([this.createFrame()]); | ||
} | ||
start() { | ||
process.on("exit", this.stop); | ||
this.stdoutManager.hook(); | ||
this._intervalId = setInterval(this._render.bind(this), this.options.updateIntervalMs ?? DEFAULT_UPDATE_INTERVAL_MS); | ||
@@ -21,4 +25,7 @@ } | ||
if (this._intervalId) { | ||
this.stdoutManager.erase(); | ||
this.stdoutManager.unhook(false); | ||
clearInterval(this._intervalId); | ||
this._intervalId = undefined; | ||
process.off("exit", this.stop); | ||
} | ||
@@ -25,0 +32,0 @@ } |
import chalk from "chalk"; | ||
import { truncateText } from "../../utils/cli-text.js"; | ||
import { clamp } from "../../utils/numbers.js"; | ||
import { PRETTY_MS_OPTIONS } from "../../format-transfer-status.js"; | ||
@@ -36,5 +35,5 @@ import isUnicodeSupported from "is-unicode-supported"; | ||
renderProgressLine() { | ||
const { formattedSpeed, formatTimeLeft, formatTransferred, formatTotal } = this.status; | ||
const progressBarPercentage = clamp(this.status.transferredBytes / this.status.totalBytes, 0, 1); | ||
const progressBarText = ` ${getFormattedPercentage(progressBarPercentage)} (${formatTransferred}/${formatTotal}) `; | ||
const { formattedSpeed, formatTimeLeft, formatTransferred, formatTotal, formattedPercentage, percentage } = this.status; | ||
const formattedPercentageWithPadding = formattedPercentage.padEnd(6, " "); | ||
const progressBarText = ` ${formattedPercentageWithPadding} (${formatTransferred}/${formatTotal}) `; | ||
const etaText = formatTimeLeft + " left"; | ||
@@ -66,6 +65,6 @@ return renderDataLine([{ | ||
return renderProgressBar({ | ||
barText: leftPad + ` ${chalk.black.bgWhiteBright(getFormattedPercentage(progressBarPercentage))} ${chalk.gray(`(${formatTransferred}/${formatTotal})`)} `, | ||
backgroundText: leftPad + ` ${chalk.yellow.bgGray(getFormattedPercentage(progressBarPercentage))} ${chalk.white(`(${formatTransferred}/${formatTotal})`)} `, | ||
barText: leftPad + ` ${chalk.black.bgWhiteBright(formattedPercentageWithPadding)} ${chalk.gray(`(${formatTransferred}/${formatTotal})`)} `, | ||
backgroundText: leftPad + ` ${chalk.yellow.bgGray(formattedPercentageWithPadding)} ${chalk.white(`(${formatTransferred}/${formatTotal})`)} `, | ||
length: size, | ||
loadedPercentage: progressBarPercentage, | ||
loadedPercentage: percentage / 100, | ||
barStyle: chalk.black.bgWhiteBright, | ||
@@ -204,7 +203,2 @@ backgroundStyle: chalk.bgGray | ||
} | ||
function getFormattedPercentage(percentage) { | ||
const truncatedNumber = Math.floor(percentage * 100 * 1000) / 1000; | ||
const percentText = truncatedNumber.toFixed(3); | ||
return (percentText.slice(0, 5) + "%").padEnd(6, " "); | ||
} | ||
function renderProgressBar({ barText, backgroundText, length, loadedPercentage, barStyle, backgroundStyle }) { | ||
@@ -211,0 +205,0 @@ const barChars = Math.floor(length * loadedPercentage); |
@@ -0,1 +1,2 @@ | ||
import UpdateManager from "stdout-update"; | ||
import { CliFormattedStatus } from "./progress-bars/base-transfer-cli-progress-bar.js"; | ||
@@ -17,7 +18,11 @@ import cliSpinners from "cli-spinners"; | ||
export default class TransferCli { | ||
readonly loadingAnimation: CliSpinnersLoadingAnimation; | ||
protected readonly loadingAnimation: CliSpinnersLoadingAnimation; | ||
protected options: TransferCliOptions; | ||
protected stdoutManager: UpdateManager; | ||
constructor(options: Partial<TransferCliOptions>); | ||
startLoading(): void; | ||
start(): void; | ||
stop(): void; | ||
updateStatues(statues: FormattedStatus[]): void; | ||
protected _logUpdate(text: string): void; | ||
} |
@@ -1,2 +0,2 @@ | ||
import logUpdate from "log-update"; | ||
import UpdateManager from "stdout-update"; | ||
import debounce from "lodash.debounce"; | ||
@@ -17,2 +17,3 @@ import cliSpinners from "cli-spinners"; | ||
options; | ||
stdoutManager = UpdateManager.getInstance(); | ||
constructor(options) { | ||
@@ -26,3 +27,17 @@ this.options = { ...DEFAULT_TRANSFER_CLI_OPTIONS, ...options }; | ||
}); | ||
this.stop = this.stop.bind(this); | ||
} | ||
startLoading() { | ||
this.loadingAnimation.start(); | ||
} | ||
start() { | ||
this.loadingAnimation.stop(); | ||
this.stdoutManager.hook(); | ||
process.on("exit", this.stop); | ||
} | ||
stop() { | ||
this.stdoutManager.erase(); | ||
this.stdoutManager.unhook(false); | ||
process.off("exit", this.stop); | ||
} | ||
updateStatues(statues) { | ||
@@ -37,5 +52,5 @@ const newLog = statues.map((status) => { | ||
_logUpdate(text) { | ||
logUpdate(text); | ||
this.stdoutManager.update(text.split("\n")); | ||
} | ||
} | ||
//# sourceMappingURL=transfer-cli.js.map |
@@ -41,4 +41,3 @@ import { clamp } from "./utils/numbers.js"; | ||
const timeLeftFinalNumber = clamp((timeLeft || 0) * 1000, 0, MAX_TIME_LEFT); | ||
const percentage = Number(clamp(((transferred / total) * 100), 0, 100) | ||
.toFixed(2)); | ||
const percentage = clamp(((transferred / total) * 100), 0, 100); | ||
return this._latestProgress = { | ||
@@ -45,0 +44,0 @@ transferredBytes: clamp(transferred), |
{ | ||
"name": "ipull", | ||
"version": "3.0.1", | ||
"version": "3.0.2", | ||
"description": "The only file downloader you'll ever need. For node.js and the browser, CLI and library for fast and reliable file downloads.", | ||
@@ -131,3 +131,2 @@ "main": "dist/index.js", | ||
"lodash.debounce": "^4.0.8", | ||
"log-update": "^6.0.0", | ||
"lowdb": "^7.0.1", | ||
@@ -137,4 +136,5 @@ "pretty-bytes": "^6.1.0", | ||
"slice-ansi": "^7.1.0", | ||
"stdout-update": "^4.0.1", | ||
"strip-ansi": "^7.1.0" | ||
} | ||
} |
@@ -42,3 +42,4 @@ <div align="center"> | ||
directory: './this/path', | ||
cliProgress: true // Show progress bar in the CLI (default: false) | ||
cliProgress: true, // Show progress bar in the CLI (default: false) | ||
parallelStreams: 3 // Number of parallel connections (default: 3) | ||
}); | ||
@@ -45,0 +46,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
214060
2853
304
+ Addedstdout-update@^4.0.1
+ Addedansi-escapes@6.2.1(transitive)
+ Addedstdout-update@4.0.1(transitive)
- Removedlog-update@^6.0.0
- Removedansi-escapes@7.0.0(transitive)
- Removedcli-cursor@5.0.0(transitive)
- Removedenvironment@1.1.0(transitive)
- Removedlog-update@6.1.0(transitive)
- Removedmimic-function@5.0.1(transitive)
- Removedonetime@7.0.0(transitive)
- Removedrestore-cursor@5.1.0(transitive)
- Removedsignal-exit@4.1.0(transitive)
- Removedwrap-ansi@9.0.0(transitive)