filemail-sdk 9.3.3 → 9.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/client/downloader/downloaderCore.d.ts.map +1 -1
- package/dist/src/client/downloader/downloaderCore.js +8 -4
- package/dist/src/client/downloader/downloaderCore.js.map +1 -1
- package/dist/src/downloader/chunkDownloader.d.ts.map +1 -1
- package/dist/src/downloader/chunkDownloader.js +22 -3
- package/dist/src/downloader/chunkDownloader.js.map +1 -1
- package/dist/src/downloader/downloadOptions.d.ts +23 -0
- package/dist/src/downloader/downloadOptions.d.ts.map +1 -1
- package/dist/src/downloader/downloadOptions.js +23 -0
- package/dist/src/downloader/downloadOptions.js.map +1 -1
- package/dist/src/downloader/filesDownloader.d.ts.map +1 -1
- package/dist/src/downloader/filesDownloader.js +70 -3
- package/dist/src/downloader/filesDownloader.js.map +1 -1
- package/dist/src/utils/fileSystem/fileSystemService.d.ts +2 -0
- package/dist/src/utils/fileSystem/fileSystemService.d.ts.map +1 -1
- package/dist/src/utils/fileSystem/fileSystemService.js.map +1 -1
- package/dist/src/utils/fileSystem/node/nodeFileSystemService.d.ts +2 -0
- package/dist/src/utils/fileSystem/node/nodeFileSystemService.d.ts.map +1 -1
- package/dist/src/utils/fileSystem/node/nodeFileSystemService.js +22 -1
- package/dist/src/utils/fileSystem/node/nodeFileSystemService.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloaderCore.d.ts","sourceRoot":"","sources":["../../../../src/client/downloader/downloaderCore.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,eAAe,MAAM,qCAAqC,CAAC;AACvE,OAAO,KAAK,SAAS,MAAM,8BAA8B,CAAC;AAC1D,OAAO,KAAK,iBAAiB,MAAM,6CAA6C,CAAC;AACjF,OAAO,cAAc,MAAM,qCAAqC,CAAC;AAEjE,OAAO,KAAK,YAAY,MAAM,uCAAuC,CAAC;AAGtE,OAAO,KAAK,eAAe,MAAM,0CAA0C,CAAC;AAC5E,OAAO,KAAK,oBAAoB,MAAM,+CAA+C,CAAC;AAEtF,OAAO,KAAK,0BAA0B,MAAM,sDAAsD,CAAC;AACnG,OAAO,KAAK,uBAAuB,MAAM,4CAA4C,CAAC;AACtF,OAAO,KAAK,YAAY,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,iBAAiB,MAAM,6CAA6C,CAAC;AACjF,OAAO,KAAK,cAAc,MAAM,oCAAoC,CAAC;AACrE,OAAO,KAAK,iBAAiB,MAAM,iDAAiD,CAAC;AAGrF,OAAO,KAAK,oBAAoB,MAAM,4CAA4C,CAAC;AACnF,OAAO,KAAK,+BAA+B,MAAM,qDAAqD,CAAC;AAEvG,MAAM,CAAC,OAAO,OAAO,cAAc;;gBAkBnB,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,oBAAoB,EAAE,0BAA0B,CAAC,MAAM,CAAC,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,+BAA+B;IAY7U,IAAI,MAAM,IAAI,cAAc,GAAG,IAAI,CAKlC;IAED,IAAI,KAAK,IAAI,iBAAiB,GAAG,IAAI,CAKpC;IAED,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAEK,KAAK,CAAC,OAAO,EAAE;QAAE,sBAAsB,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"downloaderCore.d.ts","sourceRoot":"","sources":["../../../../src/client/downloader/downloaderCore.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,eAAe,MAAM,qCAAqC,CAAC;AACvE,OAAO,KAAK,SAAS,MAAM,8BAA8B,CAAC;AAC1D,OAAO,KAAK,iBAAiB,MAAM,6CAA6C,CAAC;AACjF,OAAO,cAAc,MAAM,qCAAqC,CAAC;AAEjE,OAAO,KAAK,YAAY,MAAM,uCAAuC,CAAC;AAGtE,OAAO,KAAK,eAAe,MAAM,0CAA0C,CAAC;AAC5E,OAAO,KAAK,oBAAoB,MAAM,+CAA+C,CAAC;AAEtF,OAAO,KAAK,0BAA0B,MAAM,sDAAsD,CAAC;AACnG,OAAO,KAAK,uBAAuB,MAAM,4CAA4C,CAAC;AACtF,OAAO,KAAK,YAAY,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,iBAAiB,MAAM,6CAA6C,CAAC;AACjF,OAAO,KAAK,cAAc,MAAM,oCAAoC,CAAC;AACrE,OAAO,KAAK,iBAAiB,MAAM,iDAAiD,CAAC;AAGrF,OAAO,KAAK,oBAAoB,MAAM,4CAA4C,CAAC;AACnF,OAAO,KAAK,+BAA+B,MAAM,qDAAqD,CAAC;AAEvG,MAAM,CAAC,OAAO,OAAO,cAAc;;gBAkBnB,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,oBAAoB,EAAE,0BAA0B,CAAC,MAAM,CAAC,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,+BAA+B;IAY7U,IAAI,MAAM,IAAI,cAAc,GAAG,IAAI,CAKlC;IAED,IAAI,KAAK,IAAI,iBAAiB,GAAG,IAAI,CAKpC;IAED,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAEK,KAAK,CAAC,OAAO,EAAE;QAAE,sBAAsB,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB3E,MAAM,IAAI,OAAO;IAQjB,KAAK,IAAI,OAAO;IAQhB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAI/C,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC;IAYvE,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC;IAYjE,qBAAqB,CAAC,OAAO,EAAE,oBAAoB;IAcnD,uBAAuB;IAsBvB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqC3E,OAAO,CAAC,yCAAyC;IAU3C,eAAe,CAAC,UAAU,GAAE,MAAM,GAAG,IAAW;CAqNzD"}
|
|
@@ -57,21 +57,25 @@ export default class DownloaderCore {
|
|
|
57
57
|
const pauseOptions = { ...options, pausedTrigger: resolve };
|
|
58
58
|
this.#filesDownloader.requestPause(pauseOptions);
|
|
59
59
|
});
|
|
60
|
+
if (paused)
|
|
61
|
+
this.#healthMonitor.stop();
|
|
60
62
|
if (paused)
|
|
61
63
|
await this.#downloadTracker?.saveState();
|
|
62
|
-
this.#healthMonitor.stop();
|
|
63
64
|
return paused;
|
|
64
65
|
}
|
|
65
66
|
resume() {
|
|
66
67
|
this.#throwIfNotRunning();
|
|
67
68
|
const result = this.#filesDownloader.resume();
|
|
68
|
-
|
|
69
|
+
if (result)
|
|
70
|
+
this.#healthMonitor.start();
|
|
69
71
|
return result;
|
|
70
72
|
}
|
|
71
73
|
abort() {
|
|
72
74
|
this.#throwIfNotRunning();
|
|
73
|
-
this.#
|
|
74
|
-
|
|
75
|
+
const aborted = this.#filesDownloader.requestAbort();
|
|
76
|
+
if (aborted)
|
|
77
|
+
this.#healthMonitor.stop();
|
|
78
|
+
return aborted;
|
|
75
79
|
}
|
|
76
80
|
configureThrottling(options) {
|
|
77
81
|
this.#eventsEngine.emit(TransferDownloadEvent.ConfigureThrottling, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloaderCore.js","sourceRoot":"","sources":["../../../../src/client/downloader/downloaderCore.ts"],"names":[],"mappings":"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","sourcesContent":["import ApiError from \"../../utils/api/apiError.js\";\r\nimport DownloadErrorCode from \"../../downloader/dtos/downloadErrorCode.js\";\r\nimport TransferDownloadEvent from \"../../downloader/dtos/downloadEvent.js\";\r\nimport FilesDownloader from \"../../downloader/filesDownloader.js\";\r\nimport DownloadFileInfo from \"../../downloader/dtos/downloadFileInfo.js\";\r\nimport TransferConcurrencyController from \"../../utils/concurrencyController/transferConcurrencyController.js\";\r\nimport DownloadError from '../../downloader/dtos/downloadError.js';\r\nimport UrlUtil from \"../../utils/api/urlUtil.js\";\r\nimport DownloadTracker from \"../../downloader/downloadTracker.js\";\r\nimport FilesPreparer from \"../../downloader/filesPreparer.js\";\r\nimport DownloadFolderResolver from \"../../downloader/downloadFolderResolver.js\";\r\nimport DownloadRegistrationMode from \"../../downloader/dtos/downloadRegistrationMode.js\";\r\n\r\nimport type DownloadOptions from \"../../downloader/downloadOptions.js\";\r\nimport type ApiClient from \"../../utils/api/apiClient.js\";\r\nimport type ThrottlingOptions from '../../utils/networking/throttlingOptions.js';\r\nimport TransferStatus from '../../utils/types/transferStatus.js';\r\nimport type GetTransferResponse from \"../../utils/api/dtos/getTransferResponse.js\";\r\nimport type DownloadInfo from \"../../downloader/dtos/downloadInfo.js\";\r\nimport type TransferFileDto from \"../../utils/api/dtos/transferFileDto.js\";\r\nimport type GetTransferRequest from \"../../utils/api/dtos/getTransferRequest.js\";\r\nimport type DownloadCommand from \"../../downloader/dtos/downloadCommand.js\";\r\nimport type DownloadFilesCommand from \"../../downloader/dtos/downloadFilesCommand.js\";\r\nimport type DownloaderRestoredState from \"../../downloader/dtos/downloaderRestoredState.js\";\r\nimport type ChunkStreamProviderFactory from \"../../utils/fileSystem/chunkStreamProviderFactory.js\";\r\nimport type FileServerClientFactory from \"../../utils/api/fileServerClientFactory.js\";\r\nimport type EventsEngine from \"../../utils/events/eventsEngine.js\";\r\nimport type FileSystemService from \"../../utils/fileSystem/fileSystemService.js\";\r\nimport type DownloadLogger from \"../../downloader/downloadLogger.js\";\r\nimport type DownloadEventArgs from \"../../downloader/eventArgs/downloadEventArgs.js\";\r\nimport type { DownloadCommandE2eeData } from \"../../downloader/dtos/downloadCommand.js\";\r\nimport type { DownloadTrackingMetadataE2eeData } from \"../../downloader/dtos/downloadTrackingMetadata.js\";\r\nimport type CryptographyProvider from \"../../utils/crypto/cryptographyProvider.js\";\r\nimport type TransferDownloaderHealthMonitor from \"../healthMonitor/transferDownloaderHealthMonitor.js\";\r\n\r\nexport default class DownloaderCore {\r\n readonly #apiClient: ApiClient;\r\n readonly #options: DownloadOptions;\r\n readonly #chunkProviderFactory: ChunkStreamProviderFactory<string>;\r\n readonly #fileserverClientFactory: FileServerClientFactory;\r\n readonly #fileSystem: FileSystemService;\r\n readonly #eventsEngine: EventsEngine;\r\n readonly #logger: DownloadLogger;\r\n readonly #cryptography: CryptographyProvider;\r\n readonly #healthMonitor: TransferDownloaderHealthMonitor;\r\n\r\n #downloadId?: string;\r\n #transferId?: string;\r\n #trackId?: string;\r\n #filesDownloader: FilesDownloader | null = null;\r\n #downloadTracker: DownloadTracker | null = null;\r\n #downloadRegistrationMode: DownloadRegistrationMode = DownloadRegistrationMode.Transfer;\r\n\r\n constructor(options: DownloadOptions, logger: DownloadLogger, apiClient: ApiClient, chunkProviderFactory: ChunkStreamProviderFactory<string>, fileserverClientFactory: FileServerClientFactory, fileSystem: FileSystemService, eventsEngine: EventsEngine, cryptography: CryptographyProvider, healthMonitor: TransferDownloaderHealthMonitor) {\r\n this.#options = options;\r\n this.#logger = logger;\r\n this.#apiClient = apiClient;\r\n this.#chunkProviderFactory = chunkProviderFactory;\r\n this.#fileserverClientFactory = fileserverClientFactory;\r\n this.#fileSystem = fileSystem;\r\n this.#eventsEngine = eventsEngine;\r\n this.#cryptography = cryptography;\r\n this.#healthMonitor = healthMonitor;\r\n }\r\n\r\n get status(): TransferStatus | null {\r\n if (this.#filesDownloader === null)\r\n return null;\r\n\r\n return this.#filesDownloader.status;\r\n }\r\n\r\n get state(): DownloadEventArgs | null {\r\n if (this.#filesDownloader === null)\r\n return null;\r\n\r\n return this.#filesDownloader.getCurrentState(true);\r\n }\r\n\r\n get downloadId(): string | undefined {\r\n return this.#downloadId;\r\n }\r\n\r\n async pause(options: { interruptPendingChunks: boolean }): Promise<boolean> {\r\n this.#throwIfNotRunning();\r\n\r\n const paused = await new Promise<boolean>(resolve => {\r\n const pauseOptions = { ...options, pausedTrigger: resolve };\r\n this.#filesDownloader!.requestPause(pauseOptions);\r\n });\r\n\r\n if (paused)\r\n await this.#downloadTracker?.saveState();\r\n\r\n this.#healthMonitor.stop();\r\n\r\n return paused;\r\n }\r\n\r\n resume(): boolean {\r\n this.#throwIfNotRunning();\r\n const result = this.#filesDownloader!.resume();\r\n this.#healthMonitor.start();\r\n return result;\r\n }\r\n\r\n abort(): boolean {\r\n this.#throwIfNotRunning();\r\n this.#healthMonitor.stop();\r\n return this.#filesDownloader!.requestAbort();\r\n }\r\n\r\n configureThrottling(options: ThrottlingOptions): void {\r\n this.#eventsEngine.emit(TransferDownloadEvent.ConfigureThrottling, options);\r\n }\r\n\r\n async downloadAllFilesZipped(command: DownloadCommand): Promise<DownloadInfo> {\r\n this.#throwIfRunning();\r\n this.#validateOptions();\r\n\r\n this.#trackId = command.trackId;\r\n const transfer = await this.#getEffectiveTransfer(command, 0);\r\n\r\n this.#downloadRegistrationMode = DownloadRegistrationMode.Transfer;\r\n\r\n return this.#downloadZipped(transfer, command.e2eeDataOrTextKey);\r\n }\r\n\r\n async downloadAllFiles(command: DownloadCommand): Promise<DownloadInfo> {\r\n this.#throwIfRunning();\r\n this.#validateOptions();\r\n\r\n this.#trackId = command.trackId;\r\n const transfer = await this.#getEffectiveTransfer(command, -1);\r\n\r\n this.#downloadRegistrationMode = DownloadRegistrationMode.Transfer;\r\n\r\n return this.#download(transfer, transfer.files, command.e2eeDataOrTextKey);\r\n }\r\n\r\n async downloadSpecificFiles(command: DownloadFilesCommand) {\r\n this.#throwIfRunning();\r\n this.#validateOptions();\r\n\r\n this.#trackId = command.trackId;\r\n const transfer = await this.#getEffectiveTransfer(command, 0);\r\n\r\n this.#downloadRegistrationMode = typeof transfer.numberoffiles === `number` && command.files.length === transfer.numberoffiles\r\n ? DownloadRegistrationMode.Transfer\r\n : DownloadRegistrationMode.EverySingleFile;\r\n\r\n return this.#download(transfer, command.files, command.e2eeDataOrTextKey);\r\n }\r\n\r\n async restartFailedDownloader() {\r\n if (!this.#downloadTracker)\r\n throw new Error(`Download tracker hasn't been initialized`);\r\n if (!this.#options.downloadTrackingBaseFolder)\r\n throw new Error(`Download tracking base folder hasn't been populated`);\r\n if (this.#filesDownloader?.status !== TransferStatus.Failed && this.#filesDownloader?.status !== TransferStatus.Aborted)\r\n throw new Error(`Downloader has to be in Failed or Aborted stated`);\r\n\r\n const downloadMetadata = await DownloadTracker.restoreDownloadMetadata(this.#downloadId!, this.#options.downloadTrackingBaseFolder, this.#fileSystem);\r\n const downloadCommandE2eeData = this.createDownloadCommandE2eeDataFromMetadata(downloadMetadata.e2eeData);\r\n\r\n // find out from disk the entire list of files, which were in progress and how many chunks have completed already\r\n const downloaderState = await this.#downloadTracker.restoreDownloaderState();\r\n\r\n // restart from where we left off\r\n const runningInfo = this.#run(downloaderState.files, downloadCommandE2eeData, downloadMetadata.region, downloaderState.state);\r\n runningInfo.then((di: DownloadInfo) => {\r\n this.#eventsEngine.emit(TransferDownloadEvent.RecoveredInterruptedDownload, di.initialState);\r\n });\r\n return runningInfo;\r\n }\r\n\r\n async recoverInterruptedDownload(downloadId: string): Promise<DownloadInfo> {\r\n this.#logger.applyDownloadId(downloadId);\r\n this.#logger.logInfo(`Download recovery requested for ${downloadId}`);\r\n\r\n this.#throwIfRunning();\r\n\r\n if (!this.#options.downloadTrackingBaseFolder)\r\n throw new DownloadError(`The download tracking folder is not specified for ${downloadId}`, undefined, DownloadErrorCode.TrackingNotAvailable);\r\n\r\n this.#downloadId = downloadId;\r\n this.#transferId = downloadId.split(`_`)[0]!;\r\n this.#logger.applyTransferId(this.#transferId);\r\n\r\n const downloadMetadata = await DownloadTracker.restoreDownloadMetadata(this.#downloadId!, this.#options.downloadTrackingBaseFolder, this.#fileSystem);\r\n\r\n const downloadCommandE2eeData = this.createDownloadCommandE2eeDataFromMetadata(downloadMetadata.e2eeData);\r\n\r\n this.#downloadTracker = new DownloadTracker(this.#downloadId!, this.#options, this.#fileSystem, this.#eventsEngine, downloadCommandE2eeData, downloadMetadata.region);\r\n const restoredOptions = await this.#downloadTracker.restoreDownloadOptions();\r\n\r\n // just make sure we are running the same config as when the download was last dumped to disk - otherwise things could get out of sync\r\n this.#options.downloadFolder = restoredOptions.downloadFolder;\r\n this.#options.downloadTrackingBaseFolder = restoredOptions.downloadTrackingBaseFolder;\r\n this.#options.maxChunkSize = restoredOptions.maxChunkSize;\r\n this.#options.checkChunksMd5 = restoredOptions.checkChunksMd5;\r\n\r\n // find out from disk the entire list of files, which were in progress and how many chunks have completed already\r\n const downloaderState = await this.#downloadTracker.restoreDownloaderState();\r\n\r\n // restart from where we left off\r\n const runningInfo = this.#run(downloaderState.files, downloadCommandE2eeData, downloadMetadata.region, downloaderState.state);\r\n runningInfo.then((di: DownloadInfo) => {\r\n this.#eventsEngine.emit(TransferDownloadEvent.RecoveredInterruptedDownload, di.initialState);\r\n });\r\n return runningInfo;\r\n }\r\n\r\n private createDownloadCommandE2eeDataFromMetadata(metadataE2eeData?: DownloadTrackingMetadataE2eeData): DownloadCommandE2eeData | undefined {\r\n if (!metadataE2eeData)\r\n return undefined;\r\n\r\n return {\r\n iv: Buffer.from(metadataE2eeData.ivAsHex, `hex`),\r\n key: Buffer.from(metadataE2eeData.keyAsHex, `hex`),\r\n };\r\n }\r\n\r\n async cleanupDownload(downloadId: string | null = null) {\r\n const cleanupDownloadId = downloadId ?? this.#downloadId;\r\n\r\n if (!cleanupDownloadId)\r\n throw new DownloadError(`Cannot cleanup unknown download`);\r\n\r\n if (!this.#options.downloadTrackingBaseFolder)\r\n throw new DownloadError(`The download tracking folder is not specified for download ${cleanupDownloadId}`);\r\n\r\n this.#logger.applyDownloadId(cleanupDownloadId);\r\n\r\n await DownloadTracker.removeDownloadTrackingFolder(cleanupDownloadId, this.#options.downloadTrackingBaseFolder, this.#fileSystem);\r\n }\r\n\r\n async #downloadZipped(transfer: GetTransferResponse, e2eeDataOrTextKey?: DownloadCommandE2eeData | string): Promise<DownloadInfo> {\r\n const fsClient = this.#fileserverClientFactory.createFileServerTcpDownloadClient(transfer.compressedfileurl!, null, null, transfer.id, ``, this.#options);\r\n const metaData = await fsClient.getCompressedFileMetadata();\r\n\r\n // for now fileservers are not able to validate md5 for chunks of the zip\r\n // note: this should be rather handled in files downloader logic\r\n this.#options.checkChunksMd5 = false;\r\n\r\n const zipFile: TransferFileDto = {\r\n downloadurl: transfer.compressedfileurl!,\r\n fileid: UrlUtil.getQueryStringFromUrl(transfer.compressedfileurl!).compressedfilekey!,\r\n filename: metaData.fileName,\r\n filesize: metaData.fileSize,\r\n compressed: true,\r\n };\r\n\r\n return this.#download(transfer, [zipFile], e2eeDataOrTextKey);\r\n }\r\n\r\n async #download(transfer: GetTransferResponse, files: Array<TransferFileDto>, e2eeDataOrTextKey?: DownloadCommandE2eeData | string): Promise<DownloadInfo> {\r\n this.#transferId = transfer.id;\r\n this.#downloadId = this.#generateDownloadId();\r\n\r\n this.#logger.applyDownloadId(this.#downloadId);\r\n this.#logger.applyTransferId(this.#transferId);\r\n\r\n if (this.#options.downloadFolderTemplate)\r\n this.#options.downloadFolder = DownloadFolderResolver.prepareDownloadFolder(this.#options.downloadFolderTemplate!, transfer);\r\n\r\n const e2eeData = await this.#getE2eeDataOrThrow(transfer, e2eeDataOrTextKey);\r\n\r\n const downloadFiles = this.#mapTransferFilesDtosToFileInfos(files);\r\n const preparer = new FilesPreparer(this.#options.downloadFolder!, this.#options.maxChunkSize, this.#fileSystem);\r\n preparer.prepareFiles(downloadFiles);\r\n\r\n return this.#run(downloadFiles, e2eeData, transfer.region);\r\n }\r\n\r\n async #getE2eeDataOrThrow(transfer: GetTransferResponse, e2eeDataOrTextKey?: string | DownloadCommandE2eeData): Promise<DownloadCommandE2eeData | undefined> {\r\n if (!transfer.e2ee)\r\n return undefined;\r\n\r\n if (!e2eeDataOrTextKey)\r\n throw new DownloadError(`No E2EE data or text key available to decrypt the transfer`);\r\n\r\n if (typeof e2eeDataOrTextKey === `object`)\r\n return e2eeDataOrTextKey;\r\n\r\n if (!transfer.e2ee_keyhash)\r\n throw new DownloadError(`No E2EE db data available to decrypt the transfer`);\r\n\r\n const e2eeDbDataBuffer = Buffer.from(transfer.e2ee_keyhash, `hex`);\r\n\r\n const iv = e2eeDbDataBuffer.subarray(0, 16);\r\n const salt = e2eeDbDataBuffer.subarray(16, 32);\r\n const key = await this.#cryptography.deriveKey(e2eeDataOrTextKey, salt);\r\n\r\n return { iv, key: Buffer.from(key) };\r\n }\r\n\r\n async #run(downloadFiles: Array<DownloadFileInfo>, e2eeData?: DownloadCommandE2eeData, region?: string, restoredState?: DownloaderRestoredState): Promise<DownloadInfo> {\r\n if (!this.#transferId)\r\n throw new DownloadError(`This downloader has no transfer ID specified`);\r\n\r\n if (!this.#downloadId)\r\n throw new DownloadError(`This downloader has no download ID initialized`);\r\n\r\n this.#filesDownloader = new FilesDownloader(\r\n this.#logger,\r\n this.#transferId!,\r\n this.#trackId,\r\n this.#downloadId!,\r\n region,\r\n downloadFiles,\r\n restoredState,\r\n this.#fileSystem,\r\n new TransferConcurrencyController(this.#options.parallelChunks!),\r\n this.#eventsEngine,\r\n this.#cryptography,\r\n this.#options,\r\n this.#apiClient,\r\n this.#fileserverClientFactory,\r\n this.#chunkProviderFactory,\r\n this.#downloadRegistrationMode,\r\n e2eeData,\r\n );\r\n\r\n if (this.#options.downloadTrackingBaseFolder) {\r\n if (!this.#downloadTracker)\r\n this.#downloadTracker = new DownloadTracker(this.#downloadId!, this.#options, this.#fileSystem, this.#eventsEngine, e2eeData, region);\r\n\r\n await this.#downloadTracker.start(downloadFiles);\r\n }\r\n\r\n const initialState = this.#filesDownloader.getCurrentState(false);\r\n\r\n this.#healthMonitor.start();\r\n\r\n const completion = this.#filesDownloader.start();\r\n\r\n completion.finally(() => {\r\n this.#healthMonitor.stop();\r\n });\r\n\r\n return { downloadId: this.#downloadId!, transferId: this.#transferId!, completion, initialState };\r\n }\r\n\r\n async #getEffectiveTransfer(command: DownloadCommand, fileslimit: number) {\r\n let effectiveTransfer: GetTransferResponse | null = null;\r\n\r\n if (command.transfer)\r\n effectiveTransfer = await this.#fetchTransfer(command.transfer, undefined, command.password, fileslimit, command.checkLogintoken);\r\n\r\n if (!effectiveTransfer) {\r\n // still don't know the transfer - must use track ID to fetch it\r\n if (!this.#trackId)\r\n throw new DownloadError(`Could not resolve transfer to be downloaded - missing transfer info and missing track id`);\r\n\r\n effectiveTransfer = await this.#fetchTransfer(undefined, this.#trackId!, command.password, fileslimit, command.checkLogintoken);\r\n }\r\n\r\n if (!effectiveTransfer)\r\n throw new DownloadError(`Could not resolve transfer to be downloaded - transfer=${command.transfer} track=${command.trackId}`);\r\n\r\n if (effectiveTransfer.isexpired === true)\r\n throw new DownloadError(`Transfer is expired transfer=${effectiveTransfer.id}`, undefined, DownloadErrorCode.TransferExpired);\r\n\r\n if (effectiveTransfer.blockdownloads === true)\r\n throw new DownloadError(`Transfer has downloads disabled transfer=${effectiveTransfer.id}`, undefined, DownloadErrorCode.DownloadsDisabled);\r\n\r\n if (fileslimit !== 0 && (!effectiveTransfer.files || effectiveTransfer.files.length === 0))\r\n throw new DownloadError(`Transfer has no files to download transfer=${effectiveTransfer.id}`, undefined, DownloadErrorCode.NoFilesToDownload);\r\n\r\n return effectiveTransfer;\r\n }\r\n\r\n async #fetchTransfer(\r\n transferid?: string | undefined,\r\n trackid?: string | undefined,\r\n password?: string | undefined,\r\n fileslimit?: number | undefined,\r\n checkLogintoken?: boolean,\r\n ): Promise<GetTransferResponse> {\r\n try {\r\n // specifying which file properties we want to get from API\r\n // reason: api adds lots of other unnecessary properties - downloader doesn't need them and if a transfer has lots of files (some can be up to 100k)\r\n // it makes the response much smaller (up to 80% size reduction)\r\n const request: GetTransferRequest = { fileslimit, transferid, trackid, password, fprops: [`fileid`, `filesize`, `filename`, `downloadurl`], checkLogintoken };\r\n return await this.#apiClient.getTransfer(request);\r\n }\r\n catch (error) {\r\n const code = this.#apiErrorToDownloadErrorCode(error);\r\n const downloadError = new DownloadError(`Could not fetch transfer by id=${transferid ?? `---`} track=${trackid ?? `---`}`, { cause: error as Error }, code);\r\n throw downloadError;\r\n }\r\n }\r\n\r\n #apiErrorToDownloadErrorCode(error: unknown) {\r\n if (error instanceof ApiError) {\r\n switch (error.httpStatus) {\r\n case 401: return DownloadErrorCode.InvalidTransferPassword;\r\n case 404: return DownloadErrorCode.TransferNotFound;\r\n case 409: return DownloadErrorCode.TransferInvalidState;\r\n default: break;\r\n }\r\n }\r\n return undefined;\r\n }\r\n\r\n #throwIfRunning() {\r\n if (this.#filesDownloader)\r\n throw new DownloadError(`This instance of downloader is already in use. Please create a new one.`);\r\n }\r\n\r\n #throwIfNotRunning() {\r\n if (!this.#filesDownloader)\r\n throw new DownloadError(`This instance of downloader is not running yet. Please start the download first.`);\r\n }\r\n\r\n #generateDownloadId(): string {\r\n return `${this.#transferId}_${this.#cryptography.generateRandomString(10)}`;\r\n }\r\n\r\n #mapTransferFilesDtosToFileInfos(files: Array<TransferFileDto>) {\r\n return files.map(file => new DownloadFileInfo(\r\n file.fileid,\r\n file.downloadurl,\r\n file.filename,\r\n file.filesize,\r\n ));\r\n }\r\n\r\n #validateOptions() {\r\n if (!this.#options.downloadFolder && !this.#options.downloadFolderTemplate)\r\n throw new DownloadError(`downloadFolder OR downloadFolderTemplate is required`);\r\n\r\n if (this.#options.maxChunkSize !== null && this.#options.maxChunkSize! <= 0)\r\n throw new DownloadError(`maxChunkSize must be greater than 0. Provide \"null\" if you want to download without chunking.`);\r\n }\r\n}"]}
|
|
1
|
+
{"version":3,"file":"downloaderCore.js","sourceRoot":"","sources":["../../../../src/client/downloader/downloaderCore.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,6BAA6B,CAAC;AACnD,OAAO,iBAAiB,MAAM,4CAA4C,CAAC;AAC3E,OAAO,qBAAqB,MAAM,wCAAwC,CAAC;AAC3E,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,gBAAgB,MAAM,2CAA2C,CAAC;AACzE,OAAO,6BAA6B,MAAM,oEAAoE,CAAC;AAC/G,OAAO,aAAa,MAAM,wCAAwC,CAAC;AACnE,OAAO,OAAO,MAAM,4BAA4B,CAAC;AACjD,OAAO,eAAe,MAAM,qCAAqC,CAAC;AAClE,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAChF,OAAO,wBAAwB,MAAM,mDAAmD,CAAC;AAKzF,OAAO,cAAc,MAAM,qCAAqC,CAAC;AAmBjE,MAAM,CAAC,OAAO,OAAO,cAAc;IACtB,UAAU,CAAY;IACtB,QAAQ,CAAkB;IAC1B,qBAAqB,CAAqC;IAC1D,wBAAwB,CAA0B;IAClD,WAAW,CAAoB;IAC/B,aAAa,CAAe;IAC5B,OAAO,CAAiB;IACxB,aAAa,CAAuB;IACpC,cAAc,CAAkC;IAEzD,WAAW,CAAU;IACrB,WAAW,CAAU;IACrB,QAAQ,CAAU;IAClB,gBAAgB,GAA2B,IAAI,CAAC;IAChD,gBAAgB,GAA2B,IAAI,CAAC;IAChD,yBAAyB,GAA6B,wBAAwB,CAAC,QAAQ,CAAC;IAExF,YAAY,OAAwB,EAAE,MAAsB,EAAE,SAAoB,EAAE,oBAAwD,EAAE,uBAAgD,EAAE,UAA6B,EAAE,YAA0B,EAAE,YAAkC,EAAE,aAA8C;QACzU,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,qBAAqB,GAAG,oBAAoB,CAAC;QAClD,IAAI,CAAC,wBAAwB,GAAG,uBAAuB,CAAC;QACxD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;IACxC,CAAC;IAED,IAAI,MAAM;QACN,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI;YAC9B,OAAO,IAAI,CAAC;QAEhB,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;IACxC,CAAC;IAED,IAAI,KAAK;QACL,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI;YAC9B,OAAO,IAAI,CAAC;QAEhB,OAAO,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAA4C;QACpD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAU,OAAO,CAAC,EAAE;YAChD,MAAM,YAAY,GAAG,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;YAC5D,IAAI,CAAC,gBAAiB,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM;YACN,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAE/B,IAAI,MAAM;YACN,MAAM,IAAI,CAAC,gBAAgB,EAAE,SAAS,EAAE,CAAC;QAE7C,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,MAAM;QACF,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAiB,CAAC,MAAM,EAAE,CAAC;QAC/C,IAAI,MAAM;YACN,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAChC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK;QACD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAiB,CAAC,YAAY,EAAE,CAAC;QACtD,IAAI,OAAO;YACP,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,mBAAmB,CAAC,OAA0B;QAC1C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,qBAAqB,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,OAAwB;QACjD,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAE9D,IAAI,CAAC,yBAAyB,GAAG,wBAAwB,CAAC,QAAQ,CAAC;QAEnE,OAAO,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,OAAwB;QAC3C,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAE/D,IAAI,CAAC,yBAAyB,GAAG,wBAAwB,CAAC,QAAQ,CAAC;QAEnE,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,OAA6B;QACrD,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAE9D,IAAI,CAAC,yBAAyB,GAAG,OAAO,QAAQ,CAAC,aAAa,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,aAAa;YAC1H,CAAC,CAAC,wBAAwB,CAAC,QAAQ;YACnC,CAAC,CAAC,wBAAwB,CAAC,eAAe,CAAC;QAE/C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,uBAAuB;QACzB,IAAI,CAAC,IAAI,CAAC,gBAAgB;YACtB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,0BAA0B;YACzC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAC3E,IAAI,IAAI,CAAC,gBAAgB,EAAE,MAAM,KAAK,cAAc,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,EAAE,MAAM,KAAK,cAAc,CAAC,OAAO;YACnH,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAExE,MAAM,gBAAgB,GAAG,MAAM,eAAe,CAAC,uBAAuB,CAAC,IAAI,CAAC,WAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,0BAA0B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACtJ,MAAM,uBAAuB,GAAG,IAAI,CAAC,yCAAyC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAE1G,iHAAiH;QACjH,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,sBAAsB,EAAE,CAAC;QAE7E,iCAAiC;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;QAC9H,WAAW,CAAC,IAAI,CAAC,CAAC,EAAgB,EAAE,EAAE;YAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,qBAAqB,CAAC,4BAA4B,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;QACjG,CAAC,CAAC,CAAC;QACH,OAAO,WAAW,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,0BAA0B,CAAC,UAAkB;QAC/C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,mCAAmC,UAAU,EAAE,CAAC,CAAC;QAEtE,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,0BAA0B;YACzC,MAAM,IAAI,aAAa,CAAC,qDAAqD,UAAU,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC,oBAAoB,CAAC,CAAC;QAElJ,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE/C,MAAM,gBAAgB,GAAG,MAAM,eAAe,CAAC,uBAAuB,CAAC,IAAI,CAAC,WAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,0BAA0B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAEtJ,MAAM,uBAAuB,GAAG,IAAI,CAAC,yCAAyC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAE1G,IAAI,CAAC,gBAAgB,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,WAAY,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,EAAE,uBAAuB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACtK,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,sBAAsB,EAAE,CAAC;QAE7E,sIAAsI;QACtI,IAAI,CAAC,QAAQ,CAAC,cAAc,GAAG,eAAe,CAAC,cAAc,CAAC;QAC9D,IAAI,CAAC,QAAQ,CAAC,0BAA0B,GAAG,eAAe,CAAC,0BAA0B,CAAC;QACtF,IAAI,CAAC,QAAQ,CAAC,YAAY,GAAG,eAAe,CAAC,YAAY,CAAC;QAC1D,IAAI,CAAC,QAAQ,CAAC,cAAc,GAAG,eAAe,CAAC,cAAc,CAAC;QAE9D,iHAAiH;QACjH,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,sBAAsB,EAAE,CAAC;QAE7E,iCAAiC;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;QAC9H,WAAW,CAAC,IAAI,CAAC,CAAC,EAAgB,EAAE,EAAE;YAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,qBAAqB,CAAC,4BAA4B,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;QACjG,CAAC,CAAC,CAAC;QACH,OAAO,WAAW,CAAC;IACvB,CAAC;IAEO,yCAAyC,CAAC,gBAAmD;QACjG,IAAI,CAAC,gBAAgB;YACjB,OAAO,SAAS,CAAC;QAErB,OAAO;YACH,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC;YAChD,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC;SACrD,CAAC;IACN,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,aAA4B,IAAI;QAClD,MAAM,iBAAiB,GAAG,UAAU,IAAI,IAAI,CAAC,WAAW,CAAC;QAEzD,IAAI,CAAC,iBAAiB;YAClB,MAAM,IAAI,aAAa,CAAC,iCAAiC,CAAC,CAAC;QAE/D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,0BAA0B;YACzC,MAAM,IAAI,aAAa,CAAC,8DAA8D,iBAAiB,EAAE,CAAC,CAAC;QAE/G,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;QAEhD,MAAM,eAAe,CAAC,4BAA4B,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,0BAA0B,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtI,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAA6B,EAAE,iBAAoD;QACrG,MAAM,QAAQ,GAAG,IAAI,CAAC,wBAAwB,CAAC,iCAAiC,CAAC,QAAQ,CAAC,iBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1J,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,yBAAyB,EAAE,CAAC;QAE5D,yEAAyE;QACzE,gEAAgE;QAChE,IAAI,CAAC,QAAQ,CAAC,cAAc,GAAG,KAAK,CAAC;QAErC,MAAM,OAAO,GAAoB;YAC7B,WAAW,EAAE,QAAQ,CAAC,iBAAkB;YACxC,MAAM,EAAE,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,iBAAkB,CAAC,CAAC,iBAAkB;YACrF,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,UAAU,EAAE,IAAI;SACnB,CAAC;QAEF,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAA6B,EAAE,KAA6B,EAAE,iBAAoD;QAC9H,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAE9C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE/C,IAAI,IAAI,CAAC,QAAQ,CAAC,sBAAsB;YACpC,IAAI,CAAC,QAAQ,CAAC,cAAc,GAAG,sBAAsB,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAuB,EAAE,QAAQ,CAAC,CAAC;QAEjI,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAE7E,MAAM,aAAa,GAAG,IAAI,CAAC,gCAAgC,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAChH,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QAErC,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,QAA6B,EAAE,iBAAoD;QACzG,IAAI,CAAC,QAAQ,CAAC,IAAI;YACd,OAAO,SAAS,CAAC;QAErB,IAAI,CAAC,iBAAiB;YAClB,MAAM,IAAI,aAAa,CAAC,4DAA4D,CAAC,CAAC;QAE1F,IAAI,OAAO,iBAAiB,KAAK,QAAQ;YACrC,OAAO,iBAAiB,CAAC;QAE7B,IAAI,CAAC,QAAQ,CAAC,YAAY;YACtB,MAAM,IAAI,aAAa,CAAC,mDAAmD,CAAC,CAAC;QAEjF,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAEnE,MAAM,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;QAExE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,aAAsC,EAAE,QAAkC,EAAE,MAAe,EAAE,aAAuC;QAC3I,IAAI,CAAC,IAAI,CAAC,WAAW;YACjB,MAAM,IAAI,aAAa,CAAC,8CAA8C,CAAC,CAAC;QAE5E,IAAI,CAAC,IAAI,CAAC,WAAW;YACjB,MAAM,IAAI,aAAa,CAAC,gDAAgD,CAAC,CAAC;QAE9E,IAAI,CAAC,gBAAgB,GAAG,IAAI,eAAe,CACvC,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,WAAY,EACjB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,WAAY,EACjB,MAAM,EACN,aAAa,EACb,aAAa,EACb,IAAI,CAAC,WAAW,EAChB,IAAI,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAe,CAAC,EAChE,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,wBAAwB,EAC7B,IAAI,CAAC,qBAAqB,EAC1B,IAAI,CAAC,yBAAyB,EAC9B,QAAQ,CACX,CAAC;QAEF,IAAI,IAAI,CAAC,QAAQ,CAAC,0BAA0B,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,CAAC,gBAAgB;gBACtB,IAAI,CAAC,gBAAgB,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,WAAY,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YAE1I,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAElE,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAEjD,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE;YACpB,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,WAAY,EAAE,UAAU,EAAE,IAAI,CAAC,WAAY,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;IACtG,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,OAAwB,EAAE,UAAkB;QACpE,IAAI,iBAAiB,GAA+B,IAAI,CAAC;QAEzD,IAAI,OAAO,CAAC,QAAQ;YAChB,iBAAiB,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;QAEtI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACrB,gEAAgE;YAChE,IAAI,CAAC,IAAI,CAAC,QAAQ;gBACd,MAAM,IAAI,aAAa,CAAC,0FAA0F,CAAC,CAAC;YAExH,iBAAiB,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,QAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;QACpI,CAAC;QAED,IAAI,CAAC,iBAAiB;YAClB,MAAM,IAAI,aAAa,CAAC,0DAA0D,OAAO,CAAC,QAAQ,UAAU,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAEnI,IAAI,iBAAiB,CAAC,SAAS,KAAK,IAAI;YACpC,MAAM,IAAI,aAAa,CAAC,gCAAgC,iBAAiB,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAElI,IAAI,iBAAiB,CAAC,cAAc,KAAK,IAAI;YACzC,MAAM,IAAI,aAAa,CAAC,4CAA4C,iBAAiB,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;QAEhJ,IAAI,UAAU,KAAK,CAAC,IAAI,CAAC,CAAC,iBAAiB,CAAC,KAAK,IAAI,iBAAiB,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;YACtF,MAAM,IAAI,aAAa,CAAC,8CAA8C,iBAAiB,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;QAElJ,OAAO,iBAAiB,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,cAAc,CAChB,UAA+B,EAC/B,OAA4B,EAC5B,QAA6B,EAC7B,UAA+B,EAC/B,eAAyB;QAEzB,IAAI,CAAC;YACD,2DAA2D;YAC3D,oJAAoJ;YACpJ,gEAAgE;YAChE,MAAM,OAAO,GAAuB,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;YAC9J,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,kCAAkC,UAAU,IAAI,KAAK,UAAU,OAAO,IAAI,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE,IAAI,CAAC,CAAC;YAC5J,MAAM,aAAa,CAAC;QACxB,CAAC;IACL,CAAC;IAED,4BAA4B,CAAC,KAAc;QACvC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC5B,QAAQ,KAAK,CAAC,UAAU,EAAE,CAAC;gBACvB,KAAK,GAAG,CAAC,CAAC,OAAO,iBAAiB,CAAC,uBAAuB,CAAC;gBAC3D,KAAK,GAAG,CAAC,CAAC,OAAO,iBAAiB,CAAC,gBAAgB,CAAC;gBACpD,KAAK,GAAG,CAAC,CAAC,OAAO,iBAAiB,CAAC,oBAAoB,CAAC;gBACxD,OAAO,CAAC,CAAC,MAAM;YACnB,CAAC;QACL,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,eAAe;QACX,IAAI,IAAI,CAAC,gBAAgB;YACrB,MAAM,IAAI,aAAa,CAAC,yEAAyE,CAAC,CAAC;IAC3G,CAAC;IAED,kBAAkB;QACd,IAAI,CAAC,IAAI,CAAC,gBAAgB;YACtB,MAAM,IAAI,aAAa,CAAC,kFAAkF,CAAC,CAAC;IACpH,CAAC;IAED,mBAAmB;QACf,OAAO,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC;IAED,gCAAgC,CAAC,KAA6B;QAC1D,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,gBAAgB,CACzC,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,QAAQ,CAChB,CAAC,CAAC;IACP,CAAC;IAED,gBAAgB;QACZ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAsB;YACtE,MAAM,IAAI,aAAa,CAAC,sDAAsD,CAAC,CAAC;QAEpF,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAa,IAAI,CAAC;YACvE,MAAM,IAAI,aAAa,CAAC,+FAA+F,CAAC,CAAC;IACjI,CAAC;CACJ","sourcesContent":["import ApiError from \"../../utils/api/apiError.js\";\r\nimport DownloadErrorCode from \"../../downloader/dtos/downloadErrorCode.js\";\r\nimport TransferDownloadEvent from \"../../downloader/dtos/downloadEvent.js\";\r\nimport FilesDownloader from \"../../downloader/filesDownloader.js\";\r\nimport DownloadFileInfo from \"../../downloader/dtos/downloadFileInfo.js\";\r\nimport TransferConcurrencyController from \"../../utils/concurrencyController/transferConcurrencyController.js\";\r\nimport DownloadError from '../../downloader/dtos/downloadError.js';\r\nimport UrlUtil from \"../../utils/api/urlUtil.js\";\r\nimport DownloadTracker from \"../../downloader/downloadTracker.js\";\r\nimport FilesPreparer from \"../../downloader/filesPreparer.js\";\r\nimport DownloadFolderResolver from \"../../downloader/downloadFolderResolver.js\";\r\nimport DownloadRegistrationMode from \"../../downloader/dtos/downloadRegistrationMode.js\";\r\n\r\nimport type DownloadOptions from \"../../downloader/downloadOptions.js\";\r\nimport type ApiClient from \"../../utils/api/apiClient.js\";\r\nimport type ThrottlingOptions from '../../utils/networking/throttlingOptions.js';\r\nimport TransferStatus from '../../utils/types/transferStatus.js';\r\nimport type GetTransferResponse from \"../../utils/api/dtos/getTransferResponse.js\";\r\nimport type DownloadInfo from \"../../downloader/dtos/downloadInfo.js\";\r\nimport type TransferFileDto from \"../../utils/api/dtos/transferFileDto.js\";\r\nimport type GetTransferRequest from \"../../utils/api/dtos/getTransferRequest.js\";\r\nimport type DownloadCommand from \"../../downloader/dtos/downloadCommand.js\";\r\nimport type DownloadFilesCommand from \"../../downloader/dtos/downloadFilesCommand.js\";\r\nimport type DownloaderRestoredState from \"../../downloader/dtos/downloaderRestoredState.js\";\r\nimport type ChunkStreamProviderFactory from \"../../utils/fileSystem/chunkStreamProviderFactory.js\";\r\nimport type FileServerClientFactory from \"../../utils/api/fileServerClientFactory.js\";\r\nimport type EventsEngine from \"../../utils/events/eventsEngine.js\";\r\nimport type FileSystemService from \"../../utils/fileSystem/fileSystemService.js\";\r\nimport type DownloadLogger from \"../../downloader/downloadLogger.js\";\r\nimport type DownloadEventArgs from \"../../downloader/eventArgs/downloadEventArgs.js\";\r\nimport type { DownloadCommandE2eeData } from \"../../downloader/dtos/downloadCommand.js\";\r\nimport type { DownloadTrackingMetadataE2eeData } from \"../../downloader/dtos/downloadTrackingMetadata.js\";\r\nimport type CryptographyProvider from \"../../utils/crypto/cryptographyProvider.js\";\r\nimport type TransferDownloaderHealthMonitor from \"../healthMonitor/transferDownloaderHealthMonitor.js\";\r\n\r\nexport default class DownloaderCore {\r\n readonly #apiClient: ApiClient;\r\n readonly #options: DownloadOptions;\r\n readonly #chunkProviderFactory: ChunkStreamProviderFactory<string>;\r\n readonly #fileserverClientFactory: FileServerClientFactory;\r\n readonly #fileSystem: FileSystemService;\r\n readonly #eventsEngine: EventsEngine;\r\n readonly #logger: DownloadLogger;\r\n readonly #cryptography: CryptographyProvider;\r\n readonly #healthMonitor: TransferDownloaderHealthMonitor;\r\n\r\n #downloadId?: string;\r\n #transferId?: string;\r\n #trackId?: string;\r\n #filesDownloader: FilesDownloader | null = null;\r\n #downloadTracker: DownloadTracker | null = null;\r\n #downloadRegistrationMode: DownloadRegistrationMode = DownloadRegistrationMode.Transfer;\r\n\r\n constructor(options: DownloadOptions, logger: DownloadLogger, apiClient: ApiClient, chunkProviderFactory: ChunkStreamProviderFactory<string>, fileserverClientFactory: FileServerClientFactory, fileSystem: FileSystemService, eventsEngine: EventsEngine, cryptography: CryptographyProvider, healthMonitor: TransferDownloaderHealthMonitor) {\r\n this.#options = options;\r\n this.#logger = logger;\r\n this.#apiClient = apiClient;\r\n this.#chunkProviderFactory = chunkProviderFactory;\r\n this.#fileserverClientFactory = fileserverClientFactory;\r\n this.#fileSystem = fileSystem;\r\n this.#eventsEngine = eventsEngine;\r\n this.#cryptography = cryptography;\r\n this.#healthMonitor = healthMonitor;\r\n }\r\n\r\n get status(): TransferStatus | null {\r\n if (this.#filesDownloader === null)\r\n return null;\r\n\r\n return this.#filesDownloader.status;\r\n }\r\n\r\n get state(): DownloadEventArgs | null {\r\n if (this.#filesDownloader === null)\r\n return null;\r\n\r\n return this.#filesDownloader.getCurrentState(true);\r\n }\r\n\r\n get downloadId(): string | undefined {\r\n return this.#downloadId;\r\n }\r\n\r\n async pause(options: { interruptPendingChunks: boolean }): Promise<boolean> {\r\n this.#throwIfNotRunning();\r\n\r\n const paused = await new Promise<boolean>(resolve => {\r\n const pauseOptions = { ...options, pausedTrigger: resolve };\r\n this.#filesDownloader!.requestPause(pauseOptions);\r\n });\r\n\r\n if (paused)\r\n this.#healthMonitor.stop();\r\n\r\n if (paused)\r\n await this.#downloadTracker?.saveState();\r\n\r\n return paused;\r\n }\r\n\r\n resume(): boolean {\r\n this.#throwIfNotRunning();\r\n const result = this.#filesDownloader!.resume();\r\n if (result)\r\n this.#healthMonitor.start();\r\n return result;\r\n }\r\n\r\n abort(): boolean {\r\n this.#throwIfNotRunning();\r\n const aborted = this.#filesDownloader!.requestAbort();\r\n if (aborted)\r\n this.#healthMonitor.stop();\r\n return aborted;\r\n }\r\n\r\n configureThrottling(options: ThrottlingOptions): void {\r\n this.#eventsEngine.emit(TransferDownloadEvent.ConfigureThrottling, options);\r\n }\r\n\r\n async downloadAllFilesZipped(command: DownloadCommand): Promise<DownloadInfo> {\r\n this.#throwIfRunning();\r\n this.#validateOptions();\r\n\r\n this.#trackId = command.trackId;\r\n const transfer = await this.#getEffectiveTransfer(command, 0);\r\n\r\n this.#downloadRegistrationMode = DownloadRegistrationMode.Transfer;\r\n\r\n return this.#downloadZipped(transfer, command.e2eeDataOrTextKey);\r\n }\r\n\r\n async downloadAllFiles(command: DownloadCommand): Promise<DownloadInfo> {\r\n this.#throwIfRunning();\r\n this.#validateOptions();\r\n\r\n this.#trackId = command.trackId;\r\n const transfer = await this.#getEffectiveTransfer(command, -1);\r\n\r\n this.#downloadRegistrationMode = DownloadRegistrationMode.Transfer;\r\n\r\n return this.#download(transfer, transfer.files, command.e2eeDataOrTextKey);\r\n }\r\n\r\n async downloadSpecificFiles(command: DownloadFilesCommand) {\r\n this.#throwIfRunning();\r\n this.#validateOptions();\r\n\r\n this.#trackId = command.trackId;\r\n const transfer = await this.#getEffectiveTransfer(command, 0);\r\n\r\n this.#downloadRegistrationMode = typeof transfer.numberoffiles === `number` && command.files.length === transfer.numberoffiles\r\n ? DownloadRegistrationMode.Transfer\r\n : DownloadRegistrationMode.EverySingleFile;\r\n\r\n return this.#download(transfer, command.files, command.e2eeDataOrTextKey);\r\n }\r\n\r\n async restartFailedDownloader() {\r\n if (!this.#downloadTracker)\r\n throw new Error(`Download tracker hasn't been initialized`);\r\n if (!this.#options.downloadTrackingBaseFolder)\r\n throw new Error(`Download tracking base folder hasn't been populated`);\r\n if (this.#filesDownloader?.status !== TransferStatus.Failed && this.#filesDownloader?.status !== TransferStatus.Aborted)\r\n throw new Error(`Downloader has to be in Failed or Aborted stated`);\r\n\r\n const downloadMetadata = await DownloadTracker.restoreDownloadMetadata(this.#downloadId!, this.#options.downloadTrackingBaseFolder, this.#fileSystem);\r\n const downloadCommandE2eeData = this.createDownloadCommandE2eeDataFromMetadata(downloadMetadata.e2eeData);\r\n\r\n // find out from disk the entire list of files, which were in progress and how many chunks have completed already\r\n const downloaderState = await this.#downloadTracker.restoreDownloaderState();\r\n\r\n // restart from where we left off\r\n const runningInfo = this.#run(downloaderState.files, downloadCommandE2eeData, downloadMetadata.region, downloaderState.state);\r\n runningInfo.then((di: DownloadInfo) => {\r\n this.#eventsEngine.emit(TransferDownloadEvent.RecoveredInterruptedDownload, di.initialState);\r\n });\r\n return runningInfo;\r\n }\r\n\r\n async recoverInterruptedDownload(downloadId: string): Promise<DownloadInfo> {\r\n this.#logger.applyDownloadId(downloadId);\r\n this.#logger.logInfo(`Download recovery requested for ${downloadId}`);\r\n\r\n this.#throwIfRunning();\r\n\r\n if (!this.#options.downloadTrackingBaseFolder)\r\n throw new DownloadError(`The download tracking folder is not specified for ${downloadId}`, undefined, DownloadErrorCode.TrackingNotAvailable);\r\n\r\n this.#downloadId = downloadId;\r\n this.#transferId = downloadId.split(`_`)[0]!;\r\n this.#logger.applyTransferId(this.#transferId);\r\n\r\n const downloadMetadata = await DownloadTracker.restoreDownloadMetadata(this.#downloadId!, this.#options.downloadTrackingBaseFolder, this.#fileSystem);\r\n\r\n const downloadCommandE2eeData = this.createDownloadCommandE2eeDataFromMetadata(downloadMetadata.e2eeData);\r\n\r\n this.#downloadTracker = new DownloadTracker(this.#downloadId!, this.#options, this.#fileSystem, this.#eventsEngine, downloadCommandE2eeData, downloadMetadata.region);\r\n const restoredOptions = await this.#downloadTracker.restoreDownloadOptions();\r\n\r\n // just make sure we are running the same config as when the download was last dumped to disk - otherwise things could get out of sync\r\n this.#options.downloadFolder = restoredOptions.downloadFolder;\r\n this.#options.downloadTrackingBaseFolder = restoredOptions.downloadTrackingBaseFolder;\r\n this.#options.maxChunkSize = restoredOptions.maxChunkSize;\r\n this.#options.checkChunksMd5 = restoredOptions.checkChunksMd5;\r\n\r\n // find out from disk the entire list of files, which were in progress and how many chunks have completed already\r\n const downloaderState = await this.#downloadTracker.restoreDownloaderState();\r\n\r\n // restart from where we left off\r\n const runningInfo = this.#run(downloaderState.files, downloadCommandE2eeData, downloadMetadata.region, downloaderState.state);\r\n runningInfo.then((di: DownloadInfo) => {\r\n this.#eventsEngine.emit(TransferDownloadEvent.RecoveredInterruptedDownload, di.initialState);\r\n });\r\n return runningInfo;\r\n }\r\n\r\n private createDownloadCommandE2eeDataFromMetadata(metadataE2eeData?: DownloadTrackingMetadataE2eeData): DownloadCommandE2eeData | undefined {\r\n if (!metadataE2eeData)\r\n return undefined;\r\n\r\n return {\r\n iv: Buffer.from(metadataE2eeData.ivAsHex, `hex`),\r\n key: Buffer.from(metadataE2eeData.keyAsHex, `hex`),\r\n };\r\n }\r\n\r\n async cleanupDownload(downloadId: string | null = null) {\r\n const cleanupDownloadId = downloadId ?? this.#downloadId;\r\n\r\n if (!cleanupDownloadId)\r\n throw new DownloadError(`Cannot cleanup unknown download`);\r\n\r\n if (!this.#options.downloadTrackingBaseFolder)\r\n throw new DownloadError(`The download tracking folder is not specified for download ${cleanupDownloadId}`);\r\n\r\n this.#logger.applyDownloadId(cleanupDownloadId);\r\n\r\n await DownloadTracker.removeDownloadTrackingFolder(cleanupDownloadId, this.#options.downloadTrackingBaseFolder, this.#fileSystem);\r\n }\r\n\r\n async #downloadZipped(transfer: GetTransferResponse, e2eeDataOrTextKey?: DownloadCommandE2eeData | string): Promise<DownloadInfo> {\r\n const fsClient = this.#fileserverClientFactory.createFileServerTcpDownloadClient(transfer.compressedfileurl!, null, null, transfer.id, ``, this.#options);\r\n const metaData = await fsClient.getCompressedFileMetadata();\r\n\r\n // for now fileservers are not able to validate md5 for chunks of the zip\r\n // note: this should be rather handled in files downloader logic\r\n this.#options.checkChunksMd5 = false;\r\n\r\n const zipFile: TransferFileDto = {\r\n downloadurl: transfer.compressedfileurl!,\r\n fileid: UrlUtil.getQueryStringFromUrl(transfer.compressedfileurl!).compressedfilekey!,\r\n filename: metaData.fileName,\r\n filesize: metaData.fileSize,\r\n compressed: true,\r\n };\r\n\r\n return this.#download(transfer, [zipFile], e2eeDataOrTextKey);\r\n }\r\n\r\n async #download(transfer: GetTransferResponse, files: Array<TransferFileDto>, e2eeDataOrTextKey?: DownloadCommandE2eeData | string): Promise<DownloadInfo> {\r\n this.#transferId = transfer.id;\r\n this.#downloadId = this.#generateDownloadId();\r\n\r\n this.#logger.applyDownloadId(this.#downloadId);\r\n this.#logger.applyTransferId(this.#transferId);\r\n\r\n if (this.#options.downloadFolderTemplate)\r\n this.#options.downloadFolder = DownloadFolderResolver.prepareDownloadFolder(this.#options.downloadFolderTemplate!, transfer);\r\n\r\n const e2eeData = await this.#getE2eeDataOrThrow(transfer, e2eeDataOrTextKey);\r\n\r\n const downloadFiles = this.#mapTransferFilesDtosToFileInfos(files);\r\n const preparer = new FilesPreparer(this.#options.downloadFolder!, this.#options.maxChunkSize, this.#fileSystem);\r\n preparer.prepareFiles(downloadFiles);\r\n\r\n return this.#run(downloadFiles, e2eeData, transfer.region);\r\n }\r\n\r\n async #getE2eeDataOrThrow(transfer: GetTransferResponse, e2eeDataOrTextKey?: string | DownloadCommandE2eeData): Promise<DownloadCommandE2eeData | undefined> {\r\n if (!transfer.e2ee)\r\n return undefined;\r\n\r\n if (!e2eeDataOrTextKey)\r\n throw new DownloadError(`No E2EE data or text key available to decrypt the transfer`);\r\n\r\n if (typeof e2eeDataOrTextKey === `object`)\r\n return e2eeDataOrTextKey;\r\n\r\n if (!transfer.e2ee_keyhash)\r\n throw new DownloadError(`No E2EE db data available to decrypt the transfer`);\r\n\r\n const e2eeDbDataBuffer = Buffer.from(transfer.e2ee_keyhash, `hex`);\r\n\r\n const iv = e2eeDbDataBuffer.subarray(0, 16);\r\n const salt = e2eeDbDataBuffer.subarray(16, 32);\r\n const key = await this.#cryptography.deriveKey(e2eeDataOrTextKey, salt);\r\n\r\n return { iv, key: Buffer.from(key) };\r\n }\r\n\r\n async #run(downloadFiles: Array<DownloadFileInfo>, e2eeData?: DownloadCommandE2eeData, region?: string, restoredState?: DownloaderRestoredState): Promise<DownloadInfo> {\r\n if (!this.#transferId)\r\n throw new DownloadError(`This downloader has no transfer ID specified`);\r\n\r\n if (!this.#downloadId)\r\n throw new DownloadError(`This downloader has no download ID initialized`);\r\n\r\n this.#filesDownloader = new FilesDownloader(\r\n this.#logger,\r\n this.#transferId!,\r\n this.#trackId,\r\n this.#downloadId!,\r\n region,\r\n downloadFiles,\r\n restoredState,\r\n this.#fileSystem,\r\n new TransferConcurrencyController(this.#options.parallelChunks!),\r\n this.#eventsEngine,\r\n this.#cryptography,\r\n this.#options,\r\n this.#apiClient,\r\n this.#fileserverClientFactory,\r\n this.#chunkProviderFactory,\r\n this.#downloadRegistrationMode,\r\n e2eeData,\r\n );\r\n\r\n if (this.#options.downloadTrackingBaseFolder) {\r\n if (!this.#downloadTracker)\r\n this.#downloadTracker = new DownloadTracker(this.#downloadId!, this.#options, this.#fileSystem, this.#eventsEngine, e2eeData, region);\r\n\r\n await this.#downloadTracker.start(downloadFiles);\r\n }\r\n\r\n const initialState = this.#filesDownloader.getCurrentState(false);\r\n\r\n this.#healthMonitor.start();\r\n\r\n const completion = this.#filesDownloader.start();\r\n\r\n completion.finally(() => {\r\n this.#healthMonitor.stop();\r\n });\r\n\r\n return { downloadId: this.#downloadId!, transferId: this.#transferId!, completion, initialState };\r\n }\r\n\r\n async #getEffectiveTransfer(command: DownloadCommand, fileslimit: number) {\r\n let effectiveTransfer: GetTransferResponse | null = null;\r\n\r\n if (command.transfer)\r\n effectiveTransfer = await this.#fetchTransfer(command.transfer, undefined, command.password, fileslimit, command.checkLogintoken);\r\n\r\n if (!effectiveTransfer) {\r\n // still don't know the transfer - must use track ID to fetch it\r\n if (!this.#trackId)\r\n throw new DownloadError(`Could not resolve transfer to be downloaded - missing transfer info and missing track id`);\r\n\r\n effectiveTransfer = await this.#fetchTransfer(undefined, this.#trackId!, command.password, fileslimit, command.checkLogintoken);\r\n }\r\n\r\n if (!effectiveTransfer)\r\n throw new DownloadError(`Could not resolve transfer to be downloaded - transfer=${command.transfer} track=${command.trackId}`);\r\n\r\n if (effectiveTransfer.isexpired === true)\r\n throw new DownloadError(`Transfer is expired transfer=${effectiveTransfer.id}`, undefined, DownloadErrorCode.TransferExpired);\r\n\r\n if (effectiveTransfer.blockdownloads === true)\r\n throw new DownloadError(`Transfer has downloads disabled transfer=${effectiveTransfer.id}`, undefined, DownloadErrorCode.DownloadsDisabled);\r\n\r\n if (fileslimit !== 0 && (!effectiveTransfer.files || effectiveTransfer.files.length === 0))\r\n throw new DownloadError(`Transfer has no files to download transfer=${effectiveTransfer.id}`, undefined, DownloadErrorCode.NoFilesToDownload);\r\n\r\n return effectiveTransfer;\r\n }\r\n\r\n async #fetchTransfer(\r\n transferid?: string | undefined,\r\n trackid?: string | undefined,\r\n password?: string | undefined,\r\n fileslimit?: number | undefined,\r\n checkLogintoken?: boolean,\r\n ): Promise<GetTransferResponse> {\r\n try {\r\n // specifying which file properties we want to get from API\r\n // reason: api adds lots of other unnecessary properties - downloader doesn't need them and if a transfer has lots of files (some can be up to 100k)\r\n // it makes the response much smaller (up to 80% size reduction)\r\n const request: GetTransferRequest = { fileslimit, transferid, trackid, password, fprops: [`fileid`, `filesize`, `filename`, `downloadurl`], checkLogintoken };\r\n return await this.#apiClient.getTransfer(request);\r\n }\r\n catch (error) {\r\n const code = this.#apiErrorToDownloadErrorCode(error);\r\n const downloadError = new DownloadError(`Could not fetch transfer by id=${transferid ?? `---`} track=${trackid ?? `---`}`, { cause: error as Error }, code);\r\n throw downloadError;\r\n }\r\n }\r\n\r\n #apiErrorToDownloadErrorCode(error: unknown) {\r\n if (error instanceof ApiError) {\r\n switch (error.httpStatus) {\r\n case 401: return DownloadErrorCode.InvalidTransferPassword;\r\n case 404: return DownloadErrorCode.TransferNotFound;\r\n case 409: return DownloadErrorCode.TransferInvalidState;\r\n default: break;\r\n }\r\n }\r\n return undefined;\r\n }\r\n\r\n #throwIfRunning() {\r\n if (this.#filesDownloader)\r\n throw new DownloadError(`This instance of downloader is already in use. Please create a new one.`);\r\n }\r\n\r\n #throwIfNotRunning() {\r\n if (!this.#filesDownloader)\r\n throw new DownloadError(`This instance of downloader is not running yet. Please start the download first.`);\r\n }\r\n\r\n #generateDownloadId(): string {\r\n return `${this.#transferId}_${this.#cryptography.generateRandomString(10)}`;\r\n }\r\n\r\n #mapTransferFilesDtosToFileInfos(files: Array<TransferFileDto>) {\r\n return files.map(file => new DownloadFileInfo(\r\n file.fileid,\r\n file.downloadurl,\r\n file.filename,\r\n file.filesize,\r\n ));\r\n }\r\n\r\n #validateOptions() {\r\n if (!this.#options.downloadFolder && !this.#options.downloadFolderTemplate)\r\n throw new DownloadError(`downloadFolder OR downloadFolderTemplate is required`);\r\n\r\n if (this.#options.maxChunkSize !== null && this.#options.maxChunkSize! <= 0)\r\n throw new DownloadError(`maxChunkSize must be greater than 0. Provide \"null\" if you want to download without chunking.`);\r\n }\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunkDownloader.d.ts","sourceRoot":"","sources":["../../../src/downloader/chunkDownloader.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,qBAAqB,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,gBAAgB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,aAAa,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,uBAAuB,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,0BAA0B,MAAM,mDAAmD,CAAC;AAChG,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAIhE,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,4CAA4C,CAAC;AAEpG,MAAM,CAAC,OAAO,OAAO,eAAe;;gBAgB5B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,qBAAqB,EAC5B,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,eAAe,EACxB,uBAAuB,EAAE,uBAAuB,EAChD,0BAA0B,EAAE,0BAA0B,CAAC,MAAM,CAAC,EAC9D,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,cAAc,EACtB,cAAc,CAAC,EAAE,iCAAiC;IAehD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"chunkDownloader.d.ts","sourceRoot":"","sources":["../../../src/downloader/chunkDownloader.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,qBAAqB,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,gBAAgB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,aAAa,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,uBAAuB,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,0BAA0B,MAAM,mDAAmD,CAAC;AAChG,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAIhE,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,4CAA4C,CAAC;AAEpG,MAAM,CAAC,OAAO,OAAO,eAAe;;gBAgB5B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,qBAAqB,EAC5B,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,eAAe,EACxB,uBAAuB,EAAE,uBAAuB,EAChD,0BAA0B,EAAE,0BAA0B,CAAC,MAAM,CAAC,EAC9D,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,cAAc,EACtB,cAAc,CAAC,EAAE,iCAAiC;IAehD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAwPlC"}
|
|
@@ -125,10 +125,11 @@ export default class ChunkDownloader {
|
|
|
125
125
|
this.#downloadState.chunkDownloadCompleted(this.#file, this.#chunk);
|
|
126
126
|
else {
|
|
127
127
|
let downloadError = null;
|
|
128
|
+
const failureCode = this.#toFailureCode(unhandledError ?? lastError);
|
|
128
129
|
if (unhandledError)
|
|
129
|
-
downloadError = new DownloadError(`Unhandled chunk download error for file ${this.#file.url} chunk ${this.#chunk.chunkNumber}`, { cause: unhandledError });
|
|
130
|
+
downloadError = new DownloadError(`Unhandled chunk download error for file ${this.#file.url} chunk ${this.#chunk.chunkNumber}`, { cause: unhandledError }, failureCode);
|
|
130
131
|
else if (lastError)
|
|
131
|
-
downloadError = new DownloadError(`Chunk download failed for file ${this.#file.url} chunk ${this.#chunk.chunkNumber} attempt ${attempts}.`, { cause: lastError });
|
|
132
|
+
downloadError = new DownloadError(`Chunk download failed for file ${this.#file.url} chunk ${this.#chunk.chunkNumber} attempt ${attempts}.`, { cause: lastError }, failureCode);
|
|
132
133
|
if (downloadError !== null) {
|
|
133
134
|
this.#downloadState.chunkDownloadFailed(this.#file, this.#chunk, downloadError);
|
|
134
135
|
}
|
|
@@ -162,7 +163,7 @@ export default class ChunkDownloader {
|
|
|
162
163
|
return false;
|
|
163
164
|
if (apiReturnedNotOkResponseStatus)
|
|
164
165
|
return false;
|
|
165
|
-
if (error
|
|
166
|
+
if (this.#isDiskFullError(error))
|
|
166
167
|
return false;
|
|
167
168
|
if (error && error instanceof RequestError)
|
|
168
169
|
return this.#options.fileserverAttemptLimit <= 0 || networkErrors < this.#options.fileserverAttemptLimit;
|
|
@@ -211,5 +212,23 @@ export default class ChunkDownloader {
|
|
|
211
212
|
chunk: DownloadFileChunkProgress.FromChunkInfo(this.#chunk),
|
|
212
213
|
};
|
|
213
214
|
}
|
|
215
|
+
#toFailureCode(error) {
|
|
216
|
+
if (this.#isDiskFullError(error))
|
|
217
|
+
return DownloadErrorCode.DiskFull;
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
#isDiskFullError(error) {
|
|
221
|
+
if (!error)
|
|
222
|
+
return false;
|
|
223
|
+
let current = error;
|
|
224
|
+
for (let depth = 0; depth < 8 && current; depth++) {
|
|
225
|
+
if (current.code === `ENOSPC`)
|
|
226
|
+
return true;
|
|
227
|
+
if (typeof current.message === `string` && current.message.includes(`ENOSPC`))
|
|
228
|
+
return true;
|
|
229
|
+
current = current.cause;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
214
233
|
}
|
|
215
234
|
//# sourceMappingURL=chunkDownloader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunkDownloader.js","sourceRoot":"","sources":["../../../src/downloader/chunkDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-await-in-loop */\r\nimport { RequestError } from \"got\";\r\nimport sleep from \"abortable-sleep\";\r\n\r\nimport TransferStatus from \"../utils/types/transferStatus.js\";\r\nimport DownloadError from \"./dtos/downloadError.js\";\r\nimport TransferDownloadEvent from \"./dtos/downloadEvent.js\";\r\nimport DownloadErrorCode from \"./dtos/downloadErrorCode.js\";\r\nimport DownloadFileChunkProgress from \"./downloadStatus/downloadFileChunkProgress.js\";\r\nimport { isBaseResponse } from \"../utils/api/dtos/baseResponse.js\";\r\n\r\nimport type DownloadFileChunkInfo from \"./dtos/downloadFileChunkInfo.js\";\r\nimport type DownloadFileInfo from \"./dtos/downloadFileInfo.js\";\r\nimport type DownloadOptions from \"./downloadOptions.js\";\r\nimport type DownloadState from \"./downloadStatus/downloadState.js\";\r\nimport type FileServerClientFactory from \"../utils/api/fileServerClientFactory.js\";\r\nimport type ChunkStreamProviderFactory from \"../utils/fileSystem/chunkStreamProviderFactory.js\";\r\nimport type EventsEngine from \"../utils/events/eventsEngine.js\";\r\nimport type FileServerDownloadClient from \"./api/fileServerDownloadClient.js\";\r\nimport type ChunkProgressEvent from \"../utils/events/chunkProgressEvent.js\";\r\nimport type FileDownloadFailedEventArgs from \"./eventArgs/fileDownloadFailedEventArgs.js\";\r\nimport type DownloadLogger from \"./downloadLogger.js\";\r\nimport type { ChunkStreamProviderEncryptionData } from \"../utils/fileSystem/chunkStreamProvider.js\";\r\n\r\nexport default class ChunkDownloader {\r\n #logger: DownloadLogger;\r\n #transferId: string;\r\n #chunk: DownloadFileChunkInfo;\r\n #file: DownloadFileInfo;\r\n #downloadState: DownloadState;\r\n #options: DownloadOptions;\r\n #fileServerClientFactory: FileServerClientFactory;\r\n #chunkStreamProviderFactory: ChunkStreamProviderFactory<string>;\r\n #eventsEngine: EventsEngine;\r\n #chunkDownloadClient: FileServerDownloadClient;\r\n #encryptionData?: ChunkStreamProviderEncryptionData;\r\n #bytesTransferred = 0;\r\n #running = false;\r\n\r\n constructor(\r\n transferId: string,\r\n file: DownloadFileInfo,\r\n chunk: DownloadFileChunkInfo,\r\n downloadState: DownloadState,\r\n options: DownloadOptions,\r\n fileServerClientFactory: FileServerClientFactory,\r\n chunkStreamProviderFactory: ChunkStreamProviderFactory<string>,\r\n eventsEngine: EventsEngine,\r\n logger: DownloadLogger,\r\n encryptionData?: ChunkStreamProviderEncryptionData,\r\n ) {\r\n this.#transferId = transferId;\r\n this.#options = options;\r\n this.#file = file;\r\n this.#chunk = chunk;\r\n this.#downloadState = downloadState;\r\n this.#fileServerClientFactory = fileServerClientFactory;\r\n this.#chunkStreamProviderFactory = chunkStreamProviderFactory;\r\n this.#eventsEngine = eventsEngine;\r\n this.#chunkDownloadClient = this.#fileServerClientFactory.createFileServerTcpDownloadClient(this.#file.url, this.#chunk.start, this.#chunk.end, this.#transferId, this.#file.name, this.#options);\r\n this.#logger = logger;\r\n this.#encryptionData = encryptionData;\r\n }\r\n\r\n async download(): Promise<void> {\r\n this.#throwIfRunning();\r\n this.#throwIfMissingDownloadPath();\r\n\r\n this.#running = true;\r\n\r\n let writtenCorrectly = false;\r\n let attempts = 0;\r\n let writeErrors = 0;\r\n let networkErrors = 0;\r\n let apiReturnedNotOkResponseStatus: boolean = false;\r\n let lastError: Error | null = null;\r\n let unhandledError: Error | null = null;\r\n\r\n try {\r\n this.#downloadState.chunkDownloadStarted(this.#file, this.#chunk);\r\n\r\n this.#addEventListeners();\r\n\r\n const fileserverMd5Promise: Promise<string> | null = this.#options.checkChunksMd5\r\n ? this.#chunkDownloadClient.getMd5Checksum()\r\n : null;\r\n\r\n while (writtenCorrectly === false && this.#canRetry(lastError as Error, writeErrors, networkErrors, apiReturnedNotOkResponseStatus)) {\r\n attempts += 1;\r\n\r\n this.#bytesTransferred = 0;\r\n\r\n const chunkWriter = this.#chunkStreamProviderFactory.provide(this.#file.temporaryDownloadPath!, this.#chunk.start ?? 0, this.#chunk.chunkSize, this.#encryptionData);\r\n\r\n try {\r\n await this.#chunkDownloadClient.download(chunkWriter, this.#encryptionData);\r\n\r\n if (this.#bytesTransferred !== this.#chunk.chunkSize) {\r\n if (this.#bytesTransferred > this.#chunk.chunkSize)\r\n this.#downloadState.possibleSupersizedFile(this.#file);\r\n\r\n throw new DownloadError(`Download request returned incorrect amount of data. Expected=${this.#chunk.chunkSize} Received=${this.#bytesTransferred}`, undefined, DownloadErrorCode.FileserverResponseWrongSize);\r\n }\r\n\r\n networkErrors = 0;\r\n this.#downloadState.networkOk();\r\n\r\n if (this.#downloadState.status !== TransferStatus.Aborting) {\r\n if (fileserverMd5Promise !== null) {\r\n const remoteMd5 = await fileserverMd5Promise;\r\n const localMd5 = await chunkWriter.getMd5Checksum(`hex`);\r\n if (remoteMd5.toLowerCase() === localMd5.toLowerCase())\r\n writtenCorrectly = true;\r\n else {\r\n writeErrors += 1;\r\n lastError = new DownloadError(`MD5 checksum incorrect: fileserver=${remoteMd5} local=${localMd5}`);\r\n }\r\n }\r\n else\r\n writtenCorrectly = true;\r\n }\r\n }\r\n catch (error) {\r\n // note: getting an exception here may be caused by abort or stop operations\r\n // both operations immediately interrupt HTTP download request by calling chunkDownloadClient.release()\r\n if (this.#downloadState.pauseImmediately || this.#downloadState.status === TransferStatus.Aborting)\r\n lastError = null;\r\n else {\r\n lastError = error as Error;\r\n\r\n if (lastError instanceof RequestError) {\r\n if (this.#isApiNotOkResponseStatusResponse(lastError))\r\n apiReturnedNotOkResponseStatus = true;\r\n else {\r\n networkErrors += 1;\r\n this.#downloadState.networkFailure();\r\n }\r\n }\r\n else\r\n writeErrors += 1;\r\n\r\n this.#logger.logError(lastError, `[CHUNK_DOWNLOADER_ERROR] Error in retry loop when downloading chunk attempt=${attempts} writeerrors=${writeErrors} networkerrors=${networkErrors}`, this.#errorInfoDump());\r\n }\r\n }\r\n finally {\r\n await chunkWriter?.release();\r\n this.#chunkDownloadClient?.release();\r\n\r\n let sleepTime = 0;\r\n if (this.#downloadState.isThereNetworkProblem)\r\n sleepTime = this.#options.fileserverOfflineCheckIntervalMillis;\r\n else if (networkErrors > 0) {\r\n const pow = Math.min(networkErrors, this.#options.fileserverOfflineAfterConsecutiveAttempts);\r\n sleepTime = this.#options.fileserverOfflineCheckIntervalMillis * (2 ** (pow - this.#options.fileserverOfflineAfterConsecutiveAttempts));\r\n }\r\n\r\n if (sleepTime > 0)\r\n await sleep(sleepTime);\r\n }\r\n }\r\n }\r\n catch (error) {\r\n unhandledError = error as Error;\r\n this.#logger.logError(unhandledError, `[CHUNK_DOWNLOADER_ERROR] Unhandled error when downloading chunk`, this.#errorInfoDump());\r\n }\r\n finally {\r\n this.#removeEventListeners();\r\n }\r\n\r\n if (writtenCorrectly)\r\n this.#downloadState.chunkDownloadCompleted(this.#file, this.#chunk);\r\n else {\r\n let downloadError: DownloadError | null = null;\r\n\r\n if (unhandledError)\r\n downloadError = new DownloadError(`Unhandled chunk download error for file ${this.#file.url} chunk ${this.#chunk.chunkNumber}`, { cause: unhandledError as Error });\r\n else if (lastError)\r\n downloadError = new DownloadError(`Chunk download failed for file ${this.#file.url} chunk ${this.#chunk.chunkNumber} attempt ${attempts}.`, { cause: lastError as Error });\r\n\r\n if (downloadError !== null) {\r\n this.#downloadState.chunkDownloadFailed(\r\n this.#file,\r\n this.#chunk,\r\n downloadError,\r\n );\r\n }\r\n }\r\n\r\n this.#running = false;\r\n }\r\n\r\n #throwIfRunning() {\r\n if (this.#running)\r\n throw new DownloadError(`This instance of chunk downloader is already in use. Please create a new one.`);\r\n }\r\n\r\n #throwIfMissingDownloadPath() {\r\n if (!this.#file.temporaryDownloadPath)\r\n throw new DownloadError(`Download file ${this.#file.externalId}/${this.#file.name}/${this.#file.downloadPath} is missing the required \"temporaryDownloadPath\"`);\r\n }\r\n\r\n #addEventListeners() {\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.Aborting, this.#onAbortRequested);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.Pausing, this.#onPauseRequested);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#chunkDownloadClient.addEventListener(`progress`, this.#onProgress);\r\n }\r\n\r\n #removeEventListeners() {\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.Aborting, this.#onAbortRequested);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.Pausing, this.#onPauseRequested);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#chunkDownloadClient.removeEventListener(`progress`, this.#onProgress);\r\n }\r\n\r\n #canRetry(error: Error | null, writeErrors: number, networkErrors: number, apiReturnedNotOkResponseStatus: boolean) {\r\n if (this.#downloadState.status === TransferStatus.Aborting || this.#downloadState.status === TransferStatus.Pausing)\r\n return false;\r\n\r\n if (this.#file.isFailed)\r\n return false;\r\n\r\n if (apiReturnedNotOkResponseStatus)\r\n return false;\r\n\r\n if (error?.message.includes(`ENOSPC`))\r\n return false;\r\n\r\n if (error && error instanceof RequestError)\r\n return this.#options.fileserverAttemptLimit <= 0 || networkErrors < this.#options.fileserverAttemptLimit!;\r\n\r\n return this.#options.maxWriteAttempts! < 0 || writeErrors < this.#options.maxWriteAttempts!;\r\n }\r\n\r\n #isApiNotOkResponseStatusResponse(error: RequestError): boolean {\r\n try {\r\n if (typeof error.response?.body !== `string`)\r\n return false;\r\n\r\n const response = JSON.parse(error.response?.body);\r\n\r\n if (!isBaseResponse(response))\r\n return false;\r\n\r\n return response.responsestatus !== `OK`;\r\n }\r\n catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * This method aborts pending download - it can be achieved only by destroying the download client\r\n * (which in turn destroys the underlying download HTTP request). This operation\r\n * will throw an expected exception inside download method (in main while loop). See {@link download} method.\r\n */\r\n #interruptDownload = () => {\r\n this.#chunkDownloadClient?.release();\r\n };\r\n\r\n #onAbortRequested = () => {\r\n this.#interruptDownload();\r\n };\r\n\r\n #onPauseRequested = () => {\r\n if (this.#downloadState.pauseImmediately)\r\n this.#interruptDownload();\r\n };\r\n\r\n #onFileFailed = (args: FileDownloadFailedEventArgs) => {\r\n if (this.#file.externalId === args.file.id)\r\n this.#chunkDownloadClient?.release();\r\n };\r\n\r\n #onProgress = (args: ChunkProgressEvent) => {\r\n this.#bytesTransferred = args.transferredBytesTotal;\r\n this.#downloadState.chunkDownloadProgressed(this.#file, this.#chunk, args);\r\n };\r\n\r\n #errorInfoDump() {\r\n return {\r\n fileid: this.#file.externalId,\r\n filename: this.#file.name,\r\n chunk: DownloadFileChunkProgress.FromChunkInfo(this.#chunk),\r\n };\r\n }\r\n}"]}
|
|
1
|
+
{"version":3,"file":"chunkDownloader.js","sourceRoot":"","sources":["../../../src/downloader/chunkDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-await-in-loop */\r\nimport { RequestError } from \"got\";\r\nimport sleep from \"abortable-sleep\";\r\n\r\nimport TransferStatus from \"../utils/types/transferStatus.js\";\r\nimport DownloadError from \"./dtos/downloadError.js\";\r\nimport TransferDownloadEvent from \"./dtos/downloadEvent.js\";\r\nimport DownloadErrorCode from \"./dtos/downloadErrorCode.js\";\r\nimport DownloadFileChunkProgress from \"./downloadStatus/downloadFileChunkProgress.js\";\r\nimport { isBaseResponse } from \"../utils/api/dtos/baseResponse.js\";\r\n\r\nimport type DownloadFileChunkInfo from \"./dtos/downloadFileChunkInfo.js\";\r\nimport type DownloadFileInfo from \"./dtos/downloadFileInfo.js\";\r\nimport type DownloadOptions from \"./downloadOptions.js\";\r\nimport type DownloadState from \"./downloadStatus/downloadState.js\";\r\nimport type FileServerClientFactory from \"../utils/api/fileServerClientFactory.js\";\r\nimport type ChunkStreamProviderFactory from \"../utils/fileSystem/chunkStreamProviderFactory.js\";\r\nimport type EventsEngine from \"../utils/events/eventsEngine.js\";\r\nimport type FileServerDownloadClient from \"./api/fileServerDownloadClient.js\";\r\nimport type ChunkProgressEvent from \"../utils/events/chunkProgressEvent.js\";\r\nimport type FileDownloadFailedEventArgs from \"./eventArgs/fileDownloadFailedEventArgs.js\";\r\nimport type DownloadLogger from \"./downloadLogger.js\";\r\nimport type { ChunkStreamProviderEncryptionData } from \"../utils/fileSystem/chunkStreamProvider.js\";\r\n\r\nexport default class ChunkDownloader {\r\n #logger: DownloadLogger;\r\n #transferId: string;\r\n #chunk: DownloadFileChunkInfo;\r\n #file: DownloadFileInfo;\r\n #downloadState: DownloadState;\r\n #options: DownloadOptions;\r\n #fileServerClientFactory: FileServerClientFactory;\r\n #chunkStreamProviderFactory: ChunkStreamProviderFactory<string>;\r\n #eventsEngine: EventsEngine;\r\n #chunkDownloadClient: FileServerDownloadClient;\r\n #encryptionData?: ChunkStreamProviderEncryptionData;\r\n #bytesTransferred = 0;\r\n #running = false;\r\n\r\n constructor(\r\n transferId: string,\r\n file: DownloadFileInfo,\r\n chunk: DownloadFileChunkInfo,\r\n downloadState: DownloadState,\r\n options: DownloadOptions,\r\n fileServerClientFactory: FileServerClientFactory,\r\n chunkStreamProviderFactory: ChunkStreamProviderFactory<string>,\r\n eventsEngine: EventsEngine,\r\n logger: DownloadLogger,\r\n encryptionData?: ChunkStreamProviderEncryptionData,\r\n ) {\r\n this.#transferId = transferId;\r\n this.#options = options;\r\n this.#file = file;\r\n this.#chunk = chunk;\r\n this.#downloadState = downloadState;\r\n this.#fileServerClientFactory = fileServerClientFactory;\r\n this.#chunkStreamProviderFactory = chunkStreamProviderFactory;\r\n this.#eventsEngine = eventsEngine;\r\n this.#chunkDownloadClient = this.#fileServerClientFactory.createFileServerTcpDownloadClient(this.#file.url, this.#chunk.start, this.#chunk.end, this.#transferId, this.#file.name, this.#options);\r\n this.#logger = logger;\r\n this.#encryptionData = encryptionData;\r\n }\r\n\r\n async download(): Promise<void> {\r\n this.#throwIfRunning();\r\n this.#throwIfMissingDownloadPath();\r\n\r\n this.#running = true;\r\n\r\n let writtenCorrectly = false;\r\n let attempts = 0;\r\n let writeErrors = 0;\r\n let networkErrors = 0;\r\n let apiReturnedNotOkResponseStatus: boolean = false;\r\n let lastError: Error | null = null;\r\n let unhandledError: Error | null = null;\r\n\r\n try {\r\n this.#downloadState.chunkDownloadStarted(this.#file, this.#chunk);\r\n\r\n this.#addEventListeners();\r\n\r\n const fileserverMd5Promise: Promise<string> | null = this.#options.checkChunksMd5\r\n ? this.#chunkDownloadClient.getMd5Checksum()\r\n : null;\r\n\r\n while (writtenCorrectly === false && this.#canRetry(lastError as Error, writeErrors, networkErrors, apiReturnedNotOkResponseStatus)) {\r\n attempts += 1;\r\n\r\n this.#bytesTransferred = 0;\r\n\r\n const chunkWriter = this.#chunkStreamProviderFactory.provide(this.#file.temporaryDownloadPath!, this.#chunk.start ?? 0, this.#chunk.chunkSize, this.#encryptionData);\r\n\r\n try {\r\n await this.#chunkDownloadClient.download(chunkWriter, this.#encryptionData);\r\n\r\n if (this.#bytesTransferred !== this.#chunk.chunkSize) {\r\n if (this.#bytesTransferred > this.#chunk.chunkSize)\r\n this.#downloadState.possibleSupersizedFile(this.#file);\r\n\r\n throw new DownloadError(`Download request returned incorrect amount of data. Expected=${this.#chunk.chunkSize} Received=${this.#bytesTransferred}`, undefined, DownloadErrorCode.FileserverResponseWrongSize);\r\n }\r\n\r\n networkErrors = 0;\r\n this.#downloadState.networkOk();\r\n\r\n if (this.#downloadState.status !== TransferStatus.Aborting) {\r\n if (fileserverMd5Promise !== null) {\r\n const remoteMd5 = await fileserverMd5Promise;\r\n const localMd5 = await chunkWriter.getMd5Checksum(`hex`);\r\n if (remoteMd5.toLowerCase() === localMd5.toLowerCase())\r\n writtenCorrectly = true;\r\n else {\r\n writeErrors += 1;\r\n lastError = new DownloadError(`MD5 checksum incorrect: fileserver=${remoteMd5} local=${localMd5}`);\r\n }\r\n }\r\n else\r\n writtenCorrectly = true;\r\n }\r\n }\r\n catch (error) {\r\n // note: getting an exception here may be caused by abort or stop operations\r\n // both operations immediately interrupt HTTP download request by calling chunkDownloadClient.release()\r\n if (this.#downloadState.pauseImmediately || this.#downloadState.status === TransferStatus.Aborting)\r\n lastError = null;\r\n else {\r\n lastError = error as Error;\r\n\r\n if (lastError instanceof RequestError) {\r\n if (this.#isApiNotOkResponseStatusResponse(lastError))\r\n apiReturnedNotOkResponseStatus = true;\r\n else {\r\n networkErrors += 1;\r\n this.#downloadState.networkFailure();\r\n }\r\n }\r\n else\r\n writeErrors += 1;\r\n\r\n this.#logger.logError(lastError, `[CHUNK_DOWNLOADER_ERROR] Error in retry loop when downloading chunk attempt=${attempts} writeerrors=${writeErrors} networkerrors=${networkErrors}`, this.#errorInfoDump());\r\n }\r\n }\r\n finally {\r\n await chunkWriter?.release();\r\n this.#chunkDownloadClient?.release();\r\n\r\n let sleepTime = 0;\r\n if (this.#downloadState.isThereNetworkProblem)\r\n sleepTime = this.#options.fileserverOfflineCheckIntervalMillis;\r\n else if (networkErrors > 0) {\r\n const pow = Math.min(networkErrors, this.#options.fileserverOfflineAfterConsecutiveAttempts);\r\n sleepTime = this.#options.fileserverOfflineCheckIntervalMillis * (2 ** (pow - this.#options.fileserverOfflineAfterConsecutiveAttempts));\r\n }\r\n\r\n if (sleepTime > 0)\r\n await sleep(sleepTime);\r\n }\r\n }\r\n }\r\n catch (error) {\r\n unhandledError = error as Error;\r\n this.#logger.logError(unhandledError, `[CHUNK_DOWNLOADER_ERROR] Unhandled error when downloading chunk`, this.#errorInfoDump());\r\n }\r\n finally {\r\n this.#removeEventListeners();\r\n }\r\n\r\n if (writtenCorrectly)\r\n this.#downloadState.chunkDownloadCompleted(this.#file, this.#chunk);\r\n else {\r\n let downloadError: DownloadError | null = null;\r\n const failureCode = this.#toFailureCode(unhandledError ?? lastError);\r\n\r\n if (unhandledError)\r\n downloadError = new DownloadError(`Unhandled chunk download error for file ${this.#file.url} chunk ${this.#chunk.chunkNumber}`, { cause: unhandledError as Error }, failureCode);\r\n else if (lastError)\r\n downloadError = new DownloadError(`Chunk download failed for file ${this.#file.url} chunk ${this.#chunk.chunkNumber} attempt ${attempts}.`, { cause: lastError as Error }, failureCode);\r\n\r\n if (downloadError !== null) {\r\n this.#downloadState.chunkDownloadFailed(\r\n this.#file,\r\n this.#chunk,\r\n downloadError,\r\n );\r\n }\r\n }\r\n\r\n this.#running = false;\r\n }\r\n\r\n #throwIfRunning() {\r\n if (this.#running)\r\n throw new DownloadError(`This instance of chunk downloader is already in use. Please create a new one.`);\r\n }\r\n\r\n #throwIfMissingDownloadPath() {\r\n if (!this.#file.temporaryDownloadPath)\r\n throw new DownloadError(`Download file ${this.#file.externalId}/${this.#file.name}/${this.#file.downloadPath} is missing the required \"temporaryDownloadPath\"`);\r\n }\r\n\r\n #addEventListeners() {\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.Aborting, this.#onAbortRequested);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.Pausing, this.#onPauseRequested);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#chunkDownloadClient.addEventListener(`progress`, this.#onProgress);\r\n }\r\n\r\n #removeEventListeners() {\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.Aborting, this.#onAbortRequested);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.Pausing, this.#onPauseRequested);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#chunkDownloadClient.removeEventListener(`progress`, this.#onProgress);\r\n }\r\n\r\n #canRetry(error: Error | null, writeErrors: number, networkErrors: number, apiReturnedNotOkResponseStatus: boolean) {\r\n if (this.#downloadState.status === TransferStatus.Aborting || this.#downloadState.status === TransferStatus.Pausing)\r\n return false;\r\n\r\n if (this.#file.isFailed)\r\n return false;\r\n\r\n if (apiReturnedNotOkResponseStatus)\r\n return false;\r\n\r\n if (this.#isDiskFullError(error))\r\n return false;\r\n\r\n if (error && error instanceof RequestError)\r\n return this.#options.fileserverAttemptLimit <= 0 || networkErrors < this.#options.fileserverAttemptLimit!;\r\n\r\n return this.#options.maxWriteAttempts! < 0 || writeErrors < this.#options.maxWriteAttempts!;\r\n }\r\n\r\n #isApiNotOkResponseStatusResponse(error: RequestError): boolean {\r\n try {\r\n if (typeof error.response?.body !== `string`)\r\n return false;\r\n\r\n const response = JSON.parse(error.response?.body);\r\n\r\n if (!isBaseResponse(response))\r\n return false;\r\n\r\n return response.responsestatus !== `OK`;\r\n }\r\n catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * This method aborts pending download - it can be achieved only by destroying the download client\r\n * (which in turn destroys the underlying download HTTP request). This operation\r\n * will throw an expected exception inside download method (in main while loop). See {@link download} method.\r\n */\r\n #interruptDownload = () => {\r\n this.#chunkDownloadClient?.release();\r\n };\r\n\r\n #onAbortRequested = () => {\r\n this.#interruptDownload();\r\n };\r\n\r\n #onPauseRequested = () => {\r\n if (this.#downloadState.pauseImmediately)\r\n this.#interruptDownload();\r\n };\r\n\r\n #onFileFailed = (args: FileDownloadFailedEventArgs) => {\r\n if (this.#file.externalId === args.file.id)\r\n this.#chunkDownloadClient?.release();\r\n };\r\n\r\n #onProgress = (args: ChunkProgressEvent) => {\r\n this.#bytesTransferred = args.transferredBytesTotal;\r\n this.#downloadState.chunkDownloadProgressed(this.#file, this.#chunk, args);\r\n };\r\n\r\n #errorInfoDump() {\r\n return {\r\n fileid: this.#file.externalId,\r\n filename: this.#file.name,\r\n chunk: DownloadFileChunkProgress.FromChunkInfo(this.#chunk),\r\n };\r\n }\r\n\r\n #toFailureCode(error: Error | null): DownloadErrorCode | undefined {\r\n if (this.#isDiskFullError(error))\r\n return DownloadErrorCode.DiskFull;\r\n\r\n return undefined;\r\n }\r\n\r\n #isDiskFullError(error: unknown): boolean {\r\n if (!error)\r\n return false;\r\n\r\n let current = error as { message?: string, code?: string, cause?: unknown } | undefined;\r\n for (let depth = 0; depth < 8 && current; depth++) {\r\n if (current.code === `ENOSPC`)\r\n return true;\r\n\r\n if (typeof current.message === `string` && current.message.includes(`ENOSPC`))\r\n return true;\r\n\r\n current = current.cause as { message?: string, code?: string, cause?: unknown } | undefined;\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n"]}
|
|
@@ -141,6 +141,29 @@ declare class DownloadOptions {
|
|
|
141
141
|
* @default false
|
|
142
142
|
*/
|
|
143
143
|
skipRegistration: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Transfer size threshold (in bytes) for always preallocating destination files with truncate.
|
|
146
|
+
* If total transfer size is <= this value, downloader always preallocates.
|
|
147
|
+
* @default 5 * 1024 * 1024 * 1024 (5GB)
|
|
148
|
+
*/
|
|
149
|
+
preallocateAlwaysUntilTransferSizeInBytes: number;
|
|
150
|
+
/**
|
|
151
|
+
* Probe file size (in bytes) used to measure truncate performance for large transfers.
|
|
152
|
+
* If the probe finishes quickly enough, downloader preallocates full file sizes.
|
|
153
|
+
* @default 1024 * 1024 * 1024 (1GB)
|
|
154
|
+
*/
|
|
155
|
+
preallocateProbeFileSizeInBytes: number;
|
|
156
|
+
/**
|
|
157
|
+
* Maximum probe truncate duration considered "fast" (milliseconds).
|
|
158
|
+
* If probe takes longer, downloader skips full preallocation and only checks free space.
|
|
159
|
+
* @default 3000
|
|
160
|
+
*/
|
|
161
|
+
preallocateProbeMaxDurationInMs: number;
|
|
162
|
+
/**
|
|
163
|
+
* Extra free-space margin (in bytes) required when downloader skips full preallocation.
|
|
164
|
+
* @default 512 * 1024 * 1024 (512MB)
|
|
165
|
+
*/
|
|
166
|
+
preallocateFreeSpaceSafetyMarginInBytes: number;
|
|
144
167
|
}
|
|
145
168
|
export default DownloadOptions;
|
|
146
169
|
//# sourceMappingURL=downloadOptions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloadOptions.d.ts","sourceRoot":"","sources":["../../../src/downloader/downloadOptions.ts"],"names":[],"mappings":"AAAA,cAAM,eAAe;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC;;;;;;;;;;;;OAYG;IACH,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC7C;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IACrC;;;OAGG;IACH,0BAA0B,SAAS;IACnC;;;;OAIG;IACH,aAAa,SAAK;IAClB;;;;;OAKG;IACH,cAAc,SAAK;IACnB;;;OAGG;IACH,gBAAgB,SAAM;IACtB;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAoB;IAC/C;;;OAGG;IACH,cAAc,UAAQ;IACtB;;;OAGG;IACH,YAAY,UAAS;IACrB;;;OAGG;IACH,4BAA4B,SAAS;IACrC;;;;;OAKG;IACH,sBAAsB,SAAK;IAC3B;;;;OAIG;IACH,yCAAyC,SAAM;IAC/C;;;;;OAKG;IACH,oCAAoC,SAAQ;IAC5C;;;OAGG;IACH,kBAAkB,SAAO;IACzB;;;OAGG;IACH,yBAAyB,UAAQ;IACjC;;;OAGG;IACH,8BAA8B,UAAQ;IACtC;;;;;OAKG;IACH,uCAAuC,UAAS;IAChD;;;;;;OAMG;IACH,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjD;;;OAGG;IACH,8BAA8B,SAAQ;IACtC;;;OAGG;IACH,oCAAoC,SAAQ;IAC5C;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC;;;OAGG;IACH,gBAAgB,UAAS;
|
|
1
|
+
{"version":3,"file":"downloadOptions.d.ts","sourceRoot":"","sources":["../../../src/downloader/downloadOptions.ts"],"names":[],"mappings":"AAAA,cAAM,eAAe;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC;;;;;;;;;;;;OAYG;IACH,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC7C;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IACrC;;;OAGG;IACH,0BAA0B,SAAS;IACnC;;;;OAIG;IACH,aAAa,SAAK;IAClB;;;;;OAKG;IACH,cAAc,SAAK;IACnB;;;OAGG;IACH,gBAAgB,SAAM;IACtB;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAoB;IAC/C;;;OAGG;IACH,cAAc,UAAQ;IACtB;;;OAGG;IACH,YAAY,UAAS;IACrB;;;OAGG;IACH,4BAA4B,SAAS;IACrC;;;;;OAKG;IACH,sBAAsB,SAAK;IAC3B;;;;OAIG;IACH,yCAAyC,SAAM;IAC/C;;;;;OAKG;IACH,oCAAoC,SAAQ;IAC5C;;;OAGG;IACH,kBAAkB,SAAO;IACzB;;;OAGG;IACH,yBAAyB,UAAQ;IACjC;;;OAGG;IACH,8BAA8B,UAAQ;IACtC;;;;;OAKG;IACH,uCAAuC,UAAS;IAChD;;;;;;OAMG;IACH,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjD;;;OAGG;IACH,8BAA8B,SAAQ;IACtC;;;OAGG;IACH,oCAAoC,SAAQ;IAC5C;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC;;;OAGG;IACH,gBAAgB,UAAS;IACzB;;;;OAIG;IACH,yCAAyC,SAA0B;IACnE;;;;OAIG;IACH,+BAA+B,SAAsB;IACrD;;;;OAIG;IACH,+BAA+B,SAAQ;IACvC;;;OAGG;IACH,uCAAuC,SAAqB;CAC/D;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -141,6 +141,29 @@ class DownloadOptions {
|
|
|
141
141
|
* @default false
|
|
142
142
|
*/
|
|
143
143
|
skipRegistration = false;
|
|
144
|
+
/**
|
|
145
|
+
* Transfer size threshold (in bytes) for always preallocating destination files with truncate.
|
|
146
|
+
* If total transfer size is <= this value, downloader always preallocates.
|
|
147
|
+
* @default 5 * 1024 * 1024 * 1024 (5GB)
|
|
148
|
+
*/
|
|
149
|
+
preallocateAlwaysUntilTransferSizeInBytes = 5 * 1024 * 1024 * 1024;
|
|
150
|
+
/**
|
|
151
|
+
* Probe file size (in bytes) used to measure truncate performance for large transfers.
|
|
152
|
+
* If the probe finishes quickly enough, downloader preallocates full file sizes.
|
|
153
|
+
* @default 1024 * 1024 * 1024 (1GB)
|
|
154
|
+
*/
|
|
155
|
+
preallocateProbeFileSizeInBytes = 1024 * 1024 * 1024;
|
|
156
|
+
/**
|
|
157
|
+
* Maximum probe truncate duration considered "fast" (milliseconds).
|
|
158
|
+
* If probe takes longer, downloader skips full preallocation and only checks free space.
|
|
159
|
+
* @default 3000
|
|
160
|
+
*/
|
|
161
|
+
preallocateProbeMaxDurationInMs = 3000;
|
|
162
|
+
/**
|
|
163
|
+
* Extra free-space margin (in bytes) required when downloader skips full preallocation.
|
|
164
|
+
* @default 512 * 1024 * 1024 (512MB)
|
|
165
|
+
*/
|
|
166
|
+
preallocateFreeSpaceSafetyMarginInBytes = 512 * 1024 * 1024;
|
|
144
167
|
}
|
|
145
168
|
export default DownloadOptions;
|
|
146
169
|
//# sourceMappingURL=downloadOptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloadOptions.js","sourceRoot":"","sources":["../../../src/downloader/downloadOptions.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe;IACjB;;;OAGG;IACH,UAAU,GAAkB,IAAI,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,sBAAsB,GAAkB,IAAI,CAAC;IAC7C;;;;OAIG;IACH,cAAc,GAAkB,IAAI,CAAC;IACrC;;;OAGG;IACH,0BAA0B,GAAG,KAAK,CAAC;IACnC;;;;OAIG;IACH,aAAa,GAAG,CAAC,CAAC;IAClB;;;;;OAKG;IACH,cAAc,GAAG,CAAC,CAAC;IACnB;;;OAGG;IACH,gBAAgB,GAAG,EAAE,CAAC;IACtB;;;OAGG;IACH,SAAS,GAAkB,IAAI,CAAC;IAChC;;;OAGG;IACH,YAAY,GAAkB,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/C;;;OAGG;IACH,cAAc,GAAG,IAAI,CAAC;IACtB;;;OAGG;IACH,YAAY,GAAG,KAAK,CAAC;IACrB;;;OAGG;IACH,4BAA4B,GAAG,KAAK,CAAC;IACrC;;;;;OAKG;IACH,sBAAsB,GAAG,CAAC,CAAC;IAC3B;;;;OAIG;IACH,yCAAyC,GAAG,EAAE,CAAC;IAC/C;;;;;OAKG;IACH,oCAAoC,GAAG,IAAI,CAAC;IAC5C;;;OAGG;IACH,kBAAkB,GAAG,GAAG,CAAC;IACzB;;;OAGG;IACH,yBAAyB,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,8BAA8B,GAAG,IAAI,CAAC;IACtC;;;;;OAKG;IACH,uCAAuC,GAAG,KAAK,CAAC;IAChD;;;;;;OAMG;IACH,0BAA0B,GAAkB,IAAI,CAAC;IACjD;;;OAGG;IACH,8BAA8B,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,oCAAoC,GAAG,IAAI,CAAC;IAC5C;;OAEG;IACH,UAAU,CAA8B;IACxC;;;OAGG;IACH,gBAAgB,GAAG,KAAK,CAAC;CAC5B;AAED,eAAe,eAAe,CAAC","sourcesContent":["class DownloadOptions {\r\n /**\r\n * logintoken of the current user - used to fetch transfer (optionally also pwd-protected), company setup, register download\r\n * @default null\r\n */\r\n logintoken: string | null = null;\r\n /**\r\n * Path template where files should be downloaded. Provide this OR {@link downloadFolder}. Supports placeholders:\r\n * <id> - transferId\r\n * <subject>\r\n * <description>\r\n * <domain> - domain based on \"from\" email (e.g. user@example.com --> example.com)\r\n * <emailfrom>\r\n * <datesent> - will be formatted as \"YYYY-MM-DD-hh-mm\"\r\n * <datesent:format> - will be formatted according to \"format\"; supported placeholders in \"format\": YYYY, MM, DD, hh, mm\r\n * <emailto> - all recipients emails comma-separated\r\n * <{custom_field_label}> - specified custom field named by label will replaced with that custom field's value\r\n * @default null\r\n */\r\n downloadFolderTemplate: string | null = null;\r\n /**\r\n * The path where files should be downloaded. No placeholders. Provide this OR {@link downloadFolderTemplate}.\r\n * When {@link downloadFolderTemplate} is specified this value will be overwritten with the resolved (final) path.\r\n * @default null\r\n */\r\n downloadFolder: string | null = null;\r\n /**\r\n * HTTP request timeout for web api (not Fileserver) in milliseconds.\r\n * @default 60000\r\n */\r\n apiRequestTotalTimeoutInMs = 60000;\r\n /**\r\n * How many times to attempt HTTP requests towards web API (not Fileserver) e.g. when getting a transfer from the API.\r\n * @see https://github.com/sindresorhus/got#documentation\r\n * @default 3\r\n */\r\n apiRetryLimit = 3;\r\n /**\r\n * Maximum number (N) of chunks that can be downloaded in parallel. As soon as any single chunk is complete then next available chunk is started (to keep parallel chunks always at N).\r\n * - If there is one huge file with X chunks (X>N) - first N chunks will be picked up.\r\n * - If there are 2 files, each with 3 chunks and N=4 - both files will be started (all 3 chunks of first file and only 1st chunk of the 2nd file).\r\n * @default 8\r\n */\r\n parallelChunks = 8;\r\n /**\r\n * How many times to try write downloaded bytes of a chunk and optionally verify its MD5. Set to -1 to keep trying forever.\r\n * @default 20\r\n */\r\n maxWriteAttempts = 20;\r\n /**\r\n * The user-agent header that will be used for all fileserver GetFile requests.\r\n * @default null\r\n */\r\n userAgent: string | null = null;\r\n /**\r\n * Maximum size (S) of a chunk in bytes. When set to a number - downloader will split files into chunks of this size (size of last chunk) <= S. Set to null to download every file in one HTTPS request.\r\n * @default 50 * 1024 * 1024 (50MB) - this matches the default pre-calculated rangehashes on Fileservers.\r\n */\r\n maxChunkSize: number | null = 50 * 1024 * 1024;\r\n /**\r\n * When set to **true** downloader will verify MD5 after every chunk. This doesn't work for end to end encrypted transfers.\r\n * @efault true\r\n */\r\n checkChunksMd5 = true;\r\n /**\r\n * When set to **true** downloader will verify MD5 for every completed file (entire file). To use this feature provided the MD5 value in DownloadFileInfo passed to the downloader. This doesn't work for end to end encrypted transfers.\r\n * @note NOT SUPPORTED (YET)\r\n */\r\n checkFileMd5 = false;\r\n /**\r\n * How long should it take for HTTP library to throw error when no download data is pumped to response (milliseconds).\r\n * @default 30000\r\n */\r\n fileserverDownloadIdleTimeMs = 30000;\r\n /**\r\n * How many times to attempt a single HTTP download request against Fileserver's GetFile. Set this options to 0 to keep trying forever.\r\n * When set to 0 (retring forever) the downloader will report \"network problem\" after 10 failed attempts (via event and a status field).\r\n * When connectivity is back it will be reported with an event as well.\r\n * @default 0\r\n */\r\n fileserverAttemptLimit = 0;\r\n /**\r\n * Downloader will assume there is a \"network problem\" after this many consecutive network errors without any successful progress.\r\n * Set to -1 to disable this feature.\r\n * @default 10;\r\n */\r\n fileserverOfflineAfterConsecutiveAttempts = 10;\r\n /**\r\n * Once downloader sees a \"network problem\" it will retry every pending chunk with this interval (in milliseconds).\r\n * This interval will be also used to calculate exponential backoff between first network error and getting into \"network problem\" state\r\n * and also is the maximum interval of the exponential backoff.\r\n * @default 5000;\r\n */\r\n fileserverOfflineCheckIntervalMillis = 5000;\r\n /**\r\n * Number of milliseconds between progress events emitted by the downloader (throttling - they won't be emitted more often that this amount of time).\r\n * @default 100\r\n */\r\n progressTickMillis = 100;\r\n /**\r\n * If set to **true** ==> the downloader will delete those files which were being downloaded, but failed.\r\n * @default true\r\n */\r\n deleteFailedFilesFromDisk = true;\r\n /**\r\n * When set to **true** ==> after user requests download abort - the downloader will delete all files which it tried to download, no matter their size and status.\r\n * @default true\r\n */\r\n deleteAbortedDownloadsFromDisk = true;\r\n /**\r\n * When set to **true** ==> after download completes or is aborted - the downloader will delete the main download folder if it is empty (no files have been downloaded and there are no other files left over).\r\n * Default is false as usually the download folder is set to be the OS-special (e.g. on windows --> c:\\users\\jane\\Downloads). We don't want to try removing that.\r\n * Set this to **true** only if specifying custom download folders.\r\n * @default false\r\n */\r\n deleteEmptyDownloadFoldersAfterDownload = false;\r\n /**\r\n * When this path is specified (not null) then downloader will track download progress of each file within this folder.\r\n * Every download gets own subfolder (transferID_downloadID) with dumped options of the download (options.json) and files.json\r\n * which tracks all pending files. It will not contain completed files or those that have not started yet. Tracking is performed with\r\n * chunk-precision (not byte-precision). Worst case the download tracker may lose N chunks (N being the number of {@link parallelChunks}).\r\n * @default null\r\n */\r\n downloadTrackingBaseFolder: string | null = null;\r\n /**\r\n * Minimum time interval (in milliseconds) between consecutive dumps of downloader state to disk.\r\n * @default 5000\r\n */\r\n downloadTrackingIntervalMillis = 5000;\r\n /**\r\n * Time window length (in milliseconds) over which the downloader calculates momentary download speed.\r\n * @default 2000\r\n */\r\n momentaryDownloadSpeedTimespanMillis = 2000;\r\n /**\r\n * A value that will be appended to all log events.\r\n */\r\n trackingId: number | string | undefined;\r\n /**\r\n * Skips download registration if set to true\r\n * @default false\r\n */\r\n skipRegistration = false;\r\n}\r\n\r\nexport default DownloadOptions;"]}
|
|
1
|
+
{"version":3,"file":"downloadOptions.js","sourceRoot":"","sources":["../../../src/downloader/downloadOptions.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe;IACjB;;;OAGG;IACH,UAAU,GAAkB,IAAI,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,sBAAsB,GAAkB,IAAI,CAAC;IAC7C;;;;OAIG;IACH,cAAc,GAAkB,IAAI,CAAC;IACrC;;;OAGG;IACH,0BAA0B,GAAG,KAAK,CAAC;IACnC;;;;OAIG;IACH,aAAa,GAAG,CAAC,CAAC;IAClB;;;;;OAKG;IACH,cAAc,GAAG,CAAC,CAAC;IACnB;;;OAGG;IACH,gBAAgB,GAAG,EAAE,CAAC;IACtB;;;OAGG;IACH,SAAS,GAAkB,IAAI,CAAC;IAChC;;;OAGG;IACH,YAAY,GAAkB,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/C;;;OAGG;IACH,cAAc,GAAG,IAAI,CAAC;IACtB;;;OAGG;IACH,YAAY,GAAG,KAAK,CAAC;IACrB;;;OAGG;IACH,4BAA4B,GAAG,KAAK,CAAC;IACrC;;;;;OAKG;IACH,sBAAsB,GAAG,CAAC,CAAC;IAC3B;;;;OAIG;IACH,yCAAyC,GAAG,EAAE,CAAC;IAC/C;;;;;OAKG;IACH,oCAAoC,GAAG,IAAI,CAAC;IAC5C;;;OAGG;IACH,kBAAkB,GAAG,GAAG,CAAC;IACzB;;;OAGG;IACH,yBAAyB,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,8BAA8B,GAAG,IAAI,CAAC;IACtC;;;;;OAKG;IACH,uCAAuC,GAAG,KAAK,CAAC;IAChD;;;;;;OAMG;IACH,0BAA0B,GAAkB,IAAI,CAAC;IACjD;;;OAGG;IACH,8BAA8B,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,oCAAoC,GAAG,IAAI,CAAC;IAC5C;;OAEG;IACH,UAAU,CAA8B;IACxC;;;OAGG;IACH,gBAAgB,GAAG,KAAK,CAAC;IACzB;;;;OAIG;IACH,yCAAyC,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACnE;;;;OAIG;IACH,+BAA+B,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACrD;;;;OAIG;IACH,+BAA+B,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,uCAAuC,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;CAC/D;AAED,eAAe,eAAe,CAAC","sourcesContent":["class DownloadOptions {\r\n /**\r\n * logintoken of the current user - used to fetch transfer (optionally also pwd-protected), company setup, register download\r\n * @default null\r\n */\r\n logintoken: string | null = null;\r\n /**\r\n * Path template where files should be downloaded. Provide this OR {@link downloadFolder}. Supports placeholders:\r\n * <id> - transferId\r\n * <subject>\r\n * <description>\r\n * <domain> - domain based on \"from\" email (e.g. user@example.com --> example.com)\r\n * <emailfrom>\r\n * <datesent> - will be formatted as \"YYYY-MM-DD-hh-mm\"\r\n * <datesent:format> - will be formatted according to \"format\"; supported placeholders in \"format\": YYYY, MM, DD, hh, mm\r\n * <emailto> - all recipients emails comma-separated\r\n * <{custom_field_label}> - specified custom field named by label will replaced with that custom field's value\r\n * @default null\r\n */\r\n downloadFolderTemplate: string | null = null;\r\n /**\r\n * The path where files should be downloaded. No placeholders. Provide this OR {@link downloadFolderTemplate}.\r\n * When {@link downloadFolderTemplate} is specified this value will be overwritten with the resolved (final) path.\r\n * @default null\r\n */\r\n downloadFolder: string | null = null;\r\n /**\r\n * HTTP request timeout for web api (not Fileserver) in milliseconds.\r\n * @default 60000\r\n */\r\n apiRequestTotalTimeoutInMs = 60000;\r\n /**\r\n * How many times to attempt HTTP requests towards web API (not Fileserver) e.g. when getting a transfer from the API.\r\n * @see https://github.com/sindresorhus/got#documentation\r\n * @default 3\r\n */\r\n apiRetryLimit = 3;\r\n /**\r\n * Maximum number (N) of chunks that can be downloaded in parallel. As soon as any single chunk is complete then next available chunk is started (to keep parallel chunks always at N).\r\n * - If there is one huge file with X chunks (X>N) - first N chunks will be picked up.\r\n * - If there are 2 files, each with 3 chunks and N=4 - both files will be started (all 3 chunks of first file and only 1st chunk of the 2nd file).\r\n * @default 8\r\n */\r\n parallelChunks = 8;\r\n /**\r\n * How many times to try write downloaded bytes of a chunk and optionally verify its MD5. Set to -1 to keep trying forever.\r\n * @default 20\r\n */\r\n maxWriteAttempts = 20;\r\n /**\r\n * The user-agent header that will be used for all fileserver GetFile requests.\r\n * @default null\r\n */\r\n userAgent: string | null = null;\r\n /**\r\n * Maximum size (S) of a chunk in bytes. When set to a number - downloader will split files into chunks of this size (size of last chunk) <= S. Set to null to download every file in one HTTPS request.\r\n * @default 50 * 1024 * 1024 (50MB) - this matches the default pre-calculated rangehashes on Fileservers.\r\n */\r\n maxChunkSize: number | null = 50 * 1024 * 1024;\r\n /**\r\n * When set to **true** downloader will verify MD5 after every chunk. This doesn't work for end to end encrypted transfers.\r\n * @efault true\r\n */\r\n checkChunksMd5 = true;\r\n /**\r\n * When set to **true** downloader will verify MD5 for every completed file (entire file). To use this feature provided the MD5 value in DownloadFileInfo passed to the downloader. This doesn't work for end to end encrypted transfers.\r\n * @note NOT SUPPORTED (YET)\r\n */\r\n checkFileMd5 = false;\r\n /**\r\n * How long should it take for HTTP library to throw error when no download data is pumped to response (milliseconds).\r\n * @default 30000\r\n */\r\n fileserverDownloadIdleTimeMs = 30000;\r\n /**\r\n * How many times to attempt a single HTTP download request against Fileserver's GetFile. Set this options to 0 to keep trying forever.\r\n * When set to 0 (retring forever) the downloader will report \"network problem\" after 10 failed attempts (via event and a status field).\r\n * When connectivity is back it will be reported with an event as well.\r\n * @default 0\r\n */\r\n fileserverAttemptLimit = 0;\r\n /**\r\n * Downloader will assume there is a \"network problem\" after this many consecutive network errors without any successful progress.\r\n * Set to -1 to disable this feature.\r\n * @default 10;\r\n */\r\n fileserverOfflineAfterConsecutiveAttempts = 10;\r\n /**\r\n * Once downloader sees a \"network problem\" it will retry every pending chunk with this interval (in milliseconds).\r\n * This interval will be also used to calculate exponential backoff between first network error and getting into \"network problem\" state\r\n * and also is the maximum interval of the exponential backoff.\r\n * @default 5000;\r\n */\r\n fileserverOfflineCheckIntervalMillis = 5000;\r\n /**\r\n * Number of milliseconds between progress events emitted by the downloader (throttling - they won't be emitted more often that this amount of time).\r\n * @default 100\r\n */\r\n progressTickMillis = 100;\r\n /**\r\n * If set to **true** ==> the downloader will delete those files which were being downloaded, but failed.\r\n * @default true\r\n */\r\n deleteFailedFilesFromDisk = true;\r\n /**\r\n * When set to **true** ==> after user requests download abort - the downloader will delete all files which it tried to download, no matter their size and status.\r\n * @default true\r\n */\r\n deleteAbortedDownloadsFromDisk = true;\r\n /**\r\n * When set to **true** ==> after download completes or is aborted - the downloader will delete the main download folder if it is empty (no files have been downloaded and there are no other files left over).\r\n * Default is false as usually the download folder is set to be the OS-special (e.g. on windows --> c:\\users\\jane\\Downloads). We don't want to try removing that.\r\n * Set this to **true** only if specifying custom download folders.\r\n * @default false\r\n */\r\n deleteEmptyDownloadFoldersAfterDownload = false;\r\n /**\r\n * When this path is specified (not null) then downloader will track download progress of each file within this folder.\r\n * Every download gets own subfolder (transferID_downloadID) with dumped options of the download (options.json) and files.json\r\n * which tracks all pending files. It will not contain completed files or those that have not started yet. Tracking is performed with\r\n * chunk-precision (not byte-precision). Worst case the download tracker may lose N chunks (N being the number of {@link parallelChunks}).\r\n * @default null\r\n */\r\n downloadTrackingBaseFolder: string | null = null;\r\n /**\r\n * Minimum time interval (in milliseconds) between consecutive dumps of downloader state to disk.\r\n * @default 5000\r\n */\r\n downloadTrackingIntervalMillis = 5000;\r\n /**\r\n * Time window length (in milliseconds) over which the downloader calculates momentary download speed.\r\n * @default 2000\r\n */\r\n momentaryDownloadSpeedTimespanMillis = 2000;\r\n /**\r\n * A value that will be appended to all log events.\r\n */\r\n trackingId: number | string | undefined;\r\n /**\r\n * Skips download registration if set to true\r\n * @default false\r\n */\r\n skipRegistration = false;\r\n /**\r\n * Transfer size threshold (in bytes) for always preallocating destination files with truncate.\r\n * If total transfer size is <= this value, downloader always preallocates.\r\n * @default 5 * 1024 * 1024 * 1024 (5GB)\r\n */\r\n preallocateAlwaysUntilTransferSizeInBytes = 5 * 1024 * 1024 * 1024;\r\n /**\r\n * Probe file size (in bytes) used to measure truncate performance for large transfers.\r\n * If the probe finishes quickly enough, downloader preallocates full file sizes.\r\n * @default 1024 * 1024 * 1024 (1GB)\r\n */\r\n preallocateProbeFileSizeInBytes = 1024 * 1024 * 1024;\r\n /**\r\n * Maximum probe truncate duration considered \"fast\" (milliseconds).\r\n * If probe takes longer, downloader skips full preallocation and only checks free space.\r\n * @default 3000\r\n */\r\n preallocateProbeMaxDurationInMs = 3000;\r\n /**\r\n * Extra free-space margin (in bytes) required when downloader skips full preallocation.\r\n * @default 512 * 1024 * 1024 (512MB)\r\n */\r\n preallocateFreeSpaceSafetyMarginInBytes = 512 * 1024 * 1024;\r\n}\r\n\r\nexport default DownloadOptions;\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesDownloader.d.ts","sourceRoot":"","sources":["../../../src/downloader/filesDownloader.ts"],"names":[],"mappings":"AAGA,OAAO,gBAAgB,MAAM,4BAA4B,CAAC;AAK1D,OAAO,cAAc,MAAM,kCAAkC,CAAC;AAE9D,OAAO,wBAAwB,MAAM,oCAAoC,CAAC;AAG1E,OAAO,KAAK,iBAAiB,MAAM,0CAA0C,CAAC;AAC9E,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,qBAAqB,MAAM,yDAAyD,CAAC;AACjG,OAAO,KAAK,uBAAuB,MAAM,mCAAmC,CAAC;AAC7E,OAAO,KAAK,uBAAuB,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,0BAA0B,MAAM,mDAAmD,CAAC;AAChG,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAChE,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,SAAS,MAAM,2BAA2B,CAAC;AAIvD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,oBAAoB,MAAM,yCAAyC,CAAC;AAEhF,MAAM,CAAC,OAAO,OAAO,eAAe;;
|
|
1
|
+
{"version":3,"file":"filesDownloader.d.ts","sourceRoot":"","sources":["../../../src/downloader/filesDownloader.ts"],"names":[],"mappings":"AAGA,OAAO,gBAAgB,MAAM,4BAA4B,CAAC;AAK1D,OAAO,cAAc,MAAM,kCAAkC,CAAC;AAE9D,OAAO,wBAAwB,MAAM,oCAAoC,CAAC;AAG1E,OAAO,KAAK,iBAAiB,MAAM,0CAA0C,CAAC;AAC9E,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,qBAAqB,MAAM,yDAAyD,CAAC;AACjG,OAAO,KAAK,uBAAuB,MAAM,mCAAmC,CAAC;AAC7E,OAAO,KAAK,uBAAuB,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,0BAA0B,MAAM,mDAAmD,CAAC;AAChG,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAChE,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,SAAS,MAAM,2BAA2B,CAAC;AAIvD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,oBAAoB,MAAM,yCAAyC,CAAC;AAEhF,MAAM,CAAC,OAAO,OAAO,eAAe;;gBA0B5B,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC,EACjD,QAAQ,EAAE,uBAAuB,GAAG,SAAS,EAC7C,UAAU,EAAE,iBAAiB,EAC7B,qBAAqB,EAAE,qBAAqB,EAC5C,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,oBAAoB,EAClC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,SAAS,EACpB,uBAAuB,EAAE,uBAAuB,EAChD,0BAA0B,EAAE,0BAA0B,CAAC,MAAM,CAAC,EAC9D,wBAAwB,EAAE,wBAAwB,EAClD,QAAQ,CAAC,EAAE,uBAAuB;IA4CtC,OAAO,CAAC,oBAAoB;IAOtB,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAoGzC,YAAY,CAAC,OAAO,EAAE;QAAE,sBAAsB,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE,GAAG,IAAI;IAK3G,MAAM,IAAI,OAAO;IAIjB,YAAY,IAAI,OAAO;IAKvB,eAAe,CAAC,SAAS,EAAE,OAAO,GAAG,iBAAiB;IAItD,IAAI,MAAM,IAAI,cAAc,CAE3B;CA8TJ"}
|
|
@@ -30,6 +30,9 @@ export default class FilesDownloader {
|
|
|
30
30
|
#ongoingChunkDownloads;
|
|
31
31
|
#ongoingFileDownloads;
|
|
32
32
|
#encryptionData;
|
|
33
|
+
#totalTransferSizeInBytes;
|
|
34
|
+
#shouldPreallocateFiles = true;
|
|
35
|
+
#preallocationStrategyInitialized = false;
|
|
33
36
|
constructor(logger, transferId, trackId, downloadId, region, files, oldState, fileSystem, concurrencyController, eventsEngine, cryptography, options, apiClient, fileserverClientFactory, chunkStreamProviderFactory, downloadRegistrationMode, e2eeData) {
|
|
34
37
|
if (!transferId)
|
|
35
38
|
throw new DownloadError(`No transfer specified`);
|
|
@@ -61,6 +64,7 @@ export default class FilesDownloader {
|
|
|
61
64
|
this.#downloadRegistrationMode = downloadRegistrationMode;
|
|
62
65
|
this.#ongoingChunkDownloads = {};
|
|
63
66
|
this.#ongoingFileDownloads = {};
|
|
67
|
+
this.#totalTransferSizeInBytes = this.#files.reduce((sum, file) => sum + file.sizeInBytes, 0);
|
|
64
68
|
if (e2eeData)
|
|
65
69
|
this.#encryptionData = this.createEncryptionData(e2eeData);
|
|
66
70
|
}
|
|
@@ -238,9 +242,15 @@ export default class FilesDownloader {
|
|
|
238
242
|
}
|
|
239
243
|
};
|
|
240
244
|
async #createDownloadFile(file) {
|
|
245
|
+
await this.#resolvePreallocationStrategy();
|
|
241
246
|
this.#downloadState.fileCreating(file);
|
|
242
|
-
|
|
243
|
-
|
|
247
|
+
try {
|
|
248
|
+
await this.#autoGenerateUniqueDownloadFile(file);
|
|
249
|
+
this.#downloadState.fileCreated(file);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
throw this.#toDownloadError(error, `Creating download file failed for ${file.downloadPath}`);
|
|
253
|
+
}
|
|
244
254
|
}
|
|
245
255
|
async #autoGenerateUniqueDownloadFile(file) {
|
|
246
256
|
let counter = 0;
|
|
@@ -248,7 +258,8 @@ export default class FilesDownloader {
|
|
|
248
258
|
do {
|
|
249
259
|
const newFileName = `unconfirmed_${fileLeafName}_${this.#cryptography.generateRandomString(10)}.fmdownload`;
|
|
250
260
|
const newPath = this.#fileSystem.replaceFilenameInPath(file.downloadPath, newFileName);
|
|
251
|
-
|
|
261
|
+
const sizeToReserve = this.#shouldPreallocateFiles ? file.sizeInBytes : undefined;
|
|
262
|
+
if (await this.#fileSystem.createFile(newPath, false, sizeToReserve)) {
|
|
252
263
|
// eslint-disable-next-line no-param-reassign
|
|
253
264
|
file.temporaryDownloadPath = newPath;
|
|
254
265
|
return;
|
|
@@ -257,6 +268,62 @@ export default class FilesDownloader {
|
|
|
257
268
|
} while (counter < 10000);
|
|
258
269
|
throw new DownloadError(`Downloader could not generate unique download path for ${file.downloadPath}. It tried until counter=${counter}`, undefined, DownloadErrorCode.TooManyFileRenameAttempts);
|
|
259
270
|
}
|
|
271
|
+
async #resolvePreallocationStrategy() {
|
|
272
|
+
if (this.#preallocationStrategyInitialized)
|
|
273
|
+
return;
|
|
274
|
+
if (this.#totalTransferSizeInBytes <= this.#options.preallocateAlwaysUntilTransferSizeInBytes) {
|
|
275
|
+
this.#shouldPreallocateFiles = true;
|
|
276
|
+
this.#preallocationStrategyInitialized = true;
|
|
277
|
+
this.#logger.logInfo(`[PREALLOC] Using full preallocation for transfer size ${this.#totalTransferSizeInBytes}B (threshold=${this.#options.preallocateAlwaysUntilTransferSizeInBytes}B)`);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
const probeSize = Math.min(this.#options.preallocateProbeFileSizeInBytes, this.#totalTransferSizeInBytes);
|
|
282
|
+
const maxProbeDurationInMs = this.#options.preallocateProbeMaxDurationInMs;
|
|
283
|
+
const probeDurationInMs = await this.#probePreallocationDurationInMs(probeSize);
|
|
284
|
+
if (probeDurationInMs <= maxProbeDurationInMs) {
|
|
285
|
+
this.#shouldPreallocateFiles = true;
|
|
286
|
+
this.#preallocationStrategyInitialized = true;
|
|
287
|
+
this.#logger.logInfo(`[PREALLOC] Probe truncate ${probeSize}B finished in ${probeDurationInMs}ms (limit=${maxProbeDurationInMs}ms). Using full preallocation.`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const requiredBytes = BigInt(this.#totalTransferSizeInBytes);
|
|
291
|
+
const safetyMarginBytes = BigInt(this.#options.preallocateFreeSpaceSafetyMarginInBytes);
|
|
292
|
+
await this.#fileSystem.ensureEnoughFreeSpace(this.#options.downloadFolder, requiredBytes, safetyMarginBytes);
|
|
293
|
+
this.#shouldPreallocateFiles = false;
|
|
294
|
+
this.#preallocationStrategyInitialized = true;
|
|
295
|
+
this.#logger.logInfo(`[PREALLOC] Probe truncate ${probeSize}B took ${probeDurationInMs}ms (limit=${maxProbeDurationInMs}ms). Skipping full preallocation and using free-space check only.`);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
throw this.#toDownloadError(error, `Could not resolve preallocation strategy for download folder ${this.#options.downloadFolder}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async #probePreallocationDurationInMs(probeSize) {
|
|
302
|
+
const probeFilePath = this.#buildPreallocationProbePath();
|
|
303
|
+
const startedAt = Date.now();
|
|
304
|
+
try {
|
|
305
|
+
const created = await this.#fileSystem.createFile(probeFilePath, false, probeSize);
|
|
306
|
+
if (!created)
|
|
307
|
+
throw new DownloadError(`Could not create preallocation probe file at ${probeFilePath}`);
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
await this.#fileSystem.deleteFile(probeFilePath);
|
|
311
|
+
}
|
|
312
|
+
return Date.now() - startedAt;
|
|
313
|
+
}
|
|
314
|
+
#buildPreallocationProbePath() {
|
|
315
|
+
const probeName = `fmdownload_prealloc_probe_${this.#cryptography.generateRandomString(8)}.tmp`;
|
|
316
|
+
return this.#fileSystem.pathJoin(this.#options.downloadFolder, probeName);
|
|
317
|
+
}
|
|
318
|
+
#toDownloadError(error, message) {
|
|
319
|
+
if (error instanceof DownloadError)
|
|
320
|
+
return error;
|
|
321
|
+
const wrappedError = error instanceof Error
|
|
322
|
+
? error
|
|
323
|
+
: new Error(`${error}`);
|
|
324
|
+
const code = this.#fileSystem.fileErrorToDownloadErrorCode(wrappedError);
|
|
325
|
+
return new DownloadError(message, { cause: wrappedError }, code);
|
|
326
|
+
}
|
|
260
327
|
async #downloadAllChunksForFile(file) {
|
|
261
328
|
if (file.chunks.length) {
|
|
262
329
|
// MAIN CHUNK LOOP
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesDownloader.js","sourceRoot":"","sources":["../../../src/downloader/filesDownloader.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,mBAAmB,MAAM,yCAAyC,CAAC;AAC1E,OAAO,gBAAgB,MAAM,4BAA4B,CAAC;AAC1D,OAAO,oBAAoB,MAAM,0CAA0C,CAAC;AAC5E,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,eAAe,MAAM,sBAAsB,CAAC;AACnD,OAAO,cAAc,MAAM,kCAAkC,CAAC;AAC9D,OAAO,iBAAiB,MAAM,6BAA6B,CAAC;AAC5D,OAAO,wBAAwB,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAmB3D,MAAM,CAAC,OAAO,OAAO,eAAe;IACvB,OAAO,CAAiB;IACxB,WAAW,CAAS;IACpB,QAAQ,CAAqB;IAC7B,WAAW,CAAS;IACpB,OAAO,CAAU;IACjB,MAAM,CAA0B;IAChC,cAAc,CAAgB;IAC9B,QAAQ,CAAkB;IAC1B,sBAAsB,CAAwB;IAC9C,WAAW,CAAoB;IAC/B,UAAU,CAAY;IACtB,wBAAwB,CAA0B;IAClD,2BAA2B,CAAqC;IAChE,yBAAyB,CAA2B;IACpD,aAAa,CAAe;IAC5B,aAAa,CAAuB;IACpC,sBAAsB,CAAgC;IACtD,qBAAqB,CAAsC;IAC3D,eAAe,CAAqC;IAE7D,YACI,MAAsB,EACtB,UAAkB,EAClB,OAA2B,EAC3B,UAAkB,EAClB,MAA0B,EAC1B,KAAiD,EACjD,QAA6C,EAC7C,UAA6B,EAC7B,qBAA4C,EAC5C,YAA0B,EAC1B,YAAkC,EAClC,OAAwB,EACxB,SAAoB,EACpB,uBAAgD,EAChD,0BAA8D,EAC9D,wBAAkD,EAClD,QAAkC;QAElC,IAAI,CAAC,UAAU;YACX,MAAM,IAAI,aAAa,CAAC,uBAAuB,CAAC,CAAC;QAErD,IAAI,CAAC,KAAK;YACN,MAAM,IAAI,aAAa,CAAC,8BAA8B,CAAC,CAAC;QAE5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAC1C,MAAM,IAAI,aAAa,CAAC,8BAA8B,CAAC,CAAC;QAE5D,IAAI,CAAC,OAAO,CAAC,cAAc;YACvB,MAAM,IAAI,aAAa,CAAC,6BAA6B,CAAC,CAAC;QAE3D,IAAI,QAAQ,IAAI,QAAQ,CAAC,EAAE,CAAC,MAAM,KAAK,EAAE;YACrC,MAAM,IAAI,aAAa,CAAC,0BAA0B,CAAC,CAAC;QAExD,IAAI,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,KAAK,EAAE;YACtC,MAAM,IAAI,aAAa,CAAC,sCAAsC,CAAC,CAAC;QAEpE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;QAChI,IAAI,CAAC,sBAAsB,GAAG,qBAAqB,CAAC;QACpD,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,wBAAwB,GAAG,uBAAuB,CAAC;QACxD,IAAI,CAAC,2BAA2B,GAAG,0BAA0B,CAAC;QAC9D,IAAI,CAAC,yBAAyB,GAAG,wBAAwB,CAAC;QAC1D,IAAI,CAAC,sBAAsB,GAAG,EAAE,CAAC;QACjC,IAAI,CAAC,qBAAqB,GAAG,EAAE,CAAC;QAEhC,IAAI,QAAQ;YACR,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IACnE,CAAC;IAEO,oBAAoB,CAAC,QAAiC;QAC1D,OAAO;YACH,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,GAAG,EAAE,QAAQ,CAAC,GAAG;SACpB,CAAC;IACN,CAAC;IAED,KAAK,CAAC,KAAK;QACP,IAAI,CAAC;YACD,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAE1B,IAAI,mBAAmB,GAAG,IAAI,CAAC;YAE/B,OAAO,mBAAmB,EAAE,CAAC;gBACzB,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;gBAE5B,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;gBACzD,IAAI,CAAC,cAAc,CAAC,wBAAwB,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;gBAE1E,iBAAiB;gBACjB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC7B,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ;wBACjC,SAAS;oBAEb,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,KAAK,KAAK;wBACvC,MAAM;oBAEV,sFAAsF;oBACtF,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;wBACtB,sGAAsG;wBACtG,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;4BAC9B,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,aAAa,CAAC,QAAQ,IAAI,CAAC,YAAY,kEAAkE,EAAE,SAAS,EAAE,iBAAiB,CAAC,2BAA2B,CAAC,CAAC,CAAC;4BAC/M,SAAS;wBACb,CAAC;wBAED,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,qBAAsB,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;4BAC7E,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,aAAa,CAAC,QAAQ,IAAI,CAAC,YAAY,kCAAkC,IAAI,CAAC,qBAAqB,0CAA0C,EAAE,SAAS,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC;4BAC3O,SAAS;wBACb,CAAC;oBACL,CAAC;yBACI,CAAC;wBACF,gGAAgG;wBAChG,wHAAwH;wBACxH,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;4BAC7B,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;4BACjE,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;wBAC5D,CAAC;wBAED,iCAAiC;wBACjC,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;oBACzC,CAAC;oBAED,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACvF,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,UAAU,CAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;wBAC3D,OAAO,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;oBAC1C,CAAC,CAAC,CAAC;oBAEH,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;oBAEvC,+HAA+H;oBAC/H,MAAM,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC;oBAE3C,gDAAgD;oBAChD,mFAAmF;gBACvF,CAAC;gBAED,sMAAsM;gBAEtM,wCAAwC;gBACxC,MAAM,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC;gBAEpE,uCAAuC;gBACvC,2FAA2F;gBAC3F,gGAAgG;gBAChG,oKAAoK;gBACpK,oHAAoH;gBACpH,MAAM,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEvF,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,cAAc,CAAC,OAAO,EAAE,CAAC;oBACxD,oDAAoD;oBACpD,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;gBACvC,CAAC;gBAED,mBAAmB,GAAG,MAAM,IAAI,CAAC,qCAAqC,EAAE,CAAC;YAC7E,CAAC;YAED,MAAM,IAAI,CAAC,uCAAuC,EAAE,CAAC;QACzD,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAc,EAAE,qCAAqC,CAAC,CAAC;YAC7E,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,KAAc,CAAC,CAAC;QAClD,CAAC;gBACO,CAAC;YACL,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACjC,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IAED,gBAAgB,CAAC,IAA6C,EAAE,IAAY,EAAE,MAAc;QACxF,IAAI,aAAa,GAAG,IAAI,CAAC;QACzB,IAAI,IAAI,YAAY,gBAAgB;YAChC,aAAa,GAAG,oBAAoB,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAEpE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,6CAA6C,IAAI,WAAW,MAAM,OAAO,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9I,CAAC;IAED,YAAY,CAAC,OAAuF;QAChG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM;QACF,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;IACxC,CAAC;IAED,YAAY;QACR,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzF,OAAO,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;IAC9C,CAAC;IAED,eAAe,CAAC,SAAkB;QAC9B,OAAO,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,MAAM;QACN,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,oCAAoC;QACtC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAC7B,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,EAAE,qCAAqC,CAAC,CAAC;gBAC/F,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YAClE,CAAC;YAED,IAAI,IAAI,CAAC,WAAW;gBAChB,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED,KAAK,CAAC,yBAAyB;QAC3B,IAAI,CAAC,cAAc,CAAC,2BAA2B,EAAE,CAAC;QAClD,MAAM,IAAI,CAAC,UAAU,CAAC,wBAAwB,CAAC;YAC3C,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,KAAK,EAAE,IAAI,CAAC,QAAQ;YACpB,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,qBAAqB;YAC9C,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS;SACnC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,QAAgB,EAAE,QAAgB;QAC1D,IAAI,CAAC,cAAc,CAAC,uBAAuB,EAAE,CAAC;QAC9C,MAAM,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC;YACvC,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,KAAK,EAAE,IAAI,CAAC,QAAQ;YACpB,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,qBAAqB;YAC9C,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS;YAChC,QAAQ;YACR,QAAQ;SACX,CAAC,CAAC;IACP,CAAC;IAED,kBAAkB;QACd,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,qBAAqB,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACxG,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,sBAAsB,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC1G,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAClG,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,sBAAsB,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9G,CAAC;IAED,qBAAqB;QACjB,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,qBAAqB,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC3G,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,sBAAsB,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC7G,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACrG,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,sBAAsB,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACjH,CAAC;IAED,gBAAgB,GAAG,KAAK,EAAE,IAA2B,EAAE,EAAE;QACrD,IAAI,IAAI,CAAC,yBAAyB,KAAK,wBAAwB,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB;YAC9G,MAAM,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAErE,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;IACzD,CAAC,CAAC;IAEF,iBAAiB,GAAG,KAAK,EAAE,IAA2B,EAAE,EAAE;QACtD,oKAAoK;QACpK,IAAI,CAAC;YACD,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,2CAA2C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5F,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,KAAsB,CAAC,CAAC;QAC7E,CAAC;IACL,CAAC,CAAC;IAEF,iBAAiB,GAAG,KAAK,EAAE,QAA+B,EAAE,EAAE;QAC1D,IAAI,QAAQ,CAAC,IAAI,CAAC,qBAAqB,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI;YACnI,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrG,CAAC,CAAC;IAEF,aAAa,GAAG,KAAK,EAAE,IAAiC,EAAE,EAAE;QACxD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;QAErD,IAAI,IAAI,CAAC,QAAQ,CAAC,yBAAyB,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC;gBAClD,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAE9D,IAAI,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAClC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,CAAC;gBAClF,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YACvE,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,KAAK,CAAC,mBAAmB,CAAC,IAAsB;QAC5C,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,+BAA+B,CAAC,IAAsB;QACxD,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QACpE,GAAG,CAAC;YACA,MAAM,WAAW,GAAG,eAAe,YAAY,IAAI,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,EAAE,CAAC,aAAa,CAAC;YAC5G,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;YACvF,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBACtE,6CAA6C;gBAC7C,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC;gBACrC,OAAO;YACX,CAAC;YAED,OAAO,IAAI,CAAC,CAAC;QACjB,CAAC,QAAQ,OAAO,GAAG,KAAK,EAAE;QAE1B,MAAM,IAAI,aAAa,CAAC,0DAA0D,IAAI,CAAC,YAAY,4BAA4B,OAAO,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC,yBAAyB,CAAC,CAAC;IACtM,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,IAAsB;QAClD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACrB,kBAAkB;YAClB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC9B,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,QAAQ;oBACnC,SAAS;gBAEb,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACvE,IAAI,iBAAiB,KAAK,KAAK,EAAE,CAAC;oBAC9B,6EAA6E;oBAC7E,MAAM;gBACV,CAAC;YACL,CAAC;QACL,CAAC;aACI,CAAC;YACF,gBAAgB;YAChB,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC;IACL,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,IAAsB,EAAE,KAA4B;QAC3E,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,CAAC;QAErE,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;YAC1C,sDAAsD;YACtD,gBAAgB,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,4DAA4D;YAC5D,gBAAgB,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,2BAA2B,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACxO,MAAM,YAAY,GAAkB,eAAe,CAAC,QAAQ,EAAE,CAAC;QAC/D,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC;QAErD,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE;YACtB,gBAAgB,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,qCAAqC;QACvC,IAAI,wBAAwB,GAAG,IAAI,CAAC;QACpC,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,cAAc,CAAC,QAAQ,EAAE,CAAC;YACzD,wBAAwB,GAAG,KAAK,CAAC;YACjC,IAAI,IAAI,CAAC,QAAQ,CAAC,8BAA8B;gBAC5C,MAAM,IAAI,CAAC,oCAAoC,EAAE,CAAC;YAEtD,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;QAClC,CAAC;aACI,IAAI,IAAI,CAAC,cAAc,CAAC,yBAAyB,EAAE,CAAC;YACrD,wBAAwB,GAAG,KAAK,CAAC;YACjC,IAAI,IAAI,CAAC,yBAAyB,KAAK,wBAAwB,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB;gBACvG,MAAM,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAE3C,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,wBAAwB,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,uCAAuC;QACzC,IAAI,CAAC;YACD,IAAI,IAAI,CAAC,QAAQ,CAAC,uCAAuC,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAe,CAAC;gBAC5H,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAe,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,+CAA+C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACxF,CAAC;IACL,CAAC;IAED,KAAK,CAAC,2CAA2C,CAAC,IAA0B;QACxE,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnE,IAAI,gBAAgB,GAAG,IAAI,CAAC,YAAY,CAAC;QACzC,GAAG,CAAC;YACA,+GAA+G;YAC/G,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;YACrE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,aAAa,IAAI,CAAC,CAAC;gBACnB,MAAM,OAAO,GAAG;oBACZ,GAAG,EAAE,QAAQ,CAAC,GAAG;oBACjB,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,KAAK,aAAa,GAAG;oBAC3C,GAAG,EAAE,QAAQ,CAAC,GAAG;iBACpB,CAAC;gBACF,gBAAgB,GAAG,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAChE,CAAC;QACL,CAAC,QAAQ,OAAO,KAAK,KAAK,IAAI,aAAa,GAAG,KAAK,EAAE;QAErD,IAAI,CAAC,OAAO;YACR,MAAM,IAAI,aAAa,CAAC,yCAAyC,IAAI,CAAC,qBAAqB,OAAO,IAAI,CAAC,YAAY,4BAA4B,aAAa,EAAE,EAAE,SAAS,EAAE,iBAAiB,CAAC,yBAAyB,CAAC,CAAC;QAE5N,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,SAA4B,CAAC;QACjC,OAAO,CAAC,OAAO,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YACnC,aAAa,IAAI,CAAC,CAAC;YACnB,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,qBAAsB,EAAE,gBAAgB,CAAC,CAAC;gBACjF,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,sBAAsB,IAAI,CAAC,qBAAqB,OAAO,gBAAgB,eAAe,aAAa,EAAE,CAAC,CAAC;YAChI,CAAC;YACD,OAAO,KAAK,EAAE,CAAC;gBACX,SAAS,GAAG,KAAc,CAAC;gBAC3B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,+BAA+B,IAAI,CAAC,qBAAqB,OAAO,gBAAgB,YAAY,aAAa,EAAE,CAAC,CAAC;YAClJ,CAAC;QACL,CAAC;QACD,IAAI,CAAC,OAAO;YACR,MAAM,IAAI,aAAa,CAAC,+BAA+B,IAAI,CAAC,qBAAqB,OAAO,gBAAgB,UAAU,aAAa,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,iBAAiB,CAAC,yCAAyC,CAAC,CAAC;;YAEhO,OAAO,gBAAgB,CAAC;IAChC,CAAC;CACJ","sourcesContent":["/* eslint-disable no-await-in-loop */\r\nimport DownloadState from \"./downloadStatus/downloadState.js\";\r\nimport DownloadFilePromise from \"./downloadStatus/downloadFilePromise.js\";\r\nimport DownloadFileInfo from \"./dtos/downloadFileInfo.js\";\r\nimport DownloadFileProgress from \"./downloadStatus/downloadFileProgress.js\";\r\nimport DownloadError from \"./dtos/downloadError.js\";\r\nimport TransferDownloadEvent from \"./dtos/downloadEvent.js\";\r\nimport ChunkDownloader from \"./chunkDownloader.js\";\r\nimport TransferStatus from \"../utils/types/transferStatus.js\";\r\nimport DownloadErrorCode from \"./dtos/downloadErrorCode.js\";\r\nimport DownloadRegistrationMode from \"./dtos/downloadRegistrationMode.js\";\r\nimport { promiseAllSettled } from \"../utils/promiseAll.js\";\r\n\r\nimport type FileSystemService from \"../utils/fileSystem/fileSystemService.js\";\r\nimport type DownloadOptions from \"./downloadOptions.js\";\r\nimport type ConcurrencyController from \"../utils/concurrencyController/concurrencyController.js\";\r\nimport type DownloaderRestoredState from \"./dtos/downloaderRestoredState.js\";\r\nimport type FileServerClientFactory from \"../utils/api/fileServerClientFactory.js\";\r\nimport type ChunkStreamProviderFactory from \"../utils/fileSystem/chunkStreamProviderFactory.js\";\r\nimport type EventsEngine from \"../utils/events/eventsEngine.js\";\r\nimport type DownloadEventArgs from \"./eventArgs/downloadEventArgs.js\";\r\nimport type ApiClient from \"../utils/api/apiClient.js\";\r\nimport type FileDownloadEventArgs from \"./eventArgs/fileDownloadEventArgs.js\";\r\nimport type FileDownloadFailedEventArgs from \"./eventArgs/fileDownloadFailedEventArgs.js\";\r\nimport type DownloadFileChunkInfo from \"./dtos/downloadFileChunkInfo.js\";\r\nimport type DownloadLogger from \"./downloadLogger.js\";\r\nimport type { ChunkStreamProviderEncryptionData } from \"../utils/fileSystem/chunkStreamProvider.js\";\r\nimport type { DownloadCommandE2eeData } from \"./dtos/downloadCommand.js\";\r\nimport type CryptographyProvider from \"../utils/crypto/cryptographyProvider.js\";\r\n\r\nexport default class FilesDownloader {\r\n readonly #logger: DownloadLogger;\r\n readonly #transferId: string;\r\n readonly #trackId: string | undefined;\r\n readonly #downloadId: string;\r\n readonly #region?: string;\r\n readonly #files: Array<DownloadFileInfo>;\r\n readonly #downloadState: DownloadState;\r\n readonly #options: DownloadOptions;\r\n readonly #concurrencyController: ConcurrencyController;\r\n readonly #fileSystem: FileSystemService;\r\n readonly #apiClient: ApiClient;\r\n readonly #fileserverClientFactory: FileServerClientFactory;\r\n readonly #chunkStreamProviderFactory: ChunkStreamProviderFactory<string>;\r\n readonly #downloadRegistrationMode: DownloadRegistrationMode;\r\n readonly #eventsEngine: EventsEngine;\r\n readonly #cryptography: CryptographyProvider;\r\n readonly #ongoingChunkDownloads: Record<string, Promise<void>>;\r\n readonly #ongoingFileDownloads: Record<string, DownloadFilePromise>;\r\n readonly #encryptionData?: ChunkStreamProviderEncryptionData;\r\n\r\n constructor(\r\n logger: DownloadLogger,\r\n transferId: string,\r\n trackId: string | undefined,\r\n downloadId: string,\r\n region: string | undefined,\r\n files: DownloadFileInfo | Array<DownloadFileInfo>,\r\n oldState: DownloaderRestoredState | undefined,\r\n fileSystem: FileSystemService,\r\n concurrencyController: ConcurrencyController,\r\n eventsEngine: EventsEngine,\r\n cryptography: CryptographyProvider,\r\n options: DownloadOptions,\r\n apiClient: ApiClient,\r\n fileserverClientFactory: FileServerClientFactory,\r\n chunkStreamProviderFactory: ChunkStreamProviderFactory<string>,\r\n downloadRegistrationMode: DownloadRegistrationMode,\r\n e2eeData?: DownloadCommandE2eeData,\r\n ) {\r\n if (!transferId)\r\n throw new DownloadError(`No transfer specified`);\r\n\r\n if (!files)\r\n throw new DownloadError(`No files added to downloader`);\r\n\r\n if (Array.isArray(files) && files.length === 0)\r\n throw new DownloadError(`No files added to downloader`);\r\n\r\n if (!options.downloadFolder)\r\n throw new DownloadError(`No downloadFolder specified`);\r\n\r\n if (e2eeData && e2eeData.iv.length !== 16)\r\n throw new DownloadError(`IV must be 16 bytes long`);\r\n\r\n if (e2eeData && e2eeData.key.length !== 32)\r\n throw new DownloadError(`Encryption key must be 32 bytes long`);\r\n\r\n this.#logger = logger;\r\n this.#downloadId = downloadId;\r\n this.#transferId = transferId;\r\n this.#trackId = trackId;\r\n this.#region = region;\r\n this.#fileSystem = fileSystem;\r\n this.#files = Array.isArray(files) ? files : [files];\r\n this.#options = options;\r\n this.#eventsEngine = eventsEngine;\r\n this.#cryptography = cryptography;\r\n this.#downloadState = new DownloadState(this.#downloadId, this.#transferId, this.#files, this.#options, eventsEngine, oldState);\r\n this.#concurrencyController = concurrencyController;\r\n this.#apiClient = apiClient;\r\n this.#fileserverClientFactory = fileserverClientFactory;\r\n this.#chunkStreamProviderFactory = chunkStreamProviderFactory;\r\n this.#downloadRegistrationMode = downloadRegistrationMode;\r\n this.#ongoingChunkDownloads = {};\r\n this.#ongoingFileDownloads = {};\r\n\r\n if (e2eeData)\r\n this.#encryptionData = this.createEncryptionData(e2eeData);\r\n }\r\n\r\n private createEncryptionData(e2eeData: DownloadCommandE2eeData): ChunkStreamProviderEncryptionData {\r\n return {\r\n iv: e2eeData.iv,\r\n key: e2eeData.key,\r\n };\r\n }\r\n\r\n async start(): Promise<DownloadEventArgs> {\r\n try {\r\n this.#addEventListeners();\r\n\r\n let isDownloaderRunning = true;\r\n\r\n while (isDownloaderRunning) {\r\n this.#downloadState.start();\r\n\r\n const fileServerUrl = new URL(this.#files[0].url).origin;\r\n this.#downloadState.notifyMetadataDetermined(fileServerUrl, this.#region);\r\n\r\n // MAIN FILE LOOP\r\n for (const file of this.#files) {\r\n if (file.isCompleted || file.isFailed)\r\n continue;\r\n\r\n if (this.#downloadState.isRunning === false)\r\n break;\r\n\r\n // figure out if we are downloading a new file or resuming a file that was interrupted\r\n if (file.wasEverWritten) {\r\n // this a resumed file (recreated from progress.json) - it must already exist under its temporary path\r\n if (!file.temporaryDownloadPath) {\r\n this.#downloadState.fileFailed(file, new DownloadError(`File ${file.downloadPath} was supposed to be resumed but its temporary path is not known.`, undefined, DownloadErrorCode.ResumedFilePathNotSpecified));\r\n continue;\r\n }\r\n\r\n if ((await this.#fileSystem.pathExists(file.temporaryDownloadPath!)) === false) {\r\n this.#downloadState.fileFailed(file, new DownloadError(`File ${file.downloadPath} was supposed to be resumed at ${file.temporaryDownloadPath}, but it does not exist on disk anymore.`, undefined, DownloadErrorCode.ResumedFileNotFound));\r\n continue;\r\n }\r\n }\r\n else {\r\n // in some edge cases the temporary file may exist and its name be persisted in the tracker file\r\n // but the wasEverWritten flag was not yet set - just abandon such temporary file (it will be 'empty' - null bytes only)\r\n if (file.temporaryDownloadPath) {\r\n this.#logDeletingFile(file, file.temporaryDownloadPath, `start`);\r\n this.#fileSystem.deleteFile(file.temporaryDownloadPath);\r\n }\r\n\r\n // this is a new file - create it\r\n await this.#createDownloadFile(file);\r\n }\r\n\r\n this.#ongoingFileDownloads[file.externalId] = new DownloadFilePromise(file.externalId);\r\n this.#ongoingFileDownloads[file.externalId]!.awaiter.then(id => {\r\n delete this.#ongoingFileDownloads[id];\r\n });\r\n\r\n this.#downloadState.fileStarting(file);\r\n\r\n // note: this call does not really wait for all chunks to be downloaded, but until all chunks of a file have been **scheduled**\r\n await this.#downloadAllChunksForFile(file);\r\n\r\n // so when we are here, the file is not complete\r\n // to track completeness of a file we look at events emitted by this.#downloadState\r\n }\r\n\r\n // when we got here it's either pause request, stop request (which is just like pause but without waiting for pending chunks), abort request (cancel the download entirely) or everything is complete;\r\n\r\n // wait for any remaining pending chunks\r\n await promiseAllSettled(Object.values(this.#ongoingChunkDownloads));\r\n\r\n // Wait for any remaining pending files\r\n // Note: we cannot rely just on chunk downloads being completed, because downloading chunks\r\n // doesn't mean that a file is complete; last phase of file completion is renaming the file\r\n // from a temporary name (unconfirmed_somefile.txt_qweasd1234.fmdownload) to its real name, which may need adjustments if a file with same name already exists.\r\n // When file already exists - a unique filename will be generated automatically e.g.: file.txt --> file (1).txt\r\n await promiseAllSettled(Object.values(this.#ongoingFileDownloads).map(x => x.awaiter));\r\n\r\n if (this.#downloadState.status === TransferStatus.Pausing) {\r\n // set status to paused and await until resume/abort\r\n await this.#downloadState.paused();\r\n }\r\n\r\n isDownloaderRunning = await this.#prepareDownloaderForAnotherIteration();\r\n }\r\n\r\n await this.#cleanupEmptyDownloadFolderIfConfigured();\r\n }\r\n catch (error) {\r\n this.#logger.logError(error as Error, `Unhandled downloader error occured.`);\r\n this.#downloadState.completed(error as Error);\r\n }\r\n finally {\r\n this.#removeEventListeners();\r\n }\r\n\r\n return this.#downloadState.getCurrentState(true);\r\n }\r\n\r\n #logDeletingFile(file: DownloadFileInfo | DownloadFileProgress, path: string, method: string) {\r\n let fileDataToLog = file;\r\n if (file instanceof DownloadFileInfo)\r\n fileDataToLog = DownloadFileProgress.FromFileDownloadInfo(file);\r\n\r\n this.#logger.logInfo(`[DELETE_TEMPFILE] Deleting temp file path=${path} method=${method}\\n\\n${JSON.stringify(fileDataToLog, null, 4)}\\n`);\r\n }\r\n\r\n requestPause(options: { interruptPendingChunks: boolean, pausedTrigger?: (paused: boolean) => void }): void {\r\n Object.values(this.#ongoingFileDownloads).forEach(filePromise => filePromise.complete());\r\n this.#downloadState.requestPause(options);\r\n }\r\n\r\n resume(): boolean {\r\n return this.#downloadState.resume();\r\n }\r\n\r\n requestAbort(): boolean {\r\n Object.values(this.#ongoingFileDownloads).forEach(filePromise => filePromise.complete());\r\n return this.#downloadState.requestAbort();\r\n }\r\n\r\n getCurrentState(withFiles: boolean): DownloadEventArgs {\r\n return this.#downloadState.getCurrentState(withFiles);\r\n }\r\n\r\n get status(): TransferStatus {\r\n return this.#downloadState.status;\r\n }\r\n\r\n async #deleteAllTemporaryAndCompletedFiles() {\r\n for (const file of this.#files) {\r\n if (file.temporaryDownloadPath) {\r\n this.#logDeletingFile(file, file.temporaryDownloadPath, `deleteAllTemporaryAndCompletedFiles`);\r\n await this.#fileSystem.deleteFile(file.temporaryDownloadPath);\r\n }\r\n\r\n if (file.isCompleted)\r\n await this.#fileSystem.deleteFile(file.downloadPath);\r\n }\r\n }\r\n\r\n async #registerTransferDownload() {\r\n this.#downloadState.registeringTransferDownload();\r\n await this.#apiClient.registerTransferDownload({\r\n transferid: this.#transferId,\r\n track: this.#trackId,\r\n bps: this.#downloadState.averageBytesPerSecond,\r\n browser: this.#options.userAgent,\r\n });\r\n }\r\n\r\n async #registerFileDownload(filename: string, filesize: number) {\r\n this.#downloadState.registeringFileDownload();\r\n await this.#apiClient.registerFileDownload({\r\n transferid: this.#transferId,\r\n track: this.#trackId,\r\n bps: this.#downloadState.averageBytesPerSecond,\r\n browser: this.#options.userAgent,\r\n filename,\r\n filesize,\r\n });\r\n }\r\n\r\n #addEventListeners() {\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadCompleted, this.#onFileCompleted);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadCompleting, this.#onFileCompleting);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.PossibleSupersizedFile, this.#onFileSupersized);\r\n }\r\n\r\n #removeEventListeners() {\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadCompleted, this.#onFileCompleted);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadCompleting, this.#onFileCompleting);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.PossibleSupersizedFile, this.#onFileSupersized);\r\n }\r\n\r\n #onFileCompleted = async (args: FileDownloadEventArgs) => {\r\n if (this.#downloadRegistrationMode === DownloadRegistrationMode.EverySingleFile && !this.#options.skipRegistration)\r\n await this.#registerFileDownload(args.file.name, args.file.size);\r\n\r\n this.#ongoingFileDownloads[args.file.id]?.complete();\r\n };\r\n\r\n #onFileCompleting = async (args: FileDownloadEventArgs) => {\r\n // note the args.file.downloadPath may not be final - if a file with this name eixsts the following method will generate new name e.g. \"file.txt\" --> \"file (1).txt\"\r\n try {\r\n const finalDownloadPath = await this.#doTheFinalFileRenameAfterFilesIsDownloaded(args.file);\r\n this.#downloadState.fileCompleted(args.file.id, finalDownloadPath);\r\n }\r\n catch (error) {\r\n this.#downloadState.fileFailedById(args.file.id, error as DownloadError);\r\n }\r\n };\r\n\r\n #onFileSupersized = async (fileArgs: FileDownloadEventArgs) => {\r\n if (fileArgs.file.temporaryDownloadPath && await this.#fileSystem.getFileSize(fileArgs.file.temporaryDownloadPath) > fileArgs.file.size)\r\n await this.#fileSystem.truncateFile(fileArgs.file.temporaryDownloadPath, fileArgs.file.size);\r\n };\r\n\r\n #onFileFailed = async (args: FileDownloadFailedEventArgs) => {\r\n this.#ongoingFileDownloads[args.file.id]?.complete();\r\n\r\n if (this.#options.deleteFailedFilesFromDisk) {\r\n if (args.file.downloadPath && args.file.downloaded > 0)\r\n await this.#fileSystem.deleteFile(args.file.downloadPath);\r\n\r\n if (args.file.temporaryDownloadPath) {\r\n this.#logDeletingFile(args.file, args.file.temporaryDownloadPath, `onFileFailed`);\r\n await this.#fileSystem.deleteFile(args.file.temporaryDownloadPath);\r\n }\r\n }\r\n };\r\n\r\n async #createDownloadFile(file: DownloadFileInfo) {\r\n this.#downloadState.fileCreating(file);\r\n await this.#autoGenerateUniqueDownloadFile(file);\r\n this.#downloadState.fileCreated(file);\r\n }\r\n\r\n async #autoGenerateUniqueDownloadFile(file: DownloadFileInfo) {\r\n let counter = 0;\r\n const fileLeafName = this.#fileSystem.parseFilePath(file.name).base;\r\n do {\r\n const newFileName = `unconfirmed_${fileLeafName}_${this.#cryptography.generateRandomString(10)}.fmdownload`;\r\n const newPath = this.#fileSystem.replaceFilenameInPath(file.downloadPath, newFileName);\r\n if (await this.#fileSystem.createFile(newPath, false, file.sizeInBytes)) {\r\n // eslint-disable-next-line no-param-reassign\r\n file.temporaryDownloadPath = newPath;\r\n return;\r\n }\r\n\r\n counter += 1;\r\n } while (counter < 10000);\r\n\r\n throw new DownloadError(`Downloader could not generate unique download path for ${file.downloadPath}. It tried until counter=${counter}`, undefined, DownloadErrorCode.TooManyFileRenameAttempts);\r\n }\r\n\r\n async #downloadAllChunksForFile(file: DownloadFileInfo) {\r\n if (file.chunks.length) {\r\n // MAIN CHUNK LOOP\r\n for (const chunk of file.chunks) {\r\n if (chunk.isCompleted || chunk.isFailed)\r\n continue;\r\n\r\n const downloadScheduled = await this.#downloadSingleChunk(file, chunk);\r\n if (downloadScheduled === false) {\r\n // downloader is not running or file is in failed state - stop the chunk loop\r\n break;\r\n }\r\n }\r\n }\r\n else {\r\n // zerobyte file\r\n file.updateProgress();\r\n this.#downloadState.fileCompleting(file);\r\n }\r\n }\r\n\r\n async #downloadSingleChunk(file: DownloadFileInfo, chunk: DownloadFileChunkInfo): Promise<boolean> {\r\n const releaseSemaphore = await this.#concurrencyController.acquire();\r\n\r\n if (this.#downloadState.isRunning === false) {\r\n // downloader is not running - stop trying more chunks\r\n releaseSemaphore();\r\n return false;\r\n }\r\n\r\n if (file.isFailed) {\r\n // do not try to download more chunks if the file has failed\r\n releaseSemaphore();\r\n return false;\r\n }\r\n\r\n const chunkDownloader = new ChunkDownloader(this.#transferId, file, chunk, this.#downloadState, this.#options, this.#fileserverClientFactory, this.#chunkStreamProviderFactory, this.#eventsEngine, this.#logger, this.#encryptionData);\r\n const chunkPromise: Promise<void> = chunkDownloader.download();\r\n this.#ongoingChunkDownloads[chunk.id] = chunkPromise;\r\n\r\n chunkPromise.finally(() => {\r\n releaseSemaphore();\r\n delete this.#ongoingChunkDownloads[chunk.id];\r\n });\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Prepares downloader for next loop iteration. Handles two main scenarios:\r\n * 1. Aborting - cleans up the disk state, calls downloadState to trigger Aborted event and returns a false flag (to break the loop).\r\n * 2. Completed - registers download to web api, calls downloadState to trigger Completed event and returns a false flag (to break the loop).\r\n * 3. For all other cases returns true - to continue the loop.\r\n */\r\n async #prepareDownloaderForAnotherIteration(): Promise<boolean> {\r\n let shouldDownloaderContinue = true;\r\n if (this.#downloadState.status === TransferStatus.Aborting) {\r\n shouldDownloaderContinue = false;\r\n if (this.#options.deleteAbortedDownloadsFromDisk)\r\n await this.#deleteAllTemporaryAndCompletedFiles();\r\n\r\n this.#downloadState.aborted();\r\n }\r\n else if (this.#downloadState.allFilesCompletedOrFailed) {\r\n shouldDownloaderContinue = false;\r\n if (this.#downloadRegistrationMode === DownloadRegistrationMode.Transfer && !this.#options.skipRegistration)\r\n await this.#registerTransferDownload();\r\n\r\n this.#downloadState.completed();\r\n }\r\n return shouldDownloaderContinue;\r\n }\r\n\r\n async #cleanupEmptyDownloadFolderIfConfigured() {\r\n try {\r\n if (this.#options.deleteEmptyDownloadFoldersAfterDownload && await this.#fileSystem.folderIsEmpty(this.#options.downloadFolder!))\r\n await this.#fileSystem.deleteFolder(this.#options.downloadFolder!);\r\n }\r\n catch (error) {\r\n this.#logger.logWarning(`cleanupEmptyDownloadFolderIfConfigured failed`, { error });\r\n }\r\n }\r\n\r\n async #doTheFinalFileRenameAfterFilesIsDownloaded(file: DownloadFileProgress) {\r\n let created = false;\r\n let createCounter = 0;\r\n const original = this.#fileSystem.parseFilePath(file.downloadPath);\r\n let newPathFormatted = file.downloadPath;\r\n do {\r\n // we do not want to re-reserve disk space when creating the target file (final name) - so file size is omitted\r\n created = await this.#fileSystem.createFile(newPathFormatted, false);\r\n if (!created) {\r\n createCounter += 1;\r\n const newPath = {\r\n dir: original.dir,\r\n name: `${original.name} (${createCounter})`,\r\n ext: original.ext,\r\n };\r\n newPathFormatted = this.#fileSystem.formatFilePath(newPath);\r\n }\r\n } while (created === false && createCounter < 10000);\r\n\r\n if (!created)\r\n throw new DownloadError(`Downloader could not rename temp file ${file.temporaryDownloadPath} to ${file.downloadPath}. It tried until counter=${createCounter}`, undefined, DownloadErrorCode.TooManyFileRenameAttempts);\r\n\r\n let renamed = false;\r\n let renameCounter = 0;\r\n let lastError: Error | undefined;\r\n while (!renamed && renameCounter < 5) {\r\n renameCounter += 1;\r\n try {\r\n await this.#fileSystem.renameFile(file.temporaryDownloadPath!, newPathFormatted);\r\n renamed = true;\r\n this.#logger.logInfo(`Downloader renamed ${file.temporaryDownloadPath} to ${newPathFormatted} at attempt ${renameCounter}`);\r\n }\r\n catch (error) {\r\n lastError = error as Error;\r\n this.#logger.logError(lastError, `Downloader could not rename ${file.temporaryDownloadPath} to ${newPathFormatted} attempt ${renameCounter}`);\r\n }\r\n }\r\n if (!renamed)\r\n throw new DownloadError(`Downloader could not rename ${file.temporaryDownloadPath} to ${newPathFormatted} after ${renameCounter} attempts.`, { cause: lastError }, DownloadErrorCode.RenameFromTemporaryFilenameToFinalFailure);\r\n else\r\n return newPathFormatted;\r\n }\r\n}"]}
|
|
1
|
+
{"version":3,"file":"filesDownloader.js","sourceRoot":"","sources":["../../../src/downloader/filesDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-await-in-loop */\r\nimport DownloadState from \"./downloadStatus/downloadState.js\";\r\nimport DownloadFilePromise from \"./downloadStatus/downloadFilePromise.js\";\r\nimport DownloadFileInfo from \"./dtos/downloadFileInfo.js\";\r\nimport DownloadFileProgress from \"./downloadStatus/downloadFileProgress.js\";\r\nimport DownloadError from \"./dtos/downloadError.js\";\r\nimport TransferDownloadEvent from \"./dtos/downloadEvent.js\";\r\nimport ChunkDownloader from \"./chunkDownloader.js\";\r\nimport TransferStatus from \"../utils/types/transferStatus.js\";\r\nimport DownloadErrorCode from \"./dtos/downloadErrorCode.js\";\r\nimport DownloadRegistrationMode from \"./dtos/downloadRegistrationMode.js\";\r\nimport { promiseAllSettled } from \"../utils/promiseAll.js\";\r\n\r\nimport type FileSystemService from \"../utils/fileSystem/fileSystemService.js\";\r\nimport type DownloadOptions from \"./downloadOptions.js\";\r\nimport type ConcurrencyController from \"../utils/concurrencyController/concurrencyController.js\";\r\nimport type DownloaderRestoredState from \"./dtos/downloaderRestoredState.js\";\r\nimport type FileServerClientFactory from \"../utils/api/fileServerClientFactory.js\";\r\nimport type ChunkStreamProviderFactory from \"../utils/fileSystem/chunkStreamProviderFactory.js\";\r\nimport type EventsEngine from \"../utils/events/eventsEngine.js\";\r\nimport type DownloadEventArgs from \"./eventArgs/downloadEventArgs.js\";\r\nimport type ApiClient from \"../utils/api/apiClient.js\";\r\nimport type FileDownloadEventArgs from \"./eventArgs/fileDownloadEventArgs.js\";\r\nimport type FileDownloadFailedEventArgs from \"./eventArgs/fileDownloadFailedEventArgs.js\";\r\nimport type DownloadFileChunkInfo from \"./dtos/downloadFileChunkInfo.js\";\r\nimport type DownloadLogger from \"./downloadLogger.js\";\r\nimport type { ChunkStreamProviderEncryptionData } from \"../utils/fileSystem/chunkStreamProvider.js\";\r\nimport type { DownloadCommandE2eeData } from \"./dtos/downloadCommand.js\";\r\nimport type CryptographyProvider from \"../utils/crypto/cryptographyProvider.js\";\r\n\r\nexport default class FilesDownloader {\r\n readonly #logger: DownloadLogger;\r\n readonly #transferId: string;\r\n readonly #trackId: string | undefined;\r\n readonly #downloadId: string;\r\n readonly #region?: string;\r\n readonly #files: Array<DownloadFileInfo>;\r\n readonly #downloadState: DownloadState;\r\n readonly #options: DownloadOptions;\r\n readonly #concurrencyController: ConcurrencyController;\r\n readonly #fileSystem: FileSystemService;\r\n readonly #apiClient: ApiClient;\r\n readonly #fileserverClientFactory: FileServerClientFactory;\r\n readonly #chunkStreamProviderFactory: ChunkStreamProviderFactory<string>;\r\n readonly #downloadRegistrationMode: DownloadRegistrationMode;\r\n readonly #eventsEngine: EventsEngine;\r\n readonly #cryptography: CryptographyProvider;\r\n readonly #ongoingChunkDownloads: Record<string, Promise<void>>;\r\n readonly #ongoingFileDownloads: Record<string, DownloadFilePromise>;\r\n readonly #encryptionData?: ChunkStreamProviderEncryptionData;\r\n readonly #totalTransferSizeInBytes: number;\r\n\r\n #shouldPreallocateFiles = true;\r\n #preallocationStrategyInitialized = false;\r\n\r\n constructor(\r\n logger: DownloadLogger,\r\n transferId: string,\r\n trackId: string | undefined,\r\n downloadId: string,\r\n region: string | undefined,\r\n files: DownloadFileInfo | Array<DownloadFileInfo>,\r\n oldState: DownloaderRestoredState | undefined,\r\n fileSystem: FileSystemService,\r\n concurrencyController: ConcurrencyController,\r\n eventsEngine: EventsEngine,\r\n cryptography: CryptographyProvider,\r\n options: DownloadOptions,\r\n apiClient: ApiClient,\r\n fileserverClientFactory: FileServerClientFactory,\r\n chunkStreamProviderFactory: ChunkStreamProviderFactory<string>,\r\n downloadRegistrationMode: DownloadRegistrationMode,\r\n e2eeData?: DownloadCommandE2eeData,\r\n ) {\r\n if (!transferId)\r\n throw new DownloadError(`No transfer specified`);\r\n\r\n if (!files)\r\n throw new DownloadError(`No files added to downloader`);\r\n\r\n if (Array.isArray(files) && files.length === 0)\r\n throw new DownloadError(`No files added to downloader`);\r\n\r\n if (!options.downloadFolder)\r\n throw new DownloadError(`No downloadFolder specified`);\r\n\r\n if (e2eeData && e2eeData.iv.length !== 16)\r\n throw new DownloadError(`IV must be 16 bytes long`);\r\n\r\n if (e2eeData && e2eeData.key.length !== 32)\r\n throw new DownloadError(`Encryption key must be 32 bytes long`);\r\n\r\n this.#logger = logger;\r\n this.#downloadId = downloadId;\r\n this.#transferId = transferId;\r\n this.#trackId = trackId;\r\n this.#region = region;\r\n this.#fileSystem = fileSystem;\r\n this.#files = Array.isArray(files) ? files : [files];\r\n this.#options = options;\r\n this.#eventsEngine = eventsEngine;\r\n this.#cryptography = cryptography;\r\n this.#downloadState = new DownloadState(this.#downloadId, this.#transferId, this.#files, this.#options, eventsEngine, oldState);\r\n this.#concurrencyController = concurrencyController;\r\n this.#apiClient = apiClient;\r\n this.#fileserverClientFactory = fileserverClientFactory;\r\n this.#chunkStreamProviderFactory = chunkStreamProviderFactory;\r\n this.#downloadRegistrationMode = downloadRegistrationMode;\r\n this.#ongoingChunkDownloads = {};\r\n this.#ongoingFileDownloads = {};\r\n this.#totalTransferSizeInBytes = this.#files.reduce((sum, file) => sum + file.sizeInBytes, 0);\r\n\r\n if (e2eeData)\r\n this.#encryptionData = this.createEncryptionData(e2eeData);\r\n }\r\n\r\n private createEncryptionData(e2eeData: DownloadCommandE2eeData): ChunkStreamProviderEncryptionData {\r\n return {\r\n iv: e2eeData.iv,\r\n key: e2eeData.key,\r\n };\r\n }\r\n\r\n async start(): Promise<DownloadEventArgs> {\r\n try {\r\n this.#addEventListeners();\r\n\r\n let isDownloaderRunning = true;\r\n\r\n while (isDownloaderRunning) {\r\n this.#downloadState.start();\r\n\r\n const fileServerUrl = new URL(this.#files[0].url).origin;\r\n this.#downloadState.notifyMetadataDetermined(fileServerUrl, this.#region);\r\n\r\n // MAIN FILE LOOP\r\n for (const file of this.#files) {\r\n if (file.isCompleted || file.isFailed)\r\n continue;\r\n\r\n if (this.#downloadState.isRunning === false)\r\n break;\r\n\r\n // figure out if we are downloading a new file or resuming a file that was interrupted\r\n if (file.wasEverWritten) {\r\n // this a resumed file (recreated from progress.json) - it must already exist under its temporary path\r\n if (!file.temporaryDownloadPath) {\r\n this.#downloadState.fileFailed(file, new DownloadError(`File ${file.downloadPath} was supposed to be resumed but its temporary path is not known.`, undefined, DownloadErrorCode.ResumedFilePathNotSpecified));\r\n continue;\r\n }\r\n\r\n if ((await this.#fileSystem.pathExists(file.temporaryDownloadPath!)) === false) {\r\n this.#downloadState.fileFailed(file, new DownloadError(`File ${file.downloadPath} was supposed to be resumed at ${file.temporaryDownloadPath}, but it does not exist on disk anymore.`, undefined, DownloadErrorCode.ResumedFileNotFound));\r\n continue;\r\n }\r\n }\r\n else {\r\n // in some edge cases the temporary file may exist and its name be persisted in the tracker file\r\n // but the wasEverWritten flag was not yet set - just abandon such temporary file (it will be 'empty' - null bytes only)\r\n if (file.temporaryDownloadPath) {\r\n this.#logDeletingFile(file, file.temporaryDownloadPath, `start`);\r\n this.#fileSystem.deleteFile(file.temporaryDownloadPath);\r\n }\r\n\r\n // this is a new file - create it\r\n await this.#createDownloadFile(file);\r\n }\r\n\r\n this.#ongoingFileDownloads[file.externalId] = new DownloadFilePromise(file.externalId);\r\n this.#ongoingFileDownloads[file.externalId]!.awaiter.then(id => {\r\n delete this.#ongoingFileDownloads[id];\r\n });\r\n\r\n this.#downloadState.fileStarting(file);\r\n\r\n // note: this call does not really wait for all chunks to be downloaded, but until all chunks of a file have been **scheduled**\r\n await this.#downloadAllChunksForFile(file);\r\n\r\n // so when we are here, the file is not complete\r\n // to track completeness of a file we look at events emitted by this.#downloadState\r\n }\r\n\r\n // when we got here it's either pause request, stop request (which is just like pause but without waiting for pending chunks), abort request (cancel the download entirely) or everything is complete;\r\n\r\n // wait for any remaining pending chunks\r\n await promiseAllSettled(Object.values(this.#ongoingChunkDownloads));\r\n\r\n // Wait for any remaining pending files\r\n // Note: we cannot rely just on chunk downloads being completed, because downloading chunks\r\n // doesn't mean that a file is complete; last phase of file completion is renaming the file\r\n // from a temporary name (unconfirmed_somefile.txt_qweasd1234.fmdownload) to its real name, which may need adjustments if a file with same name already exists.\r\n // When file already exists - a unique filename will be generated automatically e.g.: file.txt --> file (1).txt\r\n await promiseAllSettled(Object.values(this.#ongoingFileDownloads).map(x => x.awaiter));\r\n\r\n if (this.#downloadState.status === TransferStatus.Pausing) {\r\n // set status to paused and await until resume/abort\r\n await this.#downloadState.paused();\r\n }\r\n\r\n isDownloaderRunning = await this.#prepareDownloaderForAnotherIteration();\r\n }\r\n\r\n await this.#cleanupEmptyDownloadFolderIfConfigured();\r\n }\r\n catch (error) {\r\n this.#logger.logError(error as Error, `Unhandled downloader error occured.`);\r\n this.#downloadState.completed(error as Error);\r\n }\r\n finally {\r\n this.#removeEventListeners();\r\n }\r\n\r\n return this.#downloadState.getCurrentState(true);\r\n }\r\n\r\n #logDeletingFile(file: DownloadFileInfo | DownloadFileProgress, path: string, method: string) {\r\n let fileDataToLog = file;\r\n if (file instanceof DownloadFileInfo)\r\n fileDataToLog = DownloadFileProgress.FromFileDownloadInfo(file);\r\n\r\n this.#logger.logInfo(`[DELETE_TEMPFILE] Deleting temp file path=${path} method=${method}\\n\\n${JSON.stringify(fileDataToLog, null, 4)}\\n`);\r\n }\r\n\r\n requestPause(options: { interruptPendingChunks: boolean, pausedTrigger?: (paused: boolean) => void }): void {\r\n Object.values(this.#ongoingFileDownloads).forEach(filePromise => filePromise.complete());\r\n this.#downloadState.requestPause(options);\r\n }\r\n\r\n resume(): boolean {\r\n return this.#downloadState.resume();\r\n }\r\n\r\n requestAbort(): boolean {\r\n Object.values(this.#ongoingFileDownloads).forEach(filePromise => filePromise.complete());\r\n return this.#downloadState.requestAbort();\r\n }\r\n\r\n getCurrentState(withFiles: boolean): DownloadEventArgs {\r\n return this.#downloadState.getCurrentState(withFiles);\r\n }\r\n\r\n get status(): TransferStatus {\r\n return this.#downloadState.status;\r\n }\r\n\r\n async #deleteAllTemporaryAndCompletedFiles() {\r\n for (const file of this.#files) {\r\n if (file.temporaryDownloadPath) {\r\n this.#logDeletingFile(file, file.temporaryDownloadPath, `deleteAllTemporaryAndCompletedFiles`);\r\n await this.#fileSystem.deleteFile(file.temporaryDownloadPath);\r\n }\r\n\r\n if (file.isCompleted)\r\n await this.#fileSystem.deleteFile(file.downloadPath);\r\n }\r\n }\r\n\r\n async #registerTransferDownload() {\r\n this.#downloadState.registeringTransferDownload();\r\n await this.#apiClient.registerTransferDownload({\r\n transferid: this.#transferId,\r\n track: this.#trackId,\r\n bps: this.#downloadState.averageBytesPerSecond,\r\n browser: this.#options.userAgent,\r\n });\r\n }\r\n\r\n async #registerFileDownload(filename: string, filesize: number) {\r\n this.#downloadState.registeringFileDownload();\r\n await this.#apiClient.registerFileDownload({\r\n transferid: this.#transferId,\r\n track: this.#trackId,\r\n bps: this.#downloadState.averageBytesPerSecond,\r\n browser: this.#options.userAgent,\r\n filename,\r\n filesize,\r\n });\r\n }\r\n\r\n #addEventListeners() {\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadCompleted, this.#onFileCompleted);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadCompleting, this.#onFileCompleting);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#eventsEngine.addEventListener(TransferDownloadEvent.PossibleSupersizedFile, this.#onFileSupersized);\r\n }\r\n\r\n #removeEventListeners() {\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadCompleted, this.#onFileCompleted);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadCompleting, this.#onFileCompleting);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.FileDownloadFailed, this.#onFileFailed);\r\n this.#eventsEngine.removeEventListener(TransferDownloadEvent.PossibleSupersizedFile, this.#onFileSupersized);\r\n }\r\n\r\n #onFileCompleted = async (args: FileDownloadEventArgs) => {\r\n if (this.#downloadRegistrationMode === DownloadRegistrationMode.EverySingleFile && !this.#options.skipRegistration)\r\n await this.#registerFileDownload(args.file.name, args.file.size);\r\n\r\n this.#ongoingFileDownloads[args.file.id]?.complete();\r\n };\r\n\r\n #onFileCompleting = async (args: FileDownloadEventArgs) => {\r\n // note the args.file.downloadPath may not be final - if a file with this name eixsts the following method will generate new name e.g. \"file.txt\" --> \"file (1).txt\"\r\n try {\r\n const finalDownloadPath = await this.#doTheFinalFileRenameAfterFilesIsDownloaded(args.file);\r\n this.#downloadState.fileCompleted(args.file.id, finalDownloadPath);\r\n }\r\n catch (error) {\r\n this.#downloadState.fileFailedById(args.file.id, error as DownloadError);\r\n }\r\n };\r\n\r\n #onFileSupersized = async (fileArgs: FileDownloadEventArgs) => {\r\n if (fileArgs.file.temporaryDownloadPath && await this.#fileSystem.getFileSize(fileArgs.file.temporaryDownloadPath) > fileArgs.file.size)\r\n await this.#fileSystem.truncateFile(fileArgs.file.temporaryDownloadPath, fileArgs.file.size);\r\n };\r\n\r\n #onFileFailed = async (args: FileDownloadFailedEventArgs) => {\r\n this.#ongoingFileDownloads[args.file.id]?.complete();\r\n\r\n if (this.#options.deleteFailedFilesFromDisk) {\r\n if (args.file.downloadPath && args.file.downloaded > 0)\r\n await this.#fileSystem.deleteFile(args.file.downloadPath);\r\n\r\n if (args.file.temporaryDownloadPath) {\r\n this.#logDeletingFile(args.file, args.file.temporaryDownloadPath, `onFileFailed`);\r\n await this.#fileSystem.deleteFile(args.file.temporaryDownloadPath);\r\n }\r\n }\r\n };\r\n\r\n async #createDownloadFile(file: DownloadFileInfo) {\r\n await this.#resolvePreallocationStrategy();\r\n\r\n this.#downloadState.fileCreating(file);\r\n\r\n try {\r\n await this.#autoGenerateUniqueDownloadFile(file);\r\n this.#downloadState.fileCreated(file);\r\n }\r\n catch (error) {\r\n throw this.#toDownloadError(error, `Creating download file failed for ${file.downloadPath}`);\r\n }\r\n }\r\n\r\n async #autoGenerateUniqueDownloadFile(file: DownloadFileInfo) {\r\n let counter = 0;\r\n const fileLeafName = this.#fileSystem.parseFilePath(file.name).base;\r\n do {\r\n const newFileName = `unconfirmed_${fileLeafName}_${this.#cryptography.generateRandomString(10)}.fmdownload`;\r\n const newPath = this.#fileSystem.replaceFilenameInPath(file.downloadPath, newFileName);\r\n const sizeToReserve = this.#shouldPreallocateFiles ? file.sizeInBytes : undefined;\r\n if (await this.#fileSystem.createFile(newPath, false, sizeToReserve)) {\r\n // eslint-disable-next-line no-param-reassign\r\n file.temporaryDownloadPath = newPath;\r\n return;\r\n }\r\n\r\n counter += 1;\r\n } while (counter < 10000);\r\n\r\n throw new DownloadError(`Downloader could not generate unique download path for ${file.downloadPath}. It tried until counter=${counter}`, undefined, DownloadErrorCode.TooManyFileRenameAttempts);\r\n }\r\n\r\n async #resolvePreallocationStrategy() {\r\n if (this.#preallocationStrategyInitialized)\r\n return;\r\n\r\n if (this.#totalTransferSizeInBytes <= this.#options.preallocateAlwaysUntilTransferSizeInBytes) {\r\n this.#shouldPreallocateFiles = true;\r\n this.#preallocationStrategyInitialized = true;\r\n this.#logger.logInfo(`[PREALLOC] Using full preallocation for transfer size ${this.#totalTransferSizeInBytes}B (threshold=${this.#options.preallocateAlwaysUntilTransferSizeInBytes}B)`);\r\n return;\r\n }\r\n\r\n try {\r\n const probeSize = Math.min(this.#options.preallocateProbeFileSizeInBytes, this.#totalTransferSizeInBytes);\r\n const maxProbeDurationInMs = this.#options.preallocateProbeMaxDurationInMs;\r\n\r\n const probeDurationInMs = await this.#probePreallocationDurationInMs(probeSize);\r\n\r\n if (probeDurationInMs <= maxProbeDurationInMs) {\r\n this.#shouldPreallocateFiles = true;\r\n this.#preallocationStrategyInitialized = true;\r\n this.#logger.logInfo(`[PREALLOC] Probe truncate ${probeSize}B finished in ${probeDurationInMs}ms (limit=${maxProbeDurationInMs}ms). Using full preallocation.`);\r\n return;\r\n }\r\n\r\n const requiredBytes = BigInt(this.#totalTransferSizeInBytes);\r\n const safetyMarginBytes = BigInt(this.#options.preallocateFreeSpaceSafetyMarginInBytes);\r\n\r\n await this.#fileSystem.ensureEnoughFreeSpace(this.#options.downloadFolder!, requiredBytes, safetyMarginBytes);\r\n\r\n this.#shouldPreallocateFiles = false;\r\n this.#preallocationStrategyInitialized = true;\r\n this.#logger.logInfo(`[PREALLOC] Probe truncate ${probeSize}B took ${probeDurationInMs}ms (limit=${maxProbeDurationInMs}ms). Skipping full preallocation and using free-space check only.`);\r\n }\r\n catch (error) {\r\n throw this.#toDownloadError(\r\n error,\r\n `Could not resolve preallocation strategy for download folder ${this.#options.downloadFolder!}`,\r\n );\r\n }\r\n }\r\n\r\n async #probePreallocationDurationInMs(probeSize: number): Promise<number> {\r\n const probeFilePath = this.#buildPreallocationProbePath();\r\n const startedAt = Date.now();\r\n try {\r\n const created = await this.#fileSystem.createFile(probeFilePath, false, probeSize);\r\n if (!created)\r\n throw new DownloadError(`Could not create preallocation probe file at ${probeFilePath}`);\r\n }\r\n finally {\r\n await this.#fileSystem.deleteFile(probeFilePath);\r\n }\r\n\r\n return Date.now() - startedAt;\r\n }\r\n\r\n #buildPreallocationProbePath(): string {\r\n const probeName = `fmdownload_prealloc_probe_${this.#cryptography.generateRandomString(8)}.tmp`;\r\n return this.#fileSystem.pathJoin(this.#options.downloadFolder!, probeName);\r\n }\r\n\r\n #toDownloadError(error: unknown, message: string): DownloadError {\r\n if (error instanceof DownloadError)\r\n return error;\r\n\r\n const wrappedError = error instanceof Error\r\n ? error\r\n : new Error(`${error}`);\r\n\r\n const code = this.#fileSystem.fileErrorToDownloadErrorCode(wrappedError);\r\n return new DownloadError(message, { cause: wrappedError }, code);\r\n }\r\n\r\n async #downloadAllChunksForFile(file: DownloadFileInfo) {\r\n if (file.chunks.length) {\r\n // MAIN CHUNK LOOP\r\n for (const chunk of file.chunks) {\r\n if (chunk.isCompleted || chunk.isFailed)\r\n continue;\r\n\r\n const downloadScheduled = await this.#downloadSingleChunk(file, chunk);\r\n if (downloadScheduled === false) {\r\n // downloader is not running or file is in failed state - stop the chunk loop\r\n break;\r\n }\r\n }\r\n }\r\n else {\r\n // zerobyte file\r\n file.updateProgress();\r\n this.#downloadState.fileCompleting(file);\r\n }\r\n }\r\n\r\n async #downloadSingleChunk(file: DownloadFileInfo, chunk: DownloadFileChunkInfo): Promise<boolean> {\r\n const releaseSemaphore = await this.#concurrencyController.acquire();\r\n\r\n if (this.#downloadState.isRunning === false) {\r\n // downloader is not running - stop trying more chunks\r\n releaseSemaphore();\r\n return false;\r\n }\r\n\r\n if (file.isFailed) {\r\n // do not try to download more chunks if the file has failed\r\n releaseSemaphore();\r\n return false;\r\n }\r\n\r\n const chunkDownloader = new ChunkDownloader(this.#transferId, file, chunk, this.#downloadState, this.#options, this.#fileserverClientFactory, this.#chunkStreamProviderFactory, this.#eventsEngine, this.#logger, this.#encryptionData);\r\n const chunkPromise: Promise<void> = chunkDownloader.download();\r\n this.#ongoingChunkDownloads[chunk.id] = chunkPromise;\r\n\r\n chunkPromise.finally(() => {\r\n releaseSemaphore();\r\n delete this.#ongoingChunkDownloads[chunk.id];\r\n });\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Prepares downloader for next loop iteration. Handles two main scenarios:\r\n * 1. Aborting - cleans up the disk state, calls downloadState to trigger Aborted event and returns a false flag (to break the loop).\r\n * 2. Completed - registers download to web api, calls downloadState to trigger Completed event and returns a false flag (to break the loop).\r\n * 3. For all other cases returns true - to continue the loop.\r\n */\r\n async #prepareDownloaderForAnotherIteration(): Promise<boolean> {\r\n let shouldDownloaderContinue = true;\r\n if (this.#downloadState.status === TransferStatus.Aborting) {\r\n shouldDownloaderContinue = false;\r\n if (this.#options.deleteAbortedDownloadsFromDisk)\r\n await this.#deleteAllTemporaryAndCompletedFiles();\r\n\r\n this.#downloadState.aborted();\r\n }\r\n else if (this.#downloadState.allFilesCompletedOrFailed) {\r\n shouldDownloaderContinue = false;\r\n if (this.#downloadRegistrationMode === DownloadRegistrationMode.Transfer && !this.#options.skipRegistration)\r\n await this.#registerTransferDownload();\r\n\r\n this.#downloadState.completed();\r\n }\r\n return shouldDownloaderContinue;\r\n }\r\n\r\n async #cleanupEmptyDownloadFolderIfConfigured() {\r\n try {\r\n if (this.#options.deleteEmptyDownloadFoldersAfterDownload && await this.#fileSystem.folderIsEmpty(this.#options.downloadFolder!))\r\n await this.#fileSystem.deleteFolder(this.#options.downloadFolder!);\r\n }\r\n catch (error) {\r\n this.#logger.logWarning(`cleanupEmptyDownloadFolderIfConfigured failed`, { error });\r\n }\r\n }\r\n\r\n async #doTheFinalFileRenameAfterFilesIsDownloaded(file: DownloadFileProgress) {\r\n let created = false;\r\n let createCounter = 0;\r\n const original = this.#fileSystem.parseFilePath(file.downloadPath);\r\n let newPathFormatted = file.downloadPath;\r\n do {\r\n // we do not want to re-reserve disk space when creating the target file (final name) - so file size is omitted\r\n created = await this.#fileSystem.createFile(newPathFormatted, false);\r\n if (!created) {\r\n createCounter += 1;\r\n const newPath = {\r\n dir: original.dir,\r\n name: `${original.name} (${createCounter})`,\r\n ext: original.ext,\r\n };\r\n newPathFormatted = this.#fileSystem.formatFilePath(newPath);\r\n }\r\n } while (created === false && createCounter < 10000);\r\n\r\n if (!created)\r\n throw new DownloadError(`Downloader could not rename temp file ${file.temporaryDownloadPath} to ${file.downloadPath}. It tried until counter=${createCounter}`, undefined, DownloadErrorCode.TooManyFileRenameAttempts);\r\n\r\n let renamed = false;\r\n let renameCounter = 0;\r\n let lastError: Error | undefined;\r\n while (!renamed && renameCounter < 5) {\r\n renameCounter += 1;\r\n try {\r\n await this.#fileSystem.renameFile(file.temporaryDownloadPath!, newPathFormatted);\r\n renamed = true;\r\n this.#logger.logInfo(`Downloader renamed ${file.temporaryDownloadPath} to ${newPathFormatted} at attempt ${renameCounter}`);\r\n }\r\n catch (error) {\r\n lastError = error as Error;\r\n this.#logger.logError(lastError, `Downloader could not rename ${file.temporaryDownloadPath} to ${newPathFormatted} attempt ${renameCounter}`);\r\n }\r\n }\r\n if (!renamed)\r\n throw new DownloadError(`Downloader could not rename ${file.temporaryDownloadPath} to ${newPathFormatted} after ${renameCounter} attempts.`, { cause: lastError }, DownloadErrorCode.RenameFromTemporaryFilenameToFinalFailure);\r\n else\r\n return newPathFormatted;\r\n }\r\n}\r\n"]}
|
|
@@ -21,6 +21,8 @@ interface FileSystemService {
|
|
|
21
21
|
getFileSize(filePath: string): Promise<number>;
|
|
22
22
|
truncateFile(filePath: string, newSize: number): Promise<void>;
|
|
23
23
|
renameFile(filePath: string, newPath: string): Promise<void>;
|
|
24
|
+
getAvailableDiskSpace(path: string): Promise<bigint>;
|
|
25
|
+
ensureEnoughFreeSpace(path: string, requiredBytes: bigint, safetyMarginBytes: bigint): Promise<void>;
|
|
24
26
|
parseFilePath(filePath: string): ParsedPath;
|
|
25
27
|
formatFilePath(parsedPath: ParsedPath): string;
|
|
26
28
|
replaceFilenameInPath(filePath: string, newFileName: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileSystemService.d.ts","sourceRoot":"","sources":["../../../../src/utils/fileSystem/fileSystemService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,iBAAiB,MAAM,4CAA4C,CAAC;AAChF,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAE9C,UAAU,iBAAiB;IACvB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;IACnG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACrE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpG,QAAQ,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC1C,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C;;;;;MAKE;IACF,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1F,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9E,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5C,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IAC/C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE,4BAA4B,CAAC,KAAK,EAAE,KAAK,GAAG,iBAAiB,GAAG,SAAS,CAAC;CAC7E;AAED,eAAe,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"fileSystemService.d.ts","sourceRoot":"","sources":["../../../../src/utils/fileSystem/fileSystemService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,iBAAiB,MAAM,4CAA4C,CAAC;AAChF,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAE9C,UAAU,iBAAiB;IACvB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;IACnG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACrE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpG,QAAQ,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC1C,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C;;;;;MAKE;IACF,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1F,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9E,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5C,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IAC/C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE,4BAA4B,CAAC,KAAK,EAAE,KAAK,GAAG,iBAAiB,GAAG,SAAS,CAAC;CAC7E;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileSystemService.js","sourceRoot":"","sources":["../../../../src/utils/fileSystem/fileSystemService.ts"],"names":[],"mappings":"","sourcesContent":["import type DownloadErrorCode from \"../../downloader/dtos/downloadErrorCode.js\";\r\nimport type ParsedPath from \"./parsedPath.js\";\r\n\r\ninterface FileSystemService {\r\n writeFile(filePath: string, data: string, encoding?: BufferEncoding, abort?: AbortSignal): unknown;\r\n createFolder(folderPath: string): Promise<void>;\r\n getAllFilePaths(folderPath: string): Promise<Array<string>>;\r\n getAllFilePathsRecursive(folderPath: string): Promise<Array<string>>;\r\n readFileAsString(filePath: string, encoding?: BufferEncoding, abort?: AbortSignal): Promise<string>;\r\n pathJoin(...paths: Array<string>): string;\r\n deleteFolder(folderPath: string): Promise<boolean>;\r\n folderIsEmpty(folderPath: string): Promise<boolean>;\r\n deleteFile(filePath: string): Promise<boolean>;\r\n /**\r\n * Creates the file specified by **filePath**. Returns a bool flag whether the file was physically created or overwritten.\r\n * @param filePath - full path of the file to create\r\n * @param overwriteExisting - when **true** this method will create or overwrite the file (if exists) and return true. If **false** this method will create the file only if it does not exist and in such case it will return true. It will return false if the file already exists.\r\n * @param size - when this value is specified and it is > 0 this method will expand the file to the requested size (using 0-code bytes) after creating it.\r\n */\r\n createFile(filePath: string, overwriteExisting: boolean, size?: number): Promise<boolean>;\r\n pathExists(thePath: string, checkReadWriteAccess?: boolean): Promise<boolean>;\r\n getFileSize(filePath: string): Promise<number>;\r\n truncateFile(filePath: string, newSize: number): Promise<void>;\r\n renameFile(filePath: string, newPath: string): Promise<void>;\r\n parseFilePath(filePath: string): ParsedPath;\r\n formatFilePath(parsedPath: ParsedPath): string;\r\n replaceFilenameInPath(filePath: string, newFileName: string): string;\r\n\r\n fileErrorToDownloadErrorCode(error: Error): DownloadErrorCode | undefined;\r\n}\r\n\r\nexport default FileSystemService
|
|
1
|
+
{"version":3,"file":"fileSystemService.js","sourceRoot":"","sources":["../../../../src/utils/fileSystem/fileSystemService.ts"],"names":[],"mappings":"","sourcesContent":["import type DownloadErrorCode from \"../../downloader/dtos/downloadErrorCode.js\";\r\nimport type ParsedPath from \"./parsedPath.js\";\r\n\r\ninterface FileSystemService {\r\n writeFile(filePath: string, data: string, encoding?: BufferEncoding, abort?: AbortSignal): unknown;\r\n createFolder(folderPath: string): Promise<void>;\r\n getAllFilePaths(folderPath: string): Promise<Array<string>>;\r\n getAllFilePathsRecursive(folderPath: string): Promise<Array<string>>;\r\n readFileAsString(filePath: string, encoding?: BufferEncoding, abort?: AbortSignal): Promise<string>;\r\n pathJoin(...paths: Array<string>): string;\r\n deleteFolder(folderPath: string): Promise<boolean>;\r\n folderIsEmpty(folderPath: string): Promise<boolean>;\r\n deleteFile(filePath: string): Promise<boolean>;\r\n /**\r\n * Creates the file specified by **filePath**. Returns a bool flag whether the file was physically created or overwritten.\r\n * @param filePath - full path of the file to create\r\n * @param overwriteExisting - when **true** this method will create or overwrite the file (if exists) and return true. If **false** this method will create the file only if it does not exist and in such case it will return true. It will return false if the file already exists.\r\n * @param size - when this value is specified and it is > 0 this method will expand the file to the requested size (using 0-code bytes) after creating it.\r\n */\r\n createFile(filePath: string, overwriteExisting: boolean, size?: number): Promise<boolean>;\r\n pathExists(thePath: string, checkReadWriteAccess?: boolean): Promise<boolean>;\r\n getFileSize(filePath: string): Promise<number>;\r\n truncateFile(filePath: string, newSize: number): Promise<void>;\r\n renameFile(filePath: string, newPath: string): Promise<void>;\r\n getAvailableDiskSpace(path: string): Promise<bigint>;\r\n ensureEnoughFreeSpace(path: string, requiredBytes: bigint, safetyMarginBytes: bigint): Promise<void>;\r\n parseFilePath(filePath: string): ParsedPath;\r\n formatFilePath(parsedPath: ParsedPath): string;\r\n replaceFilenameInPath(filePath: string, newFileName: string): string;\r\n\r\n fileErrorToDownloadErrorCode(error: Error): DownloadErrorCode | undefined;\r\n}\r\n\r\nexport default FileSystemService;\r\n"]}
|
|
@@ -17,6 +17,8 @@ export default class NodeFileSystemService implements FileSystemService {
|
|
|
17
17
|
getFileSize(filePath: string): Promise<number>;
|
|
18
18
|
truncateFile(filePath: string, size: number): Promise<void>;
|
|
19
19
|
renameFile(oldPath: string, newPath: string): Promise<void>;
|
|
20
|
+
getAvailableDiskSpace(pathToCheck: string): Promise<bigint>;
|
|
21
|
+
ensureEnoughFreeSpace(pathToCheck: string, requiredBytes: bigint, safetyMarginBytes: bigint): Promise<void>;
|
|
20
22
|
pathJoin(...paths: Array<string>): string;
|
|
21
23
|
fileErrorToDownloadErrorCode(error: Error): DownloadErrorCode | undefined;
|
|
22
24
|
parseFilePath(filePath: string): ParsedPath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeFileSystemService.d.ts","sourceRoot":"","sources":["../../../../../src/utils/fileSystem/node/nodeFileSystemService.ts"],"names":[],"mappings":"AAEA,OAAO,iBAAiB,MAAM,+CAA+C,CAAC;AAC9E,OAAO,KAAK,iBAAiB,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,iBAAiB;;IAoB7D,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAM/D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAuB,EAAE,KAAK,GAAE,WAAW,GAAG,SAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvI,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAI3D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAuB,EAAE,KAAK,GAAE,WAAW,GAAG,SAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAY7E,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAmBvC,kBAAkB;IAgC1B,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCzF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU9C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"nodeFileSystemService.d.ts","sourceRoot":"","sources":["../../../../../src/utils/fileSystem/node/nodeFileSystemService.ts"],"names":[],"mappings":"AAEA,OAAO,iBAAiB,MAAM,+CAA+C,CAAC;AAC9E,OAAO,KAAK,iBAAiB,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,iBAAiB;;IAoB7D,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAM/D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAuB,EAAE,KAAK,GAAE,WAAW,GAAG,SAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvI,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAI3D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAuB,EAAE,KAAK,GAAE,WAAW,GAAG,SAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAY7E,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAmBvC,kBAAkB;IAgC1B,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCzF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU9C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK3D,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjH,QAAQ,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM;IAIzC,4BAA4B,CAAC,KAAK,EAAE,KAAK,GAAG,iBAAiB,GAAG,SAAS;IAoBzE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAI3C,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM;IAI9C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;CAOvE"}
|
|
@@ -137,14 +137,35 @@ export default class NodeFileSystemService {
|
|
|
137
137
|
async renameFile(oldPath, newPath) {
|
|
138
138
|
await fs.rename(oldPath, newPath);
|
|
139
139
|
}
|
|
140
|
+
async getAvailableDiskSpace(pathToCheck) {
|
|
141
|
+
const stats = await fs.statfs(pathToCheck, { bigint: true });
|
|
142
|
+
return stats.bavail * stats.bsize;
|
|
143
|
+
}
|
|
144
|
+
async ensureEnoughFreeSpace(pathToCheck, requiredBytes, safetyMarginBytes) {
|
|
145
|
+
const availableBytes = await this.getAvailableDiskSpace(pathToCheck);
|
|
146
|
+
if (availableBytes < requiredBytes + safetyMarginBytes) {
|
|
147
|
+
const err = new Error(`Not enough disk space. Required=${requiredBytes}B, available=${availableBytes}B (margin=${safetyMarginBytes}B)`);
|
|
148
|
+
err.code = `ENOSPC`;
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
140
152
|
pathJoin(...paths) {
|
|
141
153
|
return path.join(...paths);
|
|
142
154
|
}
|
|
143
155
|
fileErrorToDownloadErrorCode(error) {
|
|
144
|
-
if (error
|
|
156
|
+
if (NodeFileSystemService.#extractErrorCode(error) === `ENOSPC`)
|
|
145
157
|
return DownloadErrorCode.DiskFull;
|
|
146
158
|
return undefined;
|
|
147
159
|
}
|
|
160
|
+
static #extractErrorCode(error) {
|
|
161
|
+
let current = error;
|
|
162
|
+
for (let depth = 0; depth < 8 && current; depth++) {
|
|
163
|
+
if (typeof current.code === `string`)
|
|
164
|
+
return current.code;
|
|
165
|
+
current = current.cause;
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
148
169
|
parseFilePath(filePath) {
|
|
149
170
|
return path.parse(filePath);
|
|
150
171
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeFileSystemService.js","sourceRoot":"","sources":["../../../../../src/utils/fileSystem/node/nodeFileSystemService.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs/promises';\r\nimport path from 'path';\r\nimport DownloadErrorCode from '../../../downloader/dtos/downloadErrorCode.js';\r\nimport type FileSystemService from \"../fileSystemService.js\";\r\nimport type ParsedPath from '../parsedPath.js';\r\n\r\nexport default class NodeFileSystemService implements FileSystemService {\r\n static async #readDirRecursive(folder: string, accumulator: Array<string>) {\r\n const paths = await fs.readdir(folder);\r\n for (const itemPath of paths) {\r\n const abs = path.join(folder, itemPath);\r\n // eslint-disable-next-line no-await-in-loop\r\n if ((await fs.stat(abs)).isDirectory())\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.#readDirRecursive(abs, accumulator);\r\n else\r\n accumulator.push(abs);\r\n }\r\n }\r\n\r\n static #isFolderTheRoot(folder: string): boolean {\r\n const thisOne = path.normalize(folder);\r\n const oneUp = path.join(folder, `..`);\r\n return oneUp === thisOne || oneUp === path.sep;\r\n }\r\n\r\n async getAllFilePathsRecursive(folderPath: string): Promise<string[]> {\r\n const files = new Array<string>();\r\n await NodeFileSystemService.#readDirRecursive(folderPath, files);\r\n return files;\r\n }\r\n\r\n async writeFile(filePath: string, data: string, encoding: BufferEncoding = `utf8`, abort: AbortSignal | undefined = undefined): Promise<void> {\r\n await fs.writeFile(filePath, data, { encoding, flag: `w`, signal: abort });\r\n }\r\n\r\n async getAllFilePaths(folderPath: string): Promise<Array<string>> {\r\n return await fs.readdir(folderPath);\r\n }\r\n\r\n async readFileAsString(filePath: string, encoding: BufferEncoding = `utf8`, abort: AbortSignal | undefined = undefined): Promise<string> {\r\n return await fs.readFile(filePath, { encoding, flag: `r`, signal: abort });\r\n }\r\n\r\n async pathExists(thePath: string, checkReadWriteAccess?: boolean): Promise<boolean> {\r\n try {\r\n // eslint-disable-next-line no-bitwise\r\n const mode = checkReadWriteAccess ? (fs.constants.W_OK | fs.constants.R_OK) : fs.constants.F_OK;\r\n await fs.access(thePath, mode);\r\n return true;\r\n }\r\n catch {\r\n return false;\r\n }\r\n }\r\n\r\n async createFolder(folderPath: string): Promise<void> {\r\n if (NodeFileSystemService.#isFolderTheRoot(folderPath))\r\n // the requested directory is a root directory (e.g. F:\\ or /) - consider it as existing\r\n return;\r\n\r\n try {\r\n await fs.mkdir(folderPath, { recursive: true });\r\n }\r\n catch (err) {\r\n const dirErr = err as Error;\r\n\r\n if (dirErr && dirErr.code === `EEXIST`)\r\n // EEXIST error is OK - the requested directory already exists\r\n return;\r\n\r\n await this.mkdirRecursiveSafe(folderPath);\r\n }\r\n }\r\n\r\n private async mkdirRecursiveSafe(target: string): Promise<void> {\r\n const foldersChain: string[] = [];\r\n\r\n let currentFolder = target;\r\n\r\n while (true) {\r\n foldersChain.push(currentFolder);\r\n\r\n const parent = path.dirname(currentFolder);\r\n\r\n if (parent === currentFolder)\r\n break;\r\n\r\n currentFolder = parent;\r\n }\r\n\r\n foldersChain.reverse();\r\n\r\n for (const dir of foldersChain) {\r\n if (NodeFileSystemService.#isFolderTheRoot(dir))\r\n continue; // the requested directory is a root directory (e.g. F:\\ or /) - consider it as existing\r\n try {\r\n // eslint-disable-next-line no-await-in-loop\r\n await fs.mkdir(dir);\r\n }\r\n catch (err: any) {\r\n if (err?.code !== `EEXIST`) // EEXIST error is OK - the requested directory already exists\r\n throw err;\r\n }\r\n }\r\n }\r\n\r\n async createFile(filePath: string, overwriteExisting: boolean, size?: number): Promise<boolean> {\r\n // Check if file folder exists and create if it doesn't\r\n const folder = path.dirname(filePath);\r\n await this.createFolder(folder);\r\n\r\n // Open the file for writing\r\n // overwriteExisting === true --> 'w' creates the file (if it doesn't exist) or truncates it (if it exists)\r\n // overwriteExisting === false --> 'wx' creates the file (if it doesn't exist) or fails (if it exists)\r\n const openMode = overwriteExisting ? `w` : `wx`;\r\n let handle: fs.FileHandle | null = null;\r\n try {\r\n try {\r\n handle = await fs.open(filePath, openMode);\r\n }\r\n catch (err) {\r\n const fileErr = err as Error;\r\n if (fileErr && fileErr.code === `EEXIST`)\r\n return false;\r\n\r\n throw err;\r\n }\r\n\r\n if ((size ?? 0) > 0)\r\n await handle.truncate(size);\r\n\r\n return true;\r\n }\r\n finally {\r\n if (handle !== null)\r\n await handle.close();\r\n }\r\n }\r\n\r\n async deleteFile(filePath: string): Promise<boolean> {\r\n try {\r\n await fs.rm(filePath);\r\n return true;\r\n }\r\n catch {\r\n return false;\r\n }\r\n }\r\n\r\n async folderIsEmpty(folderPath: string): Promise<boolean> {\r\n const contents = await fs.readdir(folderPath);\r\n return contents.length === 0;\r\n }\r\n\r\n async deleteFolder(folderPath: string): Promise<boolean> {\r\n return (await fs.rm(folderPath, { recursive: true, force: true })) === undefined;\r\n }\r\n\r\n async getFileSize(filePath: string): Promise<number> {\r\n const stat = await fs.stat(filePath);\r\n return stat.size;\r\n }\r\n\r\n async truncateFile(filePath: string, size: number): Promise<void> {\r\n await fs.truncate(filePath, size);\r\n }\r\n\r\n async renameFile(oldPath: string, newPath: string): Promise<void> {\r\n await fs.rename(oldPath, newPath);\r\n }\r\n\r\n pathJoin(...paths: Array<string>): string {\r\n return path.join(...paths);\r\n }\r\n\r\n fileErrorToDownloadErrorCode(error: Error): DownloadErrorCode | undefined {\r\n if (error && error.code === `ENOSPC`)\r\n return DownloadErrorCode.DiskFull;\r\n\r\n return undefined;\r\n }\r\n\r\n parseFilePath(filePath: string): ParsedPath {\r\n return path.parse(filePath);\r\n }\r\n\r\n formatFilePath(parsedPath: ParsedPath): string {\r\n return path.format(parsedPath);\r\n }\r\n\r\n replaceFilenameInPath(filePath: string, newFileName: string): string {\r\n const parsed = path.parse(filePath);\r\n return path.format({\r\n dir: parsed.dir,\r\n base: newFileName,\r\n });\r\n }\r\n}"]}
|
|
1
|
+
{"version":3,"file":"nodeFileSystemService.js","sourceRoot":"","sources":["../../../../../src/utils/fileSystem/node/nodeFileSystemService.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs/promises';\r\nimport path from 'path';\r\nimport DownloadErrorCode from '../../../downloader/dtos/downloadErrorCode.js';\r\nimport type FileSystemService from \"../fileSystemService.js\";\r\nimport type ParsedPath from '../parsedPath.js';\r\n\r\nexport default class NodeFileSystemService implements FileSystemService {\r\n static async #readDirRecursive(folder: string, accumulator: Array<string>) {\r\n const paths = await fs.readdir(folder);\r\n for (const itemPath of paths) {\r\n const abs = path.join(folder, itemPath);\r\n // eslint-disable-next-line no-await-in-loop\r\n if ((await fs.stat(abs)).isDirectory())\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.#readDirRecursive(abs, accumulator);\r\n else\r\n accumulator.push(abs);\r\n }\r\n }\r\n\r\n static #isFolderTheRoot(folder: string): boolean {\r\n const thisOne = path.normalize(folder);\r\n const oneUp = path.join(folder, `..`);\r\n return oneUp === thisOne || oneUp === path.sep;\r\n }\r\n\r\n async getAllFilePathsRecursive(folderPath: string): Promise<string[]> {\r\n const files = new Array<string>();\r\n await NodeFileSystemService.#readDirRecursive(folderPath, files);\r\n return files;\r\n }\r\n\r\n async writeFile(filePath: string, data: string, encoding: BufferEncoding = `utf8`, abort: AbortSignal | undefined = undefined): Promise<void> {\r\n await fs.writeFile(filePath, data, { encoding, flag: `w`, signal: abort });\r\n }\r\n\r\n async getAllFilePaths(folderPath: string): Promise<Array<string>> {\r\n return await fs.readdir(folderPath);\r\n }\r\n\r\n async readFileAsString(filePath: string, encoding: BufferEncoding = `utf8`, abort: AbortSignal | undefined = undefined): Promise<string> {\r\n return await fs.readFile(filePath, { encoding, flag: `r`, signal: abort });\r\n }\r\n\r\n async pathExists(thePath: string, checkReadWriteAccess?: boolean): Promise<boolean> {\r\n try {\r\n // eslint-disable-next-line no-bitwise\r\n const mode = checkReadWriteAccess ? (fs.constants.W_OK | fs.constants.R_OK) : fs.constants.F_OK;\r\n await fs.access(thePath, mode);\r\n return true;\r\n }\r\n catch {\r\n return false;\r\n }\r\n }\r\n\r\n async createFolder(folderPath: string): Promise<void> {\r\n if (NodeFileSystemService.#isFolderTheRoot(folderPath))\r\n // the requested directory is a root directory (e.g. F:\\ or /) - consider it as existing\r\n return;\r\n\r\n try {\r\n await fs.mkdir(folderPath, { recursive: true });\r\n }\r\n catch (err) {\r\n const dirErr = err as Error;\r\n\r\n if (dirErr && dirErr.code === `EEXIST`)\r\n // EEXIST error is OK - the requested directory already exists\r\n return;\r\n\r\n await this.mkdirRecursiveSafe(folderPath);\r\n }\r\n }\r\n\r\n private async mkdirRecursiveSafe(target: string): Promise<void> {\r\n const foldersChain: string[] = [];\r\n\r\n let currentFolder = target;\r\n\r\n while (true) {\r\n foldersChain.push(currentFolder);\r\n\r\n const parent = path.dirname(currentFolder);\r\n\r\n if (parent === currentFolder)\r\n break;\r\n\r\n currentFolder = parent;\r\n }\r\n\r\n foldersChain.reverse();\r\n\r\n for (const dir of foldersChain) {\r\n if (NodeFileSystemService.#isFolderTheRoot(dir))\r\n continue; // the requested directory is a root directory (e.g. F:\\ or /) - consider it as existing\r\n try {\r\n // eslint-disable-next-line no-await-in-loop\r\n await fs.mkdir(dir);\r\n }\r\n catch (err: any) {\r\n if (err?.code !== `EEXIST`) // EEXIST error is OK - the requested directory already exists\r\n throw err;\r\n }\r\n }\r\n }\r\n\r\n async createFile(filePath: string, overwriteExisting: boolean, size?: number): Promise<boolean> {\r\n // Check if file folder exists and create if it doesn't\r\n const folder = path.dirname(filePath);\r\n await this.createFolder(folder);\r\n\r\n // Open the file for writing\r\n // overwriteExisting === true --> 'w' creates the file (if it doesn't exist) or truncates it (if it exists)\r\n // overwriteExisting === false --> 'wx' creates the file (if it doesn't exist) or fails (if it exists)\r\n const openMode = overwriteExisting ? `w` : `wx`;\r\n let handle: fs.FileHandle | null = null;\r\n try {\r\n try {\r\n handle = await fs.open(filePath, openMode);\r\n }\r\n catch (err) {\r\n const fileErr = err as Error;\r\n if (fileErr && fileErr.code === `EEXIST`)\r\n return false;\r\n\r\n throw err;\r\n }\r\n\r\n if ((size ?? 0) > 0)\r\n await handle.truncate(size);\r\n\r\n return true;\r\n }\r\n finally {\r\n if (handle !== null)\r\n await handle.close();\r\n }\r\n }\r\n\r\n async deleteFile(filePath: string): Promise<boolean> {\r\n try {\r\n await fs.rm(filePath);\r\n return true;\r\n }\r\n catch {\r\n return false;\r\n }\r\n }\r\n\r\n async folderIsEmpty(folderPath: string): Promise<boolean> {\r\n const contents = await fs.readdir(folderPath);\r\n return contents.length === 0;\r\n }\r\n\r\n async deleteFolder(folderPath: string): Promise<boolean> {\r\n return (await fs.rm(folderPath, { recursive: true, force: true })) === undefined;\r\n }\r\n\r\n async getFileSize(filePath: string): Promise<number> {\r\n const stat = await fs.stat(filePath);\r\n return stat.size;\r\n }\r\n\r\n async truncateFile(filePath: string, size: number): Promise<void> {\r\n await fs.truncate(filePath, size);\r\n }\r\n\r\n async renameFile(oldPath: string, newPath: string): Promise<void> {\r\n await fs.rename(oldPath, newPath);\r\n }\r\n\r\n async getAvailableDiskSpace(pathToCheck: string): Promise<bigint> {\r\n const stats = await fs.statfs(pathToCheck, { bigint: true });\r\n return stats.bavail * stats.bsize;\r\n }\r\n\r\n async ensureEnoughFreeSpace(pathToCheck: string, requiredBytes: bigint, safetyMarginBytes: bigint): Promise<void> {\r\n const availableBytes = await this.getAvailableDiskSpace(pathToCheck);\r\n\r\n if (availableBytes < requiredBytes + safetyMarginBytes) {\r\n const err = new Error(`Not enough disk space. Required=${requiredBytes}B, available=${availableBytes}B (margin=${safetyMarginBytes}B)`) as NodeJS.ErrnoException;\r\n err.code = `ENOSPC`;\r\n throw err;\r\n }\r\n }\r\n\r\n pathJoin(...paths: Array<string>): string {\r\n return path.join(...paths);\r\n }\r\n\r\n fileErrorToDownloadErrorCode(error: Error): DownloadErrorCode | undefined {\r\n if (NodeFileSystemService.#extractErrorCode(error) === `ENOSPC`)\r\n return DownloadErrorCode.DiskFull;\r\n\r\n return undefined;\r\n }\r\n\r\n static #extractErrorCode(error: unknown): string | undefined {\r\n let current = error as { code?: string, cause?: unknown } | undefined;\r\n\r\n for (let depth = 0; depth < 8 && current; depth++) {\r\n if (typeof current.code === `string`)\r\n return current.code;\r\n\r\n current = current.cause as { code?: string, cause?: unknown } | undefined;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n parseFilePath(filePath: string): ParsedPath {\r\n return path.parse(filePath);\r\n }\r\n\r\n formatFilePath(parsedPath: ParsedPath): string {\r\n return path.format(parsedPath);\r\n }\r\n\r\n replaceFilenameInPath(filePath: string, newFileName: string): string {\r\n const parsed = path.parse(filePath);\r\n return path.format({\r\n dir: parsed.dir,\r\n base: newFileName,\r\n });\r\n }\r\n}\r\n"]}
|