From 04c702dfd241df3c30a8531ecfc17636893c072e Mon Sep 17 00:00:00 2001 From: Alina Sireneva Date: Fri, 27 Oct 2023 19:44:40 +0300 Subject: [PATCH] fix: improved downloadToFile API --- packages/client/src/client.ts | 9 +++++---- packages/client/src/methods/_imports.ts | 1 + .../src/methods/files/download-buffer.ts | 11 +++++----- .../client/src/methods/files/download-file.ts | 13 ++++++------ .../src/methods/files/download-iterable.ts | 20 +++++++++---------- .../src/methods/files/download-stream.ts | 15 +++++++------- packages/client/src/types/files/utils.ts | 12 +++++------ 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 2a9b4565..5c3ad658 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -267,6 +267,7 @@ import { DeleteMessageUpdate, DeleteStoryUpdate, Dialog, + FileDownloadLocation, FileDownloadParameters, FormattedString, ForumTopic, @@ -2125,7 +2126,7 @@ export interface TelegramClient extends BaseTelegramClient { * * @param params File download parameters */ - downloadAsBuffer(params: FileDownloadParameters): Promise + downloadAsBuffer(location: FileDownloadLocation, params?: FileDownloadParameters): Promise /** * Download a remote file to a local file (only for NodeJS). * Promise will resolve once the download is complete. @@ -2135,7 +2136,7 @@ export interface TelegramClient extends BaseTelegramClient { * @param filename Local file name to which the remote file will be downloaded * @param params File download parameters */ - downloadToFile(filename: string, params: FileDownloadParameters): Promise + downloadToFile(filename: string, location: FileDownloadLocation, params?: FileDownloadParameters): Promise /** * Download a file and return it as an iterable, which yields file contents * in chunks of a given size. Order of the chunks is guaranteed to be @@ -2145,7 +2146,7 @@ export interface TelegramClient extends BaseTelegramClient { * * @param params Download parameters */ - downloadAsIterable(params: FileDownloadParameters): AsyncIterableIterator + downloadAsIterable(input: FileDownloadLocation, params?: FileDownloadParameters): AsyncIterableIterator /** * Download a file and return it as a readable stream, * streaming file contents. @@ -2154,7 +2155,7 @@ export interface TelegramClient extends BaseTelegramClient { * * @param params File download parameters */ - downloadAsStream(params: FileDownloadParameters): ReadableStream + downloadAsStream(location: FileDownloadLocation, params?: FileDownloadParameters): ReadableStream /** * Normalize a {@link InputFileLike} to `InputFile`, * uploading it if needed. diff --git a/packages/client/src/methods/_imports.ts b/packages/client/src/methods/_imports.ts index 1409c488..2419a32a 100644 --- a/packages/client/src/methods/_imports.ts +++ b/packages/client/src/methods/_imports.ts @@ -36,6 +36,7 @@ import { DeleteMessageUpdate, DeleteStoryUpdate, Dialog, + FileDownloadLocation, FileDownloadParameters, FormattedString, ForumTopic, diff --git a/packages/client/src/methods/files/download-buffer.ts b/packages/client/src/methods/files/download-buffer.ts index 419cbeb7..85099991 100644 --- a/packages/client/src/methods/files/download-buffer.ts +++ b/packages/client/src/methods/files/download-buffer.ts @@ -1,7 +1,7 @@ import { BaseTelegramClient } from '@mtcute/core' import { concatBuffers } from '@mtcute/core/utils.js' -import { FileDownloadParameters, FileLocation } from '../../types/index.js' +import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { downloadAsIterable } from './download-iterable.js' /** @@ -14,15 +14,16 @@ import { downloadAsIterable } from './download-iterable.js' */ export async function downloadAsBuffer( client: BaseTelegramClient, - params: FileDownloadParameters, + location: FileDownloadLocation, + params?: FileDownloadParameters, ): Promise { - if (params.location instanceof FileLocation && ArrayBuffer.isView(params.location.location)) { - return params.location.location + if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) { + return location.location } const chunks = [] - for await (const chunk of downloadAsIterable(client, params)) { + for await (const chunk of downloadAsIterable(client, location, params)) { chunks.push(chunk) } diff --git a/packages/client/src/methods/files/download-file.ts b/packages/client/src/methods/files/download-file.ts index bbc941a6..be7bd6a7 100644 --- a/packages/client/src/methods/files/download-file.ts +++ b/packages/client/src/methods/files/download-file.ts @@ -2,7 +2,7 @@ import { createRequire } from 'module' import { BaseTelegramClient, MtUnsupportedError } from '@mtcute/core' -import { FileDownloadParameters, FileLocation } from '../../types/index.js' +import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { downloadAsIterable } from './download-iterable.js' let fs: typeof import('fs') | null = null @@ -24,15 +24,16 @@ try { export async function downloadToFile( client: BaseTelegramClient, filename: string, - params: FileDownloadParameters, + location: FileDownloadLocation, + params?: FileDownloadParameters, ): Promise { if (!fs) { throw new MtUnsupportedError('Downloading to file is only supported in NodeJS') } - if (params.location instanceof FileLocation && ArrayBuffer.isView(params.location.location)) { + if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) { // early return for inline files - const buf = params.location.location + const buf = location.location return new Promise((resolve, reject) => { fs!.writeFile(filename, buf, (err) => { @@ -44,7 +45,7 @@ export async function downloadToFile( const output = fs.createWriteStream(filename) - if (params.abortSignal) { + if (params?.abortSignal) { params.abortSignal.addEventListener('abort', () => { client.log.debug('aborting file download %s - cleaning up', filename) output.destroy() @@ -52,7 +53,7 @@ export async function downloadToFile( }) } - for await (const chunk of downloadAsIterable(client, params)) { + for await (const chunk of downloadAsIterable(client, location, params)) { output.write(chunk) } diff --git a/packages/client/src/methods/files/download-iterable.ts b/packages/client/src/methods/files/download-iterable.ts index f313fa2c..c1f3b18f 100644 --- a/packages/client/src/methods/files/download-iterable.ts +++ b/packages/client/src/methods/files/download-iterable.ts @@ -2,7 +2,7 @@ import { BaseTelegramClient, ConnectionKind, MtArgumentError, MtUnsupportedError import { ConditionVariable } from '@mtcute/core/utils.js' import { fileIdToInputFileLocation, fileIdToInputWebFileLocation, parseFileId } from '@mtcute/file-id' -import { FileDownloadParameters, FileLocation } from '../../types/index.js' +import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { determinePartSize } from '../../utils/file-utils.js' // small files (less than 128 kb) are downloaded using the "downloadSmall" pool @@ -20,18 +20,18 @@ const REQUESTS_PER_CONNECTION = 3 // some arbitrary magic value that seems to wo */ export async function* downloadAsIterable( client: BaseTelegramClient, - params: FileDownloadParameters, + input: FileDownloadLocation, + params?: FileDownloadParameters, ): AsyncIterableIterator { - const offset = params.offset ?? 0 + const offset = params?.offset ?? 0 if (offset % 4096 !== 0) { throw new MtArgumentError(`Invalid offset: ${offset}. Must be divisible by 4096`) } - let dcId = params.dcId - let fileSize = params.fileSize + let dcId = params?.dcId + let fileSize = params?.fileSize - const input = params.location let location: tl.TypeInputFileLocation | tl.TypeInputWebFileLocation if (input instanceof FileLocation) { let locationInner = input.location @@ -63,7 +63,7 @@ export async function* downloadAsIterable( // we will receive a FileMigrateError in case this is invalid if (!dcId) dcId = client.network.getPrimaryDcId() - const partSizeKb = params.partSize ?? (fileSize ? determinePartSize(fileSize) : 64) + const partSizeKb = params?.partSize ?? (fileSize ? determinePartSize(fileSize) : 64) if (partSizeKb % 4 !== 0) { throw new MtArgumentError(`Invalid part size: ${partSizeKb}. Must be divisible by 4.`) @@ -71,7 +71,7 @@ export async function* downloadAsIterable( const chunkSize = partSizeKb * 1024 - let limitBytes = params.limit ?? fileSize ?? Infinity + let limitBytes = params?.limit ?? fileSize ?? Infinity if (limitBytes === 0) return let numChunks = limitBytes === Infinity ? Infinity : ~~((limitBytes + chunkSize - offset - 1) / chunkSize) @@ -111,7 +111,7 @@ export async function* downloadAsIterable( offset: chunkSize * chunk, limit: chunkSize, }, - { dcId, kind: connectionKind, abortSignal: params.abortSignal }, + { dcId, kind: connectionKind, abortSignal: params?.abortSignal }, ) } catch (e: unknown) { if (!tl.RpcError.is(e)) throw e @@ -175,7 +175,7 @@ export async function* downloadAsIterable( position += buf.length - params.progressCallback?.(position, limitBytes) + params?.progressCallback?.(position, limitBytes) yield buf diff --git a/packages/client/src/methods/files/download-stream.ts b/packages/client/src/methods/files/download-stream.ts index a71cf91a..5c5c52de 100644 --- a/packages/client/src/methods/files/download-stream.ts +++ b/packages/client/src/methods/files/download-stream.ts @@ -1,6 +1,6 @@ import { BaseTelegramClient } from '@mtcute/core' -import { FileDownloadParameters, FileLocation } from '../../types/index.js' +import { FileDownloadLocation, FileDownloadParameters, FileLocation } from '../../types/index.js' import { bufferToStream } from '../../utils/stream-utils.js' import { downloadAsIterable } from './download-iterable.js' @@ -12,16 +12,17 @@ import { downloadAsIterable } from './download-iterable.js' */ export function downloadAsStream( client: BaseTelegramClient, - params: FileDownloadParameters, + location: FileDownloadLocation, + params?: FileDownloadParameters, ): ReadableStream { - if (params.location instanceof FileLocation && ArrayBuffer.isView(params.location.location)) { - return bufferToStream(params.location.location) + if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) { + return bufferToStream(location.location) } const cancel = new AbortController() - if (params.abortSignal) { - params.abortSignal.addEventListener('abort', () => { + if (params?.abortSignal) { + params?.abortSignal.addEventListener('abort', () => { cancel.abort() }) } @@ -29,7 +30,7 @@ export function downloadAsStream( return new ReadableStream({ start(controller) { (async () => { - for await (const chunk of downloadAsIterable(client, params)) { + for await (const chunk of downloadAsIterable(client, location, params)) { controller.enqueue(chunk) } diff --git a/packages/client/src/types/files/utils.ts b/packages/client/src/types/files/utils.ts index fe8af782..f90ef73c 100644 --- a/packages/client/src/types/files/utils.ts +++ b/packages/client/src/types/files/utils.ts @@ -51,13 +51,13 @@ export type InputFileLike = | tl.TypeInputMedia | tdFileId.RawFullRemoteFileLocation -export interface FileDownloadParameters { - /** - * File location which should be downloaded. - * You can also provide TDLib and Bot API compatible File ID - */ - location: tl.TypeInputFileLocation | tl.TypeInputWebFileLocation | FileLocation | string +/** + * File location which should be downloaded. + * You can also provide TDLib and Bot API compatible File ID + */ +export type FileDownloadLocation = tl.TypeInputFileLocation | tl.TypeInputWebFileLocation | FileLocation | string +export interface FileDownloadParameters { /** * Total file size, if known. * Used to determine upload part size.