@tgsnake/core
Version:
Pure Telegram MTProto library for nodejs
119 lines (118 loc) • 5.62 kB
JavaScript
"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;
}