|
@@ -2,13 +2,14 @@ import { createWriteStream } from 'node:fs'
|
|
import { request } from 'node:https'
|
|
import { request } from 'node:https'
|
|
import type { IncomingMessage } from 'node:http'
|
|
import type { IncomingMessage } from 'node:http'
|
|
import { basename } from 'node:path'
|
|
import { basename } from 'node:path'
|
|
|
|
+import { pipeline } from 'node:stream/promises'
|
|
|
|
+import { Transform } from 'node:stream'
|
|
|
|
|
|
export async function downloadFile(task, url: string, path: string): Promise<void> {
|
|
export async function downloadFile(task, url: string, path: string): Promise<void> {
|
|
- return new Promise((resolve, reject) => {
|
|
|
|
- const fileName = basename(path)
|
|
|
|
-
|
|
|
|
- task.title = `Starting download: ${fileName}`
|
|
|
|
|
|
+ const fileName = basename(path)
|
|
|
|
+ task.title = `Starting download: ${fileName}`
|
|
|
|
|
|
|
|
+ await new Promise<void>((resolve, reject) => {
|
|
const req = request(url, (res: IncomingMessage) => {
|
|
const req = request(url, (res: IncomingMessage) => {
|
|
if (res.statusCode !== 200) {
|
|
if (res.statusCode !== 200) {
|
|
reject(new Error(`Failed to download file. Status code: ${res.statusCode}`))
|
|
reject(new Error(`Failed to download file. Status code: ${res.statusCode}`))
|
|
@@ -18,38 +19,35 @@ export async function downloadFile(task, url: string, path: string): Promise<voi
|
|
const totalSize = Number.parseInt(res.headers['content-length'] || '0', 10)
|
|
const totalSize = Number.parseInt(res.headers['content-length'] || '0', 10)
|
|
let downloadedSize = 0
|
|
let downloadedSize = 0
|
|
|
|
|
|
- const fileStream = createWriteStream(path)
|
|
|
|
-
|
|
|
|
- res.on('data', (chunk) => {
|
|
|
|
- downloadedSize += chunk.length
|
|
|
|
- if (totalSize) {
|
|
|
|
- const progress = Math.round((downloadedSize / totalSize) * 100)
|
|
|
|
- const progressBar = '[' + '='.repeat(Math.floor((downloadedSize / totalSize) * 20)) + ' '.repeat(20 - Math.floor((downloadedSize / totalSize) * 20)) + ']'
|
|
|
|
- task.title = `Downloading ${fileName}: ${progressBar} ${progress}% (${(downloadedSize / (1024 * 1024)).toFixed(2)} MB / ${(totalSize / (1024 * 1024)).toFixed(2)} MB)`
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- task.title = `Downloading ${fileName}: ${(downloadedSize / (1024 * 1024)).toFixed(2)} MB downloaded`
|
|
|
|
- }
|
|
|
|
|
|
+ // Create a transform stream that updates progress
|
|
|
|
+ const progressStream = new Transform({
|
|
|
|
+ transform(chunk, encoding, callback) {
|
|
|
|
+ downloadedSize += chunk.length
|
|
|
|
+
|
|
|
|
+ if (totalSize) {
|
|
|
|
+ const ratio = downloadedSize / totalSize
|
|
|
|
+ const progress = Math.round(ratio * 100)
|
|
|
|
+ const progressBarCount = Math.floor(ratio * 20)
|
|
|
|
+ const progressBar = `[${'='.repeat(progressBarCount)}${' '.repeat(20 - progressBarCount)}]`
|
|
|
|
+ task.title = `Downloading ${fileName}: ${progressBar} ${progress}% (${(downloadedSize / (1024 * 1024)).toFixed(2)} MB / ${(totalSize / (1024 * 1024)).toFixed(2)} MB)`
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ task.title = `Downloading ${fileName}: ${(downloadedSize / (1024 * 1024)).toFixed(2)} MB downloaded`
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ callback(null, chunk)
|
|
|
|
+ },
|
|
})
|
|
})
|
|
|
|
|
|
- res.pipe(fileStream)
|
|
|
|
-
|
|
|
|
- fileStream.on('finish', () => {
|
|
|
|
- fileStream.close(() => {
|
|
|
|
- resolve()
|
|
|
|
- })
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- fileStream.on('error', (err) => {
|
|
|
|
- fileStream.close()
|
|
|
|
- reject(err)
|
|
|
|
- })
|
|
|
|
- })
|
|
|
|
|
|
+ const fileStream = createWriteStream(path)
|
|
|
|
|
|
- req.on('error', (err) => {
|
|
|
|
- reject(err)
|
|
|
|
|
|
+ // Use pipeline to handle piping and errors automatically.
|
|
|
|
+ pipeline(res, progressStream, fileStream)
|
|
|
|
+ .then(resolve)
|
|
|
|
+ .catch(reject)
|
|
})
|
|
})
|
|
|
|
|
|
|
|
+ req.on('error', reject)
|
|
req.end()
|
|
req.end()
|
|
})
|
|
})
|
|
}
|
|
}
|