@mtkruto/browser 0.119.0 → 0.120.0
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/README.md +1 -1
- package/esm/0_errors.d.ts.map +1 -1
- package/esm/0_errors.js +9 -31
- package/esm/3_errors.js +2 -12
- package/esm/4_errors.js +2 -12
- package/esm/_dnt.polyfills.d.ts +0 -99
- package/esm/_dnt.polyfills.d.ts.map +1 -1
- package/esm/_dnt.polyfills.js +1 -127
- package/esm/client/0_abortable_loop.js +26 -39
- package/esm/client/0_storage_operations.js +179 -218
- package/esm/client/1_client_plain.js +4 -22
- package/esm/client/2_account_manager.js +140 -149
- package/esm/client/2_bot_info_manager.js +26 -38
- package/esm/client/2_business_connection_manager.js +10 -23
- package/esm/client/2_client_encrypted.js +198 -215
- package/esm/client/2_file_manager.js +255 -262
- package/esm/client/2_network_statistics_manager.js +31 -44
- package/esm/client/2_payment_manager.js +7 -20
- package/esm/client/2_reaction_manager.js +7 -20
- package/esm/client/2_translations_manager.js +101 -111
- package/esm/client/2_update_manager.js +750 -745
- package/esm/client/3_client_encrypted_pool.js +10 -26
- package/esm/client/3_message_manager.js +503 -508
- package/esm/client/3_video_chat_manager.js +57 -68
- package/esm/client/4_callback_query_manager.js +18 -30
- package/esm/client/4_chat_list_manager.js +140 -146
- package/esm/client/4_chat_manager.js +161 -169
- package/esm/client/4_checklist_manager.js +26 -39
- package/esm/client/4_context.js +244 -259
- package/esm/client/4_forum_manager.js +67 -73
- package/esm/client/4_gift_manager.js +22 -35
- package/esm/client/4_inline_query_manager.js +16 -28
- package/esm/client/4_link_preview_manager.js +6 -19
- package/esm/client/4_poll_manager.js +44 -57
- package/esm/client/4_story_manager.js +41 -53
- package/esm/client/5_composer.js +13 -26
- package/esm/client/6_client.js +866 -896
- package/esm/client/6_client_dispatcher.js +308 -325
- package/esm/client/7_client_worker.js +16 -29
- package/esm/connection/1_connection_tcp.js +55 -82
- package/esm/connection/1_connection_web_socket.js +75 -91
- package/esm/deps/jsr.io/@roj/tgcrypto/1.0.1/dist/tgcrypto.js +3 -11
- package/esm/deps/jsr.io/@std/async/1.2.0/mux_async_iterator.js +31 -47
- package/esm/deps/jsr.io/@std/async/1.2.0/tee.js +11 -34
- package/esm/deps/jsr.io/@std/cache/0.2.2/lru_cache.js +30 -47
- package/esm/deps/jsr.io/@std/datetime/0.225.7/_date_time_formatter.js +4 -17
- package/esm/session/0_session_state.js +12 -38
- package/esm/session/1_session.js +49 -72
- package/esm/session/2_session_encrypted.js +422 -420
- package/esm/storage/2_storage_indexed_db.js +26 -44
- package/esm/storage/2_storage_local_storage.js +3 -16
- package/esm/storage/2_storage_memory.js +24 -41
- package/esm/storage/2_storage_session_storage.js +3 -16
- package/esm/tl/1_tl_reader.d.ts +1 -1
- package/esm/tl/1_tl_reader.d.ts.map +1 -1
- package/esm/tl/1_tl_reader.js +95 -103
- package/esm/tl/1_tl_writer.js +169 -178
- package/esm/transport/0_transport.js +1 -8
- package/esm/transport/1_transport_abridged.js +11 -24
- package/esm/transport/1_transport_intermediate.js +10 -23
- package/esm/utilities/0_mutex.js +4 -19
- package/esm/utilities/0_part_stream.js +11 -25
- package/esm/utilities/1_crypto.js +42 -53
- package/esm/utilities/2_queue.js +29 -47
- package/package.json +1 -1
- package/script/0_errors.d.ts.map +1 -1
- package/script/0_errors.js +9 -31
- package/script/3_errors.js +2 -12
- package/script/4_errors.js +2 -12
- package/script/_dnt.polyfills.d.ts +0 -99
- package/script/_dnt.polyfills.d.ts.map +1 -1
- package/script/_dnt.polyfills.js +0 -128
- package/script/client/0_abortable_loop.js +27 -40
- package/script/client/0_storage_operations.js +179 -218
- package/script/client/1_client_plain.js +4 -22
- package/script/client/2_account_manager.js +140 -149
- package/script/client/2_bot_info_manager.js +26 -38
- package/script/client/2_business_connection_manager.js +10 -23
- package/script/client/2_client_encrypted.js +199 -216
- package/script/client/2_file_manager.js +255 -262
- package/script/client/2_network_statistics_manager.js +32 -45
- package/script/client/2_payment_manager.js +7 -20
- package/script/client/2_reaction_manager.js +7 -20
- package/script/client/2_translations_manager.js +102 -112
- package/script/client/2_update_manager.js +750 -745
- package/script/client/3_client_encrypted_pool.js +10 -26
- package/script/client/3_message_manager.js +503 -508
- package/script/client/3_video_chat_manager.js +57 -68
- package/script/client/4_callback_query_manager.js +18 -30
- package/script/client/4_chat_list_manager.js +140 -146
- package/script/client/4_chat_manager.js +161 -169
- package/script/client/4_checklist_manager.js +26 -39
- package/script/client/4_context.js +244 -259
- package/script/client/4_forum_manager.js +67 -73
- package/script/client/4_gift_manager.js +22 -35
- package/script/client/4_inline_query_manager.js +16 -28
- package/script/client/4_link_preview_manager.js +6 -19
- package/script/client/4_poll_manager.js +44 -57
- package/script/client/4_story_manager.js +41 -53
- package/script/client/5_composer.js +13 -26
- package/script/client/6_client.js +866 -896
- package/script/client/6_client_dispatcher.js +308 -325
- package/script/client/7_client_worker.js +16 -29
- package/script/connection/1_connection_tcp.js +55 -82
- package/script/connection/1_connection_web_socket.js +75 -91
- package/script/deps/jsr.io/@roj/tgcrypto/1.0.1/dist/tgcrypto.js +3 -11
- package/script/deps/jsr.io/@std/async/1.2.0/mux_async_iterator.js +31 -47
- package/script/deps/jsr.io/@std/async/1.2.0/tee.js +11 -34
- package/script/deps/jsr.io/@std/cache/0.2.2/lru_cache.js +30 -47
- package/script/deps/jsr.io/@std/datetime/0.225.7/_date_time_formatter.js +4 -17
- package/script/session/0_session_state.js +12 -38
- package/script/session/1_session.js +49 -72
- package/script/session/2_session_encrypted.js +423 -421
- package/script/storage/2_storage_indexed_db.js +26 -44
- package/script/storage/2_storage_local_storage.js +3 -16
- package/script/storage/2_storage_memory.js +24 -41
- package/script/storage/2_storage_session_storage.js +3 -16
- package/script/tl/1_tl_reader.d.ts +1 -1
- package/script/tl/1_tl_reader.d.ts.map +1 -1
- package/script/tl/1_tl_reader.js +96 -104
- package/script/tl/1_tl_writer.js +170 -179
- package/script/transport/0_transport.js +1 -8
- package/script/transport/1_transport_abridged.js +11 -24
- package/script/transport/1_transport_intermediate.js +10 -23
- package/script/utilities/0_mutex.js +4 -19
- package/script/utilities/0_part_stream.js +11 -25
- package/script/utilities/1_crypto.js +43 -54
- package/script/utilities/2_queue.js +30 -48
|
@@ -17,18 +17,7 @@
|
|
|
17
17
|
* You should have received a copy of the GNU Lesser General Public License
|
|
18
18
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
19
19
|
*/
|
|
20
|
-
var
|
|
21
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
22
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
23
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
24
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
25
|
-
};
|
|
26
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
27
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
28
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
29
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
30
|
-
};
|
|
31
|
-
var _FileManager_instances, _a, _FileManager_c, _FileManager_Lupload, _FileManager_UPLOAD_MAX_CHUNK_SIZE, _FileManager_BIG_FILE_THRESHOLD, _FileManager_progressIds, _FileManager_uploadStream, _FileManager_uploadBuffer, _FileManager_uploadPart, _FileManager_handleError, _FileManager_getFileContents, _FileManager_CUSTOM_EMOJI_TTL;
|
|
20
|
+
var _a;
|
|
32
21
|
import { AssertionError, basename, delay, extension, extname, isAbsolute, join, MINUTE, SECOND, toFileUrl, unreachable } from "../0_deps.js";
|
|
33
22
|
import { InputError } from "../0_errors.js";
|
|
34
23
|
import { getLogger, getRandomId, iterateReadableStream, kilobyte, megabyte, mod, PartStream } from "../1_utilities.js";
|
|
@@ -40,53 +29,54 @@ import { DOWNLOAD_MAX_CHUNK_SIZE, STICKER_SET_NAME_TTL } from "../4_constants.js
|
|
|
40
29
|
import { FloodWait, StickersetInvalid } from "../4_errors.js";
|
|
41
30
|
import { UPLOAD_REQUEST_PER_CONNECTION } from "./0_utilities.js";
|
|
42
31
|
export class FileManager {
|
|
32
|
+
#c;
|
|
33
|
+
#Lupload;
|
|
34
|
+
static #UPLOAD_MAX_CHUNK_SIZE = 512 * kilobyte;
|
|
35
|
+
static #BIG_FILE_THRESHOLD = 10 * megabyte;
|
|
43
36
|
constructor(c) {
|
|
44
|
-
|
|
45
|
-
_FileManager_c.set(this, void 0);
|
|
46
|
-
_FileManager_Lupload.set(this, void 0);
|
|
47
|
-
_FileManager_progressIds.set(this, new Set());
|
|
48
|
-
__classPrivateFieldSet(this, _FileManager_c, c, "f");
|
|
37
|
+
this.#c = c;
|
|
49
38
|
const L = getLogger("FileManager").client(c.id);
|
|
50
|
-
|
|
39
|
+
this.#Lupload = L.branch("upload");
|
|
51
40
|
}
|
|
41
|
+
#progressIds = new Set();
|
|
52
42
|
getProgressId() {
|
|
53
43
|
let id;
|
|
54
44
|
do {
|
|
55
45
|
id = getRandomId();
|
|
56
|
-
} while (id === 0n ||
|
|
57
|
-
|
|
46
|
+
} while (id === 0n || this.#progressIds.has(id));
|
|
47
|
+
this.#progressIds.add(id);
|
|
58
48
|
return Promise.resolve(String(id));
|
|
59
49
|
}
|
|
60
50
|
async upload(file, params, checkName, allowStream = true) {
|
|
61
|
-
if (params?.progressId !== undefined && !
|
|
51
|
+
if (params?.progressId !== undefined && !this.#progressIds.has(BigInt(params.progressId))) {
|
|
62
52
|
throw new InputError("Invalid progressId.");
|
|
63
53
|
}
|
|
64
54
|
if (params?.progressId !== undefined) {
|
|
65
|
-
|
|
55
|
+
this.#progressIds.delete(BigInt(params.progressId));
|
|
66
56
|
}
|
|
67
|
-
let { size, name, contents } = await
|
|
57
|
+
let { size, name, contents } = await _a.#getFileContents(file, params, allowStream);
|
|
68
58
|
if (checkName) {
|
|
69
59
|
name = checkName(name);
|
|
70
60
|
}
|
|
71
61
|
if (size === 0 || size < -1) {
|
|
72
62
|
throw new InputError("Invalid file size.");
|
|
73
63
|
}
|
|
74
|
-
const poolSize = await
|
|
75
|
-
const chunkSize = params?.chunkSize ??
|
|
76
|
-
_a.validateChunkSize(chunkSize,
|
|
64
|
+
const poolSize = await this.#c.getUploadPoolSize();
|
|
65
|
+
const chunkSize = params?.chunkSize ?? _a.#UPLOAD_MAX_CHUNK_SIZE;
|
|
66
|
+
_a.validateChunkSize(chunkSize, _a.#UPLOAD_MAX_CHUNK_SIZE);
|
|
77
67
|
const mustTrackProgress = params?.progressId !== undefined;
|
|
78
68
|
const fileId = params?.progressId !== undefined ? BigInt(params.progressId) : getRandomId();
|
|
79
|
-
const isBig = contents instanceof Uint8Array ? contents.length >
|
|
69
|
+
const isBig = contents instanceof Uint8Array ? contents.length > _a.#BIG_FILE_THRESHOLD : true;
|
|
80
70
|
const whatIsUploaded = contents instanceof Uint8Array ? (isBig ? "big file" : "file") + " of size " + size : "stream";
|
|
81
|
-
|
|
71
|
+
this.#Lupload.debug("uploading " + whatIsUploaded + " with chunk size of " + chunkSize + " and pool size of " + poolSize + " and file ID of " + fileId);
|
|
82
72
|
let result;
|
|
83
73
|
if (contents instanceof Uint8Array) {
|
|
84
|
-
result = await
|
|
74
|
+
result = await this.#uploadBuffer(contents, fileId, mustTrackProgress, chunkSize, poolSize, params?.signal);
|
|
85
75
|
}
|
|
86
76
|
else {
|
|
87
|
-
result = await
|
|
77
|
+
result = await this.#uploadStream(contents, fileId, mustTrackProgress, chunkSize, poolSize, params?.signal);
|
|
88
78
|
}
|
|
89
|
-
|
|
79
|
+
this.#Lupload.debug(`[${fileId}] uploaded ` + result.parts + " part(s)");
|
|
90
80
|
if (checkName) {
|
|
91
81
|
name = checkName(name, result.firstPart);
|
|
92
82
|
}
|
|
@@ -97,15 +87,232 @@ export class FileManager {
|
|
|
97
87
|
return { _: "inputFileBig", id: fileId, name, parts: result.parts };
|
|
98
88
|
}
|
|
99
89
|
}
|
|
90
|
+
async #uploadStream(stream, fileId, mustTrackProgress, chunkSize, poolSize, signal) {
|
|
91
|
+
let part;
|
|
92
|
+
let promises = new Array();
|
|
93
|
+
let ms = 0.05;
|
|
94
|
+
let uploaded = 0;
|
|
95
|
+
let firstPart;
|
|
96
|
+
for await (part of iterateReadableStream(stream.pipeThrough(new PartStream(chunkSize)))) {
|
|
97
|
+
if (!part.isSmall && part.part > 0) {
|
|
98
|
+
await delay(ms);
|
|
99
|
+
ms = Math.max(ms * .8, 0.003);
|
|
100
|
+
}
|
|
101
|
+
if (!firstPart) {
|
|
102
|
+
firstPart = part.bytes;
|
|
103
|
+
}
|
|
104
|
+
promises.push(this.#uploadPart(fileId, part.totalParts, !part.isSmall, part.part, part.bytes, signal).then(() => {
|
|
105
|
+
if (mustTrackProgress) {
|
|
106
|
+
uploaded += part.bytes.length;
|
|
107
|
+
this.#c.handleUpdate({
|
|
108
|
+
uploadProgress: {
|
|
109
|
+
id: String(fileId),
|
|
110
|
+
uploaded,
|
|
111
|
+
total: 0,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}));
|
|
116
|
+
if (promises.length === poolSize * UPLOAD_REQUEST_PER_CONNECTION) {
|
|
117
|
+
await Promise.all(promises);
|
|
118
|
+
promises = [];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
await Promise.all(promises);
|
|
122
|
+
return { isSmall: part.isSmall, parts: part.totalParts, firstPart };
|
|
123
|
+
}
|
|
124
|
+
async #uploadBuffer(buffer, fileId, mustTrackProgress, chunkSize, poolSize, signal) {
|
|
125
|
+
const isBig = buffer.byteLength > _a.#BIG_FILE_THRESHOLD;
|
|
126
|
+
const partCount = Math.ceil(buffer.byteLength / chunkSize);
|
|
127
|
+
let promises = new Array();
|
|
128
|
+
let started = false;
|
|
129
|
+
let ms = 0.05;
|
|
130
|
+
let uploaded = 0;
|
|
131
|
+
let firstPart;
|
|
132
|
+
main: for (let part = 0; part < partCount;) {
|
|
133
|
+
for (let i = 0; i < poolSize; ++i) {
|
|
134
|
+
for (let i = 0; i < UPLOAD_REQUEST_PER_CONNECTION; ++i) {
|
|
135
|
+
const start = part * chunkSize;
|
|
136
|
+
const end = start + chunkSize;
|
|
137
|
+
const bytes = buffer.subarray(start, end);
|
|
138
|
+
if (!bytes.length) {
|
|
139
|
+
break main;
|
|
140
|
+
}
|
|
141
|
+
if (!started) {
|
|
142
|
+
started = true;
|
|
143
|
+
}
|
|
144
|
+
else if (isBig && part > 0) {
|
|
145
|
+
await delay(ms);
|
|
146
|
+
ms = Math.max(ms * .8, 0.003);
|
|
147
|
+
}
|
|
148
|
+
if (!firstPart) {
|
|
149
|
+
firstPart = bytes;
|
|
150
|
+
}
|
|
151
|
+
promises.push(this.#uploadPart(fileId, partCount, isBig, part++, bytes, signal).then(() => {
|
|
152
|
+
if (mustTrackProgress) {
|
|
153
|
+
uploaded += bytes.length;
|
|
154
|
+
this.#c.handleUpdate({
|
|
155
|
+
uploadProgress: {
|
|
156
|
+
id: String(fileId),
|
|
157
|
+
uploaded,
|
|
158
|
+
total: buffer.length,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}));
|
|
163
|
+
if (promises.length === poolSize * UPLOAD_REQUEST_PER_CONNECTION) {
|
|
164
|
+
await Promise.all(promises);
|
|
165
|
+
promises = [];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
await Promise.all(promises);
|
|
170
|
+
promises = [];
|
|
171
|
+
}
|
|
172
|
+
await Promise.all(promises);
|
|
173
|
+
return { isSmall: !isBig, parts: partCount, firstPart };
|
|
174
|
+
}
|
|
175
|
+
async #uploadPart(fileId, partCount, isBig, index, bytes, signal) {
|
|
176
|
+
let retryIn = 1;
|
|
177
|
+
let errorCount = 0;
|
|
178
|
+
while (true) {
|
|
179
|
+
try {
|
|
180
|
+
signal?.throwIfAborted();
|
|
181
|
+
this.#Lupload.debug(`[${fileId}] uploading part ` + (index + 1));
|
|
182
|
+
if (isBig) {
|
|
183
|
+
await this.#c.invoke({ _: "upload.saveBigFilePart", file_id: fileId, file_part: index, bytes, file_total_parts: partCount }, { type: "upload" });
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
await this.#c.invoke({ _: "upload.saveFilePart", file_id: fileId, bytes, file_part: index }, { type: "upload" });
|
|
187
|
+
}
|
|
188
|
+
this.#Lupload.debug(`[${fileId}] uploaded part ` + (index + 1));
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
signal?.throwIfAborted();
|
|
193
|
+
this.#Lupload.debug(`[${fileId}] failed to upload part ` + (index + 1));
|
|
194
|
+
++errorCount;
|
|
195
|
+
if (errorCount > 20) {
|
|
196
|
+
retryIn = 0;
|
|
197
|
+
errorCount = 20;
|
|
198
|
+
}
|
|
199
|
+
await this.#handleError(err, retryIn, `[${fileId}-${index + 1}]`);
|
|
200
|
+
retryIn += 2;
|
|
201
|
+
if (retryIn > 11) {
|
|
202
|
+
retryIn = 11;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async #handleError(err, retryIn, logPrefix) {
|
|
208
|
+
if (err instanceof TimeTooBig || err instanceof TimeTooSmall) {
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
else if (err instanceof FloodWait) {
|
|
212
|
+
this.#Lupload.warning(`${logPrefix} retrying in ${err.seconds} seconds:`, err);
|
|
213
|
+
await delay(err.seconds * SECOND);
|
|
214
|
+
}
|
|
215
|
+
else if (retryIn > 0) {
|
|
216
|
+
this.#Lupload.warning(`${logPrefix} retrying in ${retryIn} seconds:`, err);
|
|
217
|
+
await delay(retryIn * SECOND);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
throw err;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
static async #getFileContents(source, params, allowStream) {
|
|
224
|
+
let name = params?.fileName?.trim() || "file";
|
|
225
|
+
let contents;
|
|
226
|
+
let size = -1;
|
|
227
|
+
if (source instanceof Uint8Array) {
|
|
228
|
+
contents = source;
|
|
229
|
+
size = source.byteLength;
|
|
230
|
+
}
|
|
231
|
+
else if (source instanceof ReadableStream) {
|
|
232
|
+
if (!allowStream) {
|
|
233
|
+
throw new InputError("Streamed upload not allowed.");
|
|
234
|
+
}
|
|
235
|
+
contents = source;
|
|
236
|
+
}
|
|
237
|
+
else if (typeof source === "object" && source !== null && (Symbol.iterator in source || Symbol.asyncIterator in source)) {
|
|
238
|
+
if (!allowStream) {
|
|
239
|
+
throw new InputError("Streamed upload not allowed.");
|
|
240
|
+
}
|
|
241
|
+
contents = new ReadableStream({
|
|
242
|
+
pull: Symbol.asyncIterator in source
|
|
243
|
+
? async (controller) => {
|
|
244
|
+
const { value, done } = await source.next();
|
|
245
|
+
done ? controller.close() : controller.enqueue(value);
|
|
246
|
+
}
|
|
247
|
+
: (controller) => {
|
|
248
|
+
const { value, done } = source.next();
|
|
249
|
+
done ? controller.close() : controller.enqueue(value);
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
let url;
|
|
255
|
+
try {
|
|
256
|
+
url = new URL(source).toString();
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
let path_;
|
|
260
|
+
if (typeof source === "string") {
|
|
261
|
+
if (isAbsolute(source)) {
|
|
262
|
+
path_ = source;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
// @ts-ignore: lib
|
|
266
|
+
path_ = join(Deno.cwd(), source);
|
|
267
|
+
}
|
|
268
|
+
url = toFileUrl(path_).toString();
|
|
269
|
+
name = basename(path_);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
unreachable();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const response = await fetch(url);
|
|
276
|
+
if (response.body === null) {
|
|
277
|
+
throw new InputError("Invalid response");
|
|
278
|
+
}
|
|
279
|
+
if (name === "file") {
|
|
280
|
+
const contentType = response.headers.get("content-type")?.split(";")[0].trim();
|
|
281
|
+
if (contentType) {
|
|
282
|
+
name += extension(contentType);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const maybeFileName = new URL(response.url).pathname.split("/")
|
|
286
|
+
.filter((v) => v)
|
|
287
|
+
.slice(-1)[0]
|
|
288
|
+
.trim();
|
|
289
|
+
if (maybeFileName) {
|
|
290
|
+
name += extension(extname(maybeFileName));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const contentLength = Number(response.headers.get("content-length"));
|
|
295
|
+
if (contentLength && !isNaN(contentLength)) {
|
|
296
|
+
size = contentLength;
|
|
297
|
+
}
|
|
298
|
+
if (allowStream) {
|
|
299
|
+
contents = response.body;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
contents = new Uint8Array(await response.arrayBuffer());
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { size: params?.fileSize ? params.fileSize : size, name, contents };
|
|
306
|
+
}
|
|
100
307
|
async *downloadInner(location, dcId, params) {
|
|
101
308
|
const signal = params?.signal;
|
|
102
309
|
signal?.throwIfAborted();
|
|
103
310
|
const id = "id" in location ? location.id : "photo_id" in location ? location.photo_id : null;
|
|
104
|
-
if (id !== null &&
|
|
105
|
-
const file = await
|
|
311
|
+
if (id !== null && this.#c.storage.supportsFiles) {
|
|
312
|
+
const file = await this.#c.storage.getFile(id);
|
|
106
313
|
const partOffset = file === null ? 0 : params?.offset ? Math.ceil(10 / file[1]) - 1 : 0;
|
|
107
314
|
if (file !== null && file[0] > 0) {
|
|
108
|
-
yield*
|
|
315
|
+
yield* this.#c.storage.iterFileParts(id, file[0], partOffset, signal);
|
|
109
316
|
return;
|
|
110
317
|
}
|
|
111
318
|
}
|
|
@@ -125,18 +332,18 @@ export class FileManager {
|
|
|
125
332
|
let retryIn = 1;
|
|
126
333
|
let errorCount = 0;
|
|
127
334
|
try {
|
|
128
|
-
const file = await
|
|
335
|
+
const file = await this.#c.invoke({ _: "upload.getFile", location, offset, limit }, { dc, type: "download" });
|
|
129
336
|
signal?.throwIfAborted();
|
|
130
337
|
if (Api.is("upload.file", file)) {
|
|
131
338
|
yield file.bytes;
|
|
132
339
|
if (id !== null) {
|
|
133
|
-
await
|
|
340
|
+
await this.#c.storage.saveFilePart(id, part, file.bytes);
|
|
134
341
|
signal?.throwIfAborted();
|
|
135
342
|
}
|
|
136
343
|
++part;
|
|
137
344
|
if (file.bytes.length < limit) {
|
|
138
345
|
if (id !== null) {
|
|
139
|
-
await
|
|
346
|
+
await this.#c.storage.setFilePartCount(id, part + 1, chunkSize);
|
|
140
347
|
signal?.throwIfAborted();
|
|
141
348
|
}
|
|
142
349
|
break;
|
|
@@ -160,7 +367,7 @@ export class FileManager {
|
|
|
160
367
|
retryIn = 0;
|
|
161
368
|
errorCount = 0;
|
|
162
369
|
}
|
|
163
|
-
await
|
|
370
|
+
await this.#handleError(err, retryIn, `[${id}-${part + 1}]`);
|
|
164
371
|
signal?.throwIfAborted();
|
|
165
372
|
retryIn += 2;
|
|
166
373
|
if (retryIn > 11) {
|
|
@@ -203,7 +410,7 @@ export class FileManager {
|
|
|
203
410
|
unreachable();
|
|
204
411
|
}
|
|
205
412
|
const big = fileId_.location.source.type === PhotoSourceType.ChatPhotoBig;
|
|
206
|
-
const peer = await
|
|
413
|
+
const peer = await this.#c.getInputPeer(Number(fileId_.location.source.chatId));
|
|
207
414
|
const location = { _: "inputPeerPhotoFileLocation", big: big ? true : undefined, peer, photo_id: fileId_.location.id };
|
|
208
415
|
yield* this.downloadInner(location, fileId_.dcId, params);
|
|
209
416
|
break;
|
|
@@ -261,15 +468,15 @@ export class FileManager {
|
|
|
261
468
|
}
|
|
262
469
|
}
|
|
263
470
|
async getStickerSetName(inputStickerSet, hash = 0) {
|
|
264
|
-
const maybeStickerSetName = await
|
|
471
|
+
const maybeStickerSetName = await this.#c.messageStorage.getStickerSetName(inputStickerSet.id, inputStickerSet.access_hash);
|
|
265
472
|
if (maybeStickerSetName !== null && Date.now() - maybeStickerSetName[1].getTime() < STICKER_SET_NAME_TTL) {
|
|
266
473
|
return maybeStickerSetName[0];
|
|
267
474
|
}
|
|
268
475
|
else {
|
|
269
476
|
try {
|
|
270
|
-
const stickerSet = await
|
|
477
|
+
const stickerSet = await this.#c.invoke({ _: "messages.getStickerSet", stickerset: inputStickerSet, hash });
|
|
271
478
|
const name = Api.as("messages.stickerSet", stickerSet).set.short_name;
|
|
272
|
-
await
|
|
479
|
+
await this.#c.messageStorage.updateStickerSetName(inputStickerSet.id, inputStickerSet.access_hash, name);
|
|
273
480
|
return name;
|
|
274
481
|
}
|
|
275
482
|
catch (err) {
|
|
@@ -282,6 +489,7 @@ export class FileManager {
|
|
|
282
489
|
}
|
|
283
490
|
}
|
|
284
491
|
}
|
|
492
|
+
static #CUSTOM_EMOJI_TTL = 30 * MINUTE;
|
|
285
493
|
async getCustomEmojiStickers(id) {
|
|
286
494
|
id = Array.isArray(id) ? id : [id];
|
|
287
495
|
if (!id.length) {
|
|
@@ -290,8 +498,8 @@ export class FileManager {
|
|
|
290
498
|
const stickers = new Array();
|
|
291
499
|
let shouldFetch = false;
|
|
292
500
|
for (const id_ of id) {
|
|
293
|
-
const maybeDocument = await
|
|
294
|
-
if (maybeDocument !== null && Date.now() - maybeDocument[1].getTime() <=
|
|
501
|
+
const maybeDocument = await this.#c.messageStorage.getCustomEmojiDocument(BigInt(id_));
|
|
502
|
+
if (maybeDocument !== null && Date.now() - maybeDocument[1].getTime() <= _a.#CUSTOM_EMOJI_TTL) {
|
|
295
503
|
const document_ = maybeDocument[0];
|
|
296
504
|
const fileId_ = {
|
|
297
505
|
type: FileType.Document,
|
|
@@ -312,9 +520,9 @@ export class FileManager {
|
|
|
312
520
|
if (!shouldFetch) {
|
|
313
521
|
return stickers;
|
|
314
522
|
}
|
|
315
|
-
const documents_ = (await
|
|
523
|
+
const documents_ = (await this.#c.invoke({ _: "messages.getCustomEmojiDocuments", document_id: id.map(BigInt) })).map((v) => Api.as("document", v));
|
|
316
524
|
for (const [i, document_] of documents_.entries()) {
|
|
317
|
-
await
|
|
525
|
+
await this.#c.messageStorage.setCustomEmojiDocument(document_.id, document_);
|
|
318
526
|
const fileId_ = {
|
|
319
527
|
type: FileType.Document,
|
|
320
528
|
dcId: document_.dc_id,
|
|
@@ -329,219 +537,4 @@ export class FileManager {
|
|
|
329
537
|
return stickers;
|
|
330
538
|
}
|
|
331
539
|
}
|
|
332
|
-
_a = FileManager
|
|
333
|
-
let part;
|
|
334
|
-
let promises = new Array();
|
|
335
|
-
let ms = 0.05;
|
|
336
|
-
let uploaded = 0;
|
|
337
|
-
let firstPart;
|
|
338
|
-
for await (part of iterateReadableStream(stream.pipeThrough(new PartStream(chunkSize)))) {
|
|
339
|
-
if (!part.isSmall && part.part > 0) {
|
|
340
|
-
await delay(ms);
|
|
341
|
-
ms = Math.max(ms * .8, 0.003);
|
|
342
|
-
}
|
|
343
|
-
if (!firstPart) {
|
|
344
|
-
firstPart = part.bytes;
|
|
345
|
-
}
|
|
346
|
-
promises.push(__classPrivateFieldGet(this, _FileManager_instances, "m", _FileManager_uploadPart).call(this, fileId, part.totalParts, !part.isSmall, part.part, part.bytes, signal).then(() => {
|
|
347
|
-
if (mustTrackProgress) {
|
|
348
|
-
uploaded += part.bytes.length;
|
|
349
|
-
__classPrivateFieldGet(this, _FileManager_c, "f").handleUpdate({
|
|
350
|
-
uploadProgress: {
|
|
351
|
-
id: String(fileId),
|
|
352
|
-
uploaded,
|
|
353
|
-
total: 0,
|
|
354
|
-
},
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
}));
|
|
358
|
-
if (promises.length === poolSize * UPLOAD_REQUEST_PER_CONNECTION) {
|
|
359
|
-
await Promise.all(promises);
|
|
360
|
-
promises = [];
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
await Promise.all(promises);
|
|
364
|
-
return { isSmall: part.isSmall, parts: part.totalParts, firstPart };
|
|
365
|
-
}, _FileManager_uploadBuffer = async function _FileManager_uploadBuffer(buffer, fileId, mustTrackProgress, chunkSize, poolSize, signal) {
|
|
366
|
-
const isBig = buffer.byteLength > __classPrivateFieldGet(_a, _a, "f", _FileManager_BIG_FILE_THRESHOLD);
|
|
367
|
-
const partCount = Math.ceil(buffer.byteLength / chunkSize);
|
|
368
|
-
let promises = new Array();
|
|
369
|
-
let started = false;
|
|
370
|
-
let ms = 0.05;
|
|
371
|
-
let uploaded = 0;
|
|
372
|
-
let firstPart;
|
|
373
|
-
main: for (let part = 0; part < partCount;) {
|
|
374
|
-
for (let i = 0; i < poolSize; ++i) {
|
|
375
|
-
for (let i = 0; i < UPLOAD_REQUEST_PER_CONNECTION; ++i) {
|
|
376
|
-
const start = part * chunkSize;
|
|
377
|
-
const end = start + chunkSize;
|
|
378
|
-
const bytes = buffer.subarray(start, end);
|
|
379
|
-
if (!bytes.length) {
|
|
380
|
-
break main;
|
|
381
|
-
}
|
|
382
|
-
if (!started) {
|
|
383
|
-
started = true;
|
|
384
|
-
}
|
|
385
|
-
else if (isBig && part > 0) {
|
|
386
|
-
await delay(ms);
|
|
387
|
-
ms = Math.max(ms * .8, 0.003);
|
|
388
|
-
}
|
|
389
|
-
if (!firstPart) {
|
|
390
|
-
firstPart = bytes;
|
|
391
|
-
}
|
|
392
|
-
promises.push(__classPrivateFieldGet(this, _FileManager_instances, "m", _FileManager_uploadPart).call(this, fileId, partCount, isBig, part++, bytes, signal).then(() => {
|
|
393
|
-
if (mustTrackProgress) {
|
|
394
|
-
uploaded += bytes.length;
|
|
395
|
-
__classPrivateFieldGet(this, _FileManager_c, "f").handleUpdate({
|
|
396
|
-
uploadProgress: {
|
|
397
|
-
id: String(fileId),
|
|
398
|
-
uploaded,
|
|
399
|
-
total: buffer.length,
|
|
400
|
-
},
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
}));
|
|
404
|
-
if (promises.length === poolSize * UPLOAD_REQUEST_PER_CONNECTION) {
|
|
405
|
-
await Promise.all(promises);
|
|
406
|
-
promises = [];
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
await Promise.all(promises);
|
|
411
|
-
promises = [];
|
|
412
|
-
}
|
|
413
|
-
await Promise.all(promises);
|
|
414
|
-
return { isSmall: !isBig, parts: partCount, firstPart };
|
|
415
|
-
}, _FileManager_uploadPart = async function _FileManager_uploadPart(fileId, partCount, isBig, index, bytes, signal) {
|
|
416
|
-
let retryIn = 1;
|
|
417
|
-
let errorCount = 0;
|
|
418
|
-
while (true) {
|
|
419
|
-
try {
|
|
420
|
-
signal?.throwIfAborted();
|
|
421
|
-
__classPrivateFieldGet(this, _FileManager_Lupload, "f").debug(`[${fileId}] uploading part ` + (index + 1));
|
|
422
|
-
if (isBig) {
|
|
423
|
-
await __classPrivateFieldGet(this, _FileManager_c, "f").invoke({ _: "upload.saveBigFilePart", file_id: fileId, file_part: index, bytes, file_total_parts: partCount }, { type: "upload" });
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
await __classPrivateFieldGet(this, _FileManager_c, "f").invoke({ _: "upload.saveFilePart", file_id: fileId, bytes, file_part: index }, { type: "upload" });
|
|
427
|
-
}
|
|
428
|
-
__classPrivateFieldGet(this, _FileManager_Lupload, "f").debug(`[${fileId}] uploaded part ` + (index + 1));
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
catch (err) {
|
|
432
|
-
signal?.throwIfAborted();
|
|
433
|
-
__classPrivateFieldGet(this, _FileManager_Lupload, "f").debug(`[${fileId}] failed to upload part ` + (index + 1));
|
|
434
|
-
++errorCount;
|
|
435
|
-
if (errorCount > 20) {
|
|
436
|
-
retryIn = 0;
|
|
437
|
-
errorCount = 20;
|
|
438
|
-
}
|
|
439
|
-
await __classPrivateFieldGet(this, _FileManager_instances, "m", _FileManager_handleError).call(this, err, retryIn, `[${fileId}-${index + 1}]`);
|
|
440
|
-
retryIn += 2;
|
|
441
|
-
if (retryIn > 11) {
|
|
442
|
-
retryIn = 11;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}, _FileManager_handleError = async function _FileManager_handleError(err, retryIn, logPrefix) {
|
|
447
|
-
if (err instanceof TimeTooBig || err instanceof TimeTooSmall) {
|
|
448
|
-
throw err;
|
|
449
|
-
}
|
|
450
|
-
else if (err instanceof FloodWait) {
|
|
451
|
-
__classPrivateFieldGet(this, _FileManager_Lupload, "f").warning(`${logPrefix} retrying in ${err.seconds} seconds:`, err);
|
|
452
|
-
await delay(err.seconds * SECOND);
|
|
453
|
-
}
|
|
454
|
-
else if (retryIn > 0) {
|
|
455
|
-
__classPrivateFieldGet(this, _FileManager_Lupload, "f").warning(`${logPrefix} retrying in ${retryIn} seconds:`, err);
|
|
456
|
-
await delay(retryIn * SECOND);
|
|
457
|
-
}
|
|
458
|
-
else {
|
|
459
|
-
throw err;
|
|
460
|
-
}
|
|
461
|
-
}, _FileManager_getFileContents = async function _FileManager_getFileContents(source, params, allowStream) {
|
|
462
|
-
let name = params?.fileName?.trim() || "file";
|
|
463
|
-
let contents;
|
|
464
|
-
let size = -1;
|
|
465
|
-
if (source instanceof Uint8Array) {
|
|
466
|
-
contents = source;
|
|
467
|
-
size = source.byteLength;
|
|
468
|
-
}
|
|
469
|
-
else if (source instanceof ReadableStream) {
|
|
470
|
-
if (!allowStream) {
|
|
471
|
-
throw new InputError("Streamed upload not allowed.");
|
|
472
|
-
}
|
|
473
|
-
contents = source;
|
|
474
|
-
}
|
|
475
|
-
else if (typeof source === "object" && source !== null && (Symbol.iterator in source || Symbol.asyncIterator in source)) {
|
|
476
|
-
if (!allowStream) {
|
|
477
|
-
throw new InputError("Streamed upload not allowed.");
|
|
478
|
-
}
|
|
479
|
-
contents = new ReadableStream({
|
|
480
|
-
pull: Symbol.asyncIterator in source
|
|
481
|
-
? async (controller) => {
|
|
482
|
-
const { value, done } = await source.next();
|
|
483
|
-
done ? controller.close() : controller.enqueue(value);
|
|
484
|
-
}
|
|
485
|
-
: (controller) => {
|
|
486
|
-
const { value, done } = source.next();
|
|
487
|
-
done ? controller.close() : controller.enqueue(value);
|
|
488
|
-
},
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
let url;
|
|
493
|
-
try {
|
|
494
|
-
url = new URL(source).toString();
|
|
495
|
-
}
|
|
496
|
-
catch {
|
|
497
|
-
let path_;
|
|
498
|
-
if (typeof source === "string") {
|
|
499
|
-
if (isAbsolute(source)) {
|
|
500
|
-
path_ = source;
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
// @ts-ignore: lib
|
|
504
|
-
path_ = join(Deno.cwd(), source);
|
|
505
|
-
}
|
|
506
|
-
url = toFileUrl(path_).toString();
|
|
507
|
-
name = basename(path_);
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
unreachable();
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
const response = await fetch(url);
|
|
514
|
-
if (response.body === null) {
|
|
515
|
-
throw new InputError("Invalid response");
|
|
516
|
-
}
|
|
517
|
-
if (name === "file") {
|
|
518
|
-
const contentType = response.headers.get("content-type")?.split(";")[0].trim();
|
|
519
|
-
if (contentType) {
|
|
520
|
-
name += extension(contentType);
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
const maybeFileName = new URL(response.url).pathname.split("/")
|
|
524
|
-
.filter((v) => v)
|
|
525
|
-
.slice(-1)[0]
|
|
526
|
-
.trim();
|
|
527
|
-
if (maybeFileName) {
|
|
528
|
-
name += extension(extname(maybeFileName));
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const contentLength = Number(response.headers.get("content-length"));
|
|
533
|
-
if (contentLength && !isNaN(contentLength)) {
|
|
534
|
-
size = contentLength;
|
|
535
|
-
}
|
|
536
|
-
if (allowStream) {
|
|
537
|
-
contents = response.body;
|
|
538
|
-
}
|
|
539
|
-
else {
|
|
540
|
-
contents = new Uint8Array(await response.arrayBuffer());
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
return { size: params?.fileSize ? params.fileSize : size, name, contents };
|
|
544
|
-
};
|
|
545
|
-
_FileManager_UPLOAD_MAX_CHUNK_SIZE = { value: 512 * kilobyte };
|
|
546
|
-
_FileManager_BIG_FILE_THRESHOLD = { value: 10 * megabyte };
|
|
547
|
-
_FileManager_CUSTOM_EMOJI_TTL = { value: 30 * MINUTE };
|
|
540
|
+
_a = FileManager;
|