UNPKG

@tgsnake/core

Version:

Pure Telegram MTProto library for nodejs

119 lines (118 loc) 5.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.handleDownload = handleDownload; exports.downloadStream = downloadStream; const File_js_1 = require("./File.js"); const index_js_1 = require("../raw/index.js"); const index_js_2 = require("../session/index.js"); const helpers_js_1 = require("../helpers.js"); const index_js_3 = require("../errors/index.js"); const index_js_4 = require("../crypto/index.js"); const platform_node_js_1 = require("../platform.node.js"); async function handleDownload(client, file, location, dcId, limit, offset) { const release = await client._getFileSemaphore.acquire(); let current = 0; const total = Math.abs(limit) || (1 << 31) - 1; const chunkSize = 1024 * 1024; let offsetBytes = helpers_js_1.bigMath.abs(offset) * BigInt(1024); const session = new index_js_2.Session(client, dcId, dcId !== client._storage.dcId ? await new index_js_2.Auth(dcId, client._storage.testMode, client._ipv6).create() : client._storage.authKey, client._storage.testMode, client._proxy, true); try { await session.start(); if (dcId !== client._storage.dcId) { const exportedAuth = await client.invoke(new index_js_1.Raw.auth.ExportAuthorization({ dcId: dcId })); await session.invoke(new index_js_1.Raw.auth.ImportAuthorization({ id: exportedAuth.id, bytes: exportedAuth.bytes, })); } let r = await session.invoke(new index_js_1.Raw.upload.GetFile({ location: location, offset: offsetBytes, limit: chunkSize, }), session.MAX_RETRIES, session.WAIT_TIMEOUT, 30000); if (r instanceof index_js_1.Raw.upload.File) { while (true) { const chunk = r.bytes; file.push(chunk); current++; offsetBytes += BigInt(chunkSize); if (platform_node_js_1.Buffer.byteLength(chunk) < chunkSize || current >= total) { break; } r = await session.invoke(new index_js_1.Raw.upload.GetFile({ location: location, offset: offsetBytes, limit: chunkSize, }), session.MAX_RETRIES, session.WAIT_TIMEOUT, 30000); } } else if (r instanceof index_js_1.Raw.upload.FileCdnRedirect) { const cdnSession = new index_js_2.Session(client, dcId, dcId !== client._storage.dcId ? await new index_js_2.Auth(dcId, client._storage.testMode, client._ipv6).create() : client._storage.authKey, client._storage.testMode, client._proxy, true, true); try { while (true) { const r2 = (await cdnSession.invoke(new index_js_1.Raw.upload.GetCdnFile({ fileToken: r.fileToken, offset: offsetBytes, limit: chunkSize, }), session.MAX_RETRIES, session.WAIT_TIMEOUT, 30000)); if (r2 instanceof index_js_1.Raw.upload.CdnFileReuploadNeeded) { try { await session.invoke(new index_js_1.Raw.upload.ReuploadCdnFile({ fileToken: r.fileToken, requestToken: r2.requestToken, })); } catch (error) { if (error instanceof index_js_3.Exceptions.BadRequest.VolumeLocNotFound) { break; } } } const chunk = r2.bytes; const decryptedChunk = await index_js_4.AES.ctr256Cipher(r.encryptionKey, platform_node_js_1.Buffer.concat([ r.encryptionIv.subarray(0, -4), (0, helpers_js_1.bigintToBuffer)(offsetBytes / BigInt(16), 4, false), ]))(chunk); const hashes = (await session.invoke(new index_js_1.Raw.upload.GetCdnFileHashes({ fileToken: r.fileToken, offset: offsetBytes, }))); for (let i = 0; i < hashes.length; i++) { const hash = hashes[i]; const hashChunk = decryptedChunk.subarray(hash.limit * i, hash.limit * (i + 1)); const chash = platform_node_js_1.crypto.createHash('sha256'); chash.update(hashChunk); index_js_3.CDNFileHashMismatch.check(chash.digest('hex') === hash.hash.toString('hex'), `CDN file hash mismatch when downloading cdn file`); } current++; offsetBytes += BigInt(chunkSize); if (platform_node_js_1.Buffer.byteLength(chunk) < chunkSize || current >= total) { break; } } } finally { await cdnSession.stop(); } } } finally { await session.stop(); file.push(null); if (platform_node_js_1.isDeno) { release(); } else { release[1](); } } } function downloadStream(client, location, dcId, limit = 0, offset = BigInt(0)) { const file = new File_js_1.File(); handleDownload(client, file, location, dcId, limit, offset); return file; }