untiktok-api 1.0.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE.txt +21 -0
  3. package/README.md +80 -0
  4. package/dist/api/comment.d.ts +42 -0
  5. package/dist/api/comment.js +84 -0
  6. package/dist/api/hashtag.d.ts +52 -0
  7. package/dist/api/hashtag.js +118 -0
  8. package/dist/api/playlist.d.ts +53 -0
  9. package/dist/api/playlist.js +112 -0
  10. package/dist/api/search.d.ts +38 -0
  11. package/dist/api/search.js +98 -0
  12. package/dist/api/sound.d.ts +57 -0
  13. package/dist/api/sound.js +133 -0
  14. package/dist/api/trending.d.ts +21 -0
  15. package/dist/api/trending.js +52 -0
  16. package/dist/api/user.d.ts +187 -0
  17. package/dist/api/user.js +498 -0
  18. package/dist/api/video.d.ts +119 -0
  19. package/dist/api/video.js +399 -0
  20. package/dist/exceptions.d.ts +24 -0
  21. package/dist/exceptions.js +61 -0
  22. package/dist/helpers.d.ts +23 -0
  23. package/dist/helpers.js +66 -0
  24. package/dist/index.d.ts +13 -0
  25. package/dist/index.js +35 -0
  26. package/dist/stealth/index.d.ts +70 -0
  27. package/dist/stealth/index.js +128 -0
  28. package/dist/stealth/js/chrome_app.d.ts +1 -0
  29. package/dist/stealth/js/chrome_app.js +27 -0
  30. package/dist/stealth/js/chrome_csi.d.ts +1 -0
  31. package/dist/stealth/js/chrome_csi.js +14 -0
  32. package/dist/stealth/js/chrome_hairline.d.ts +1 -0
  33. package/dist/stealth/js/chrome_hairline.js +16 -0
  34. package/dist/stealth/js/chrome_load_times.d.ts +1 -0
  35. package/dist/stealth/js/chrome_load_times.js +28 -0
  36. package/dist/stealth/js/chrome_runtime_script.d.ts +1 -0
  37. package/dist/stealth/js/chrome_runtime_script.js +84 -0
  38. package/dist/stealth/js/generate_magic_arrays.d.ts +1 -0
  39. package/dist/stealth/js/generate_magic_arrays.js +28 -0
  40. package/dist/stealth/js/iframe_contentWindow.d.ts +1 -0
  41. package/dist/stealth/js/iframe_contentWindow.js +22 -0
  42. package/dist/stealth/js/media_codecs.d.ts +1 -0
  43. package/dist/stealth/js/media_codecs.js +16 -0
  44. package/dist/stealth/js/navigator_hardwareConcurrency.d.ts +1 -0
  45. package/dist/stealth/js/navigator_hardwareConcurrency.js +6 -0
  46. package/dist/stealth/js/navigator_languages.d.ts +1 -0
  47. package/dist/stealth/js/navigator_languages.js +6 -0
  48. package/dist/stealth/js/navigator_permissions.d.ts +1 -0
  49. package/dist/stealth/js/navigator_permissions.js +11 -0
  50. package/dist/stealth/js/navigator_platform.d.ts +1 -0
  51. package/dist/stealth/js/navigator_platform.js +8 -0
  52. package/dist/stealth/js/navigator_plugins_script.d.ts +1 -0
  53. package/dist/stealth/js/navigator_plugins_script.js +37 -0
  54. package/dist/stealth/js/navigator_userAgent_script.d.ts +1 -0
  55. package/dist/stealth/js/navigator_userAgent_script.js +8 -0
  56. package/dist/stealth/js/navigator_vendor_script.d.ts +1 -0
  57. package/dist/stealth/js/navigator_vendor_script.js +6 -0
  58. package/dist/stealth/js/utils_script.d.ts +1 -0
  59. package/dist/stealth/js/utils_script.js +119 -0
  60. package/dist/stealth/js/webgl_vendor_script.d.ts +1 -0
  61. package/dist/stealth/js/webgl_vendor_script.js +16 -0
  62. package/dist/stealth/js/window_outerdimensions.d.ts +1 -0
  63. package/dist/stealth/js/window_outerdimensions.js +9 -0
  64. package/dist/tiktok.d.ts +96 -0
  65. package/dist/tiktok.js +758 -0
  66. package/dist/types.d.ts +58 -0
  67. package/dist/types.js +6 -0
  68. package/package.json +41 -0
@@ -0,0 +1,498 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // api/user.ts
4
+ // Mirrors TikTokApi/api/user.py
5
+ // ============================================================
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.User = void 0;
8
+ const exceptions_1 = require("../exceptions");
9
+ class User {
10
+ constructor(parent, { username, userId, secUid, data } = {}) {
11
+ this.parent = parent;
12
+ this._updateIdSecUidUsername(userId, secUid, username);
13
+ if (data) {
14
+ this.asDict = data;
15
+ this._extractFromData();
16
+ }
17
+ }
18
+ /**
19
+ * Returns whether the user is currently live on TikTok.
20
+ * Based on the presence of a non-zero roomId in the user info data.
21
+ */
22
+ get isLive() {
23
+ return this.roomId !== null;
24
+ }
25
+ /**
26
+ * Returns the roomId of the user if they are currently live on TikTok.
27
+ * Returns null if they are not live.
28
+ */
29
+ get roomId() {
30
+ const data = this.asDict ?? {};
31
+ // Check inside userInfo.user
32
+ if (data["userInfo"]) {
33
+ const user = data["userInfo"]["user"];
34
+ if (user) {
35
+ if (user["roomId"] && user["roomId"] !== "0" && user["roomId"] !== 0) {
36
+ return String(user["roomId"]);
37
+ }
38
+ if (user["room_id"] && user["room_id"] !== "0" && user["room_id"] !== 0) {
39
+ return String(user["room_id"]);
40
+ }
41
+ }
42
+ }
43
+ // Check root level
44
+ if (data["roomId"] && data["roomId"] !== "0" && data["roomId"] !== 0) {
45
+ return String(data["roomId"]);
46
+ }
47
+ if (data["room_id"] && data["room_id"] !== "0" && data["room_id"] !== 0) {
48
+ return String(data["room_id"]);
49
+ }
50
+ // Check if roomData exists (fallback)
51
+ if (data["roomData"] || data["room_data"]) {
52
+ return "unknown_room_id";
53
+ }
54
+ return null;
55
+ }
56
+ /** Gets the user's display name */
57
+ get nickname() {
58
+ return this._extractUserInfoValue("nickname");
59
+ }
60
+ /** Gets the user's bio / signature */
61
+ get signature() {
62
+ return this._extractUserInfoValue("signature");
63
+ }
64
+ /** Gets whether the user is a verified account */
65
+ get verified() {
66
+ return Boolean(this._extractUserInfoValue("verified"));
67
+ }
68
+ /** Gets whether the user has a private account */
69
+ get isPrivate() {
70
+ return Boolean(this._extractUserInfoValue("privateAccount"));
71
+ }
72
+ /** Gets the user's follower count */
73
+ get followers() {
74
+ return this._extractUserStatsValue("followerCount") || 0;
75
+ }
76
+ /** Gets the user's following count */
77
+ get following() {
78
+ return this._extractUserStatsValue("followingCount") || 0;
79
+ }
80
+ /** Gets the user's total likes (hearts) */
81
+ get likes() {
82
+ return this._extractUserStatsValue("heartCount") || 0;
83
+ }
84
+ /** Gets the user's total video count */
85
+ get videoCount() {
86
+ return this._extractUserStatsValue("videoCount") || 0;
87
+ }
88
+ /** Gets the user's link in bio */
89
+ get bioLink() {
90
+ const bioLinkObj = this._extractUserInfoValue("bioLink");
91
+ return bioLinkObj?.link ?? null;
92
+ }
93
+ /** Gets the user's profile picture URL (largest available) */
94
+ get avatar() {
95
+ return this._extractUserInfoValue("avatarLarger") ||
96
+ this._extractUserInfoValue("avatarMedium") ||
97
+ this._extractUserInfoValue("avatarThumb") || null;
98
+ }
99
+ _extractUserInfoValue(key) {
100
+ const data = this.asDict ?? {};
101
+ if (data["userInfo"]) {
102
+ const user = data["userInfo"]["user"];
103
+ if (user && user[key] !== undefined)
104
+ return user[key];
105
+ }
106
+ return data[key] ?? null;
107
+ }
108
+ _extractUserStatsValue(key) {
109
+ const data = this.asDict ?? {};
110
+ if (data["userInfo"]) {
111
+ const stats = data["userInfo"]["stats"];
112
+ if (stats && stats[key] !== undefined)
113
+ return stats[key];
114
+ }
115
+ if (data["stats"]) {
116
+ const stats = data["stats"];
117
+ if (stats[key] !== undefined)
118
+ return stats[key];
119
+ }
120
+ return data[key] ?? null;
121
+ }
122
+ /**
123
+ * Returns a dictionary of information associated with this User.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const userData = await api.user({ username: 'therock' }).info();
128
+ * ```
129
+ */
130
+ async info(kwargs = {}) {
131
+ const username = this.username;
132
+ if (!username) {
133
+ throw new TypeError("You must provide the username when creating this class to use this method.");
134
+ }
135
+ const urlParams = {
136
+ secUid: this.secUid ?? "",
137
+ uniqueId: username,
138
+ msToken: kwargs.msToken,
139
+ };
140
+ const resp = await this.parent.makeRequest({
141
+ url: "https://www.tiktok.com/api/user/detail/",
142
+ params: urlParams,
143
+ headers: kwargs.headers,
144
+ sessionIndex: kwargs.sessionIndex,
145
+ });
146
+ if (resp == null) {
147
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
148
+ }
149
+ this.asDict = resp;
150
+ this._extractFromData();
151
+ return resp;
152
+ }
153
+ /**
154
+ * Returns a user's playlists.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * for await (const playlist of api.user({ username: 'therock' }).playlists()) {
159
+ * console.log(playlist.name);
160
+ * }
161
+ * ```
162
+ */
163
+ async *playlists(count = 20, cursor = 0, kwargs = {}) {
164
+ if (!this.secUid) {
165
+ await this.info(kwargs);
166
+ }
167
+ let found = 0;
168
+ while (found < count) {
169
+ const params = {
170
+ secUid: this.secUid,
171
+ count: Math.min(count, 30),
172
+ cursor,
173
+ };
174
+ const resp = await this.parent.makeRequest({
175
+ url: "https://www.tiktok.com/api/user/playlist",
176
+ params,
177
+ headers: kwargs.headers,
178
+ sessionIndex: kwargs.sessionIndex,
179
+ });
180
+ if (resp == null) {
181
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
182
+ }
183
+ const playList = resp["playList"] ?? [];
184
+ for (const pl of playList) {
185
+ yield this.parent.playlist({ data: pl });
186
+ found++;
187
+ }
188
+ if (!resp["hasMore"])
189
+ return;
190
+ cursor = resp["cursor"];
191
+ }
192
+ }
193
+ /**
194
+ * Returns a user's videos.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * for await (const video of api.user({ username: 'davidteathercodes' }).videos()) {
199
+ * console.log(video.id);
200
+ * }
201
+ * ```
202
+ */
203
+ async *videos(count = 30, cursor = 0, kwargs = {}) {
204
+ if (!this.secUid) {
205
+ await this.info(kwargs);
206
+ }
207
+ let found = 0;
208
+ while (found < count) {
209
+ const params = {
210
+ secUid: this.secUid,
211
+ count: 30,
212
+ cursor,
213
+ };
214
+ const resp = await this.parent.makeRequest({
215
+ url: "https://www.tiktok.com/api/post/item_list/",
216
+ params,
217
+ headers: kwargs.headers,
218
+ sessionIndex: kwargs.sessionIndex,
219
+ });
220
+ if (resp == null) {
221
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
222
+ }
223
+ const itemList = resp["itemList"] ?? [];
224
+ for (const item of itemList) {
225
+ yield this.parent.video({ data: item });
226
+ found++;
227
+ }
228
+ if (!resp["hasMore"])
229
+ return;
230
+ cursor = resp["cursor"];
231
+ }
232
+ }
233
+ /**
234
+ * Returns a user's pinned videos.
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * for await (const video of api.user({ username: 'davidteathercodes' }).pinned()) {
239
+ * console.log(video.id);
240
+ * }
241
+ * ```
242
+ */
243
+ async *pinned(count = 3, kwargs = {}) {
244
+ // Pinned videos are always sent at the top of the videos feed
245
+ // We fetch a batch of videos and explicitly filter for pinned flags
246
+ let found = 0;
247
+ // We only need to check the first few videos since pinned are always at top
248
+ for await (const video of this.videos(10, 0, kwargs)) {
249
+ const data = video.asDict ?? {};
250
+ const isPinned = data["isPinned"] === true ||
251
+ data["is_pinned"] === true ||
252
+ data["isTop"] === true ||
253
+ data["is_top"] === true ||
254
+ data["isTopItem"] === true ||
255
+ data["is_top_item"] === true;
256
+ if (isPinned) {
257
+ yield video;
258
+ found++;
259
+ if (found >= count)
260
+ break;
261
+ }
262
+ }
263
+ }
264
+ /**
265
+ * Returns a user's liked posts (if public).
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * for await (const like of api.user({ username: 'davidteathercodes' }).liked()) {
270
+ * console.log(like.id);
271
+ * }
272
+ * ```
273
+ */
274
+ async *liked(count = 30, cursor = 0, kwargs = {}) {
275
+ if (!this.secUid) {
276
+ await this.info(kwargs);
277
+ }
278
+ let found = 0;
279
+ while (found < count) {
280
+ const params = {
281
+ secUid: this.secUid,
282
+ count: 30,
283
+ cursor,
284
+ };
285
+ const resp = await this.parent.makeRequest({
286
+ url: "https://www.tiktok.com/api/favorite/item_list",
287
+ params,
288
+ headers: kwargs.headers,
289
+ sessionIndex: kwargs.sessionIndex,
290
+ });
291
+ if (resp == null) {
292
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
293
+ }
294
+ const itemList = resp["itemList"] ?? [];
295
+ for (const item of itemList) {
296
+ yield this.parent.video({ data: item });
297
+ found++;
298
+ }
299
+ if (!resp["hasMore"])
300
+ return;
301
+ cursor = resp["cursor"];
302
+ }
303
+ }
304
+ /**
305
+ * Returns a user's reposted videos (if available).
306
+ * Note: TikTok might restrict visibility based on authentication or region.
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * for await (const repost of api.user({ username: 'davidteathercodes' }).reposts()) {
311
+ * console.log(repost.id);
312
+ * }
313
+ * ```
314
+ */
315
+ async *reposts(count = 30, cursor = 0, kwargs = {}) {
316
+ if (!this.secUid) {
317
+ await this.info(kwargs);
318
+ }
319
+ // "well now you can stalk your crush repost without knowing" - Al Ghozali Ramadhan
320
+ let found = 0;
321
+ while (found < count) {
322
+ const params = {
323
+ secUid: this.secUid,
324
+ count: 30,
325
+ cursor,
326
+ };
327
+ const resp = await this.parent.makeRequest({
328
+ url: "https://www.tiktok.com/api/repost/item_list/",
329
+ params,
330
+ headers: kwargs.headers,
331
+ sessionIndex: kwargs.sessionIndex,
332
+ });
333
+ if (resp == null) {
334
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
335
+ }
336
+ const itemList = resp["itemList"] ?? [];
337
+ for (const item of itemList) {
338
+ yield this.parent.video({ data: item });
339
+ found++;
340
+ }
341
+ if (!resp["hasMore"])
342
+ return;
343
+ cursor = resp["cursor"];
344
+ }
345
+ }
346
+ /**
347
+ * Returns a user's favorited/bookmarked videos (Collections).
348
+ * Note: This relies entirely on the user's privacy settings.
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * for await (const fav of api.user({ username: 'davidteathercodes' }).favorited()) {
353
+ * console.log(fav.id);
354
+ * }
355
+ * ```
356
+ */
357
+ async *favorited(count = 30, cursor = 0, kwargs = {}) {
358
+ if (!this.secUid) {
359
+ await this.info(kwargs);
360
+ }
361
+ let found = 0;
362
+ while (found < count) {
363
+ const params = {
364
+ secUid: this.secUid,
365
+ count: 30,
366
+ cursor,
367
+ };
368
+ const resp = await this.parent.makeRequest({
369
+ url: "https://www.tiktok.com/api/user/collect/item_list/",
370
+ params,
371
+ headers: kwargs.headers,
372
+ sessionIndex: kwargs.sessionIndex,
373
+ });
374
+ if (resp == null) {
375
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
376
+ }
377
+ const itemList = resp["itemList"] ?? [];
378
+ for (const item of itemList) {
379
+ yield this.parent.video({ data: item });
380
+ found++;
381
+ }
382
+ if (!resp["hasMore"])
383
+ return;
384
+ cursor = resp["cursor"];
385
+ }
386
+ }
387
+ /**
388
+ * Returns a user's followers list.
389
+ * Note: This endpoint is heavily guarded and usually requires a logged-in session (cookies).
390
+ * It may also quickly return errors or bot challenges.
391
+ *
392
+ * @example
393
+ * ```ts
394
+ * for await (const follower of api.user({ username: 'davidteathercodes' }).followersList()) {
395
+ * console.log(follower.username);
396
+ * }
397
+ * ```
398
+ */
399
+ async *followersList(count = 30, cursor = 0, kwargs = {}) {
400
+ if (!this.secUid) {
401
+ await this.info(kwargs);
402
+ }
403
+ let found = 0;
404
+ while (found < count) {
405
+ const params = {
406
+ secUid: this.secUid,
407
+ count: 30,
408
+ minCursor: cursor,
409
+ maxCursor: cursor,
410
+ };
411
+ const resp = await this.parent.makeRequest({
412
+ url: "https://www.tiktok.com/api/user/list/",
413
+ params,
414
+ headers: kwargs.headers,
415
+ sessionIndex: kwargs.sessionIndex,
416
+ });
417
+ if (resp == null) {
418
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
419
+ }
420
+ const userList = resp["userList"] ?? [];
421
+ for (const item of userList) {
422
+ yield this.parent.user({ data: item });
423
+ found++;
424
+ }
425
+ if (!resp["hasMore"])
426
+ return;
427
+ cursor = resp["minCursor"] || resp["maxCursor"];
428
+ }
429
+ }
430
+ /**
431
+ * Returns a user's following list.
432
+ * Note: Like followers, this is heavily guarded and requires authentication.
433
+ *
434
+ * @example
435
+ * ```ts
436
+ * for await (const following of api.user({ username: 'davidteathercodes' }).followingList()) {
437
+ * console.log(following.username);
438
+ * }
439
+ * ```
440
+ */
441
+ async *followingList(count = 30, cursor = 0, kwargs = {}) {
442
+ if (!this.secUid) {
443
+ await this.info(kwargs);
444
+ }
445
+ let found = 0;
446
+ while (found < count) {
447
+ const params = {
448
+ secUid: this.secUid,
449
+ count: 30,
450
+ minCursor: cursor,
451
+ maxCursor: cursor,
452
+ };
453
+ const resp = await this.parent.makeRequest({
454
+ url: "https://www.tiktok.com/api/user/following/",
455
+ params,
456
+ headers: kwargs.headers,
457
+ sessionIndex: kwargs.sessionIndex,
458
+ });
459
+ if (resp == null) {
460
+ throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
461
+ }
462
+ const userList = resp["userList"] ?? [];
463
+ for (const item of userList) {
464
+ yield this.parent.user({ data: item });
465
+ found++;
466
+ }
467
+ if (!resp["hasMore"])
468
+ return;
469
+ cursor = resp["minCursor"] || resp["maxCursor"];
470
+ }
471
+ }
472
+ _extractFromData() {
473
+ const data = this.asDict ?? {};
474
+ const keys = Object.keys(data);
475
+ if (keys.includes("userInfo")) {
476
+ const userInfo = data["userInfo"]["user"];
477
+ this._updateIdSecUidUsername(userInfo["id"], userInfo["secUid"], userInfo["uniqueId"]);
478
+ }
479
+ else {
480
+ this._updateIdSecUidUsername(data["id"], data["secUid"], data["uniqueId"]);
481
+ }
482
+ if (!this.username || !this.userId || !this.secUid) {
483
+ this.parent.logger.error(`Failed to create User with data: ${JSON.stringify(data)}`);
484
+ }
485
+ }
486
+ _updateIdSecUidUsername(id, secUid, username) {
487
+ if (id != null)
488
+ this.userId = id;
489
+ if (secUid != null)
490
+ this.secUid = secUid;
491
+ if (username != null)
492
+ this.username = username;
493
+ }
494
+ toString() {
495
+ return `TikTokApi.user(username='${this.username}', user_id='${this.userId}', sec_uid='${this.secUid}')`;
496
+ }
497
+ }
498
+ exports.User = User;
@@ -0,0 +1,119 @@
1
+ import type { TikTokApi } from "../tiktok";
2
+ import type { User } from "./user";
3
+ import type { Sound } from "./sound";
4
+ import type { Hashtag } from "./hashtag";
5
+ import type { Comment } from "./comment";
6
+ export interface VideoOptions {
7
+ id?: string | null;
8
+ url?: string | null;
9
+ data?: Record<string, unknown> | null;
10
+ sessionIndex?: number;
11
+ proxy?: string | null;
12
+ }
13
+ export declare class Video {
14
+ /** Static reference to the parent TikTokApi instance */
15
+ parent: TikTokApi;
16
+ /** TikTok's ID of the Video */
17
+ id?: string;
18
+ /** The URL of the Video */
19
+ url?: string;
20
+ /** The creation time of the Video */
21
+ createTime?: Date;
22
+ /** TikTok's stats for the Video */
23
+ stats?: Record<string, unknown>;
24
+ /** The User who created the Video */
25
+ author?: User;
26
+ /** The Sound associated with the Video */
27
+ sound?: Sound;
28
+ /** A list of Hashtags on the Video */
29
+ hashtags?: Hashtag[];
30
+ /** The raw data associated with this Video */
31
+ asDict?: Record<string, unknown>;
32
+ /** Gets the description/caption of the video */
33
+ get description(): string | null;
34
+ /** Gets the play/view count of the video */
35
+ get plays(): number;
36
+ /** Gets the digg/like count of the video */
37
+ get likes(): number;
38
+ /** Gets the comment count of the video */
39
+ get commentsCount(): number;
40
+ /** Gets the share count of the video */
41
+ get shares(): number;
42
+ /** Gets the collect/save count of the video */
43
+ get saves(): number;
44
+ /** Gets whether the video is pinned by the creator */
45
+ get isPinned(): boolean;
46
+ constructor(parent: TikTokApi, { id, url, data, sessionIndex, proxy }?: VideoOptions);
47
+ /**
48
+ * Async factory that follows redirects to resolve the video ID from a short URL.
49
+ * Use this instead of `new Video({ url })` when you have a short/redirect URL.
50
+ */
51
+ static fromUrl(parent: TikTokApi, url: string, kwargs?: {
52
+ sessionIndex?: number;
53
+ proxy?: string;
54
+ }): Promise<Video>;
55
+ /**
56
+ * Returns a dictionary of all data associated with a TikTok Video.
57
+ * Note: This is slow since it requires an HTTP request.
58
+ *
59
+ * Python uses `requests.get`; TS uses `axios.get` (equivalent sync-style).
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const info = await api.video({ url: 'https://www.tiktok.com/@.../video/...' }).info();
64
+ * ```
65
+ */
66
+ info(kwargs?: {
67
+ headers?: Record<string, string>;
68
+ sessionIndex?: number;
69
+ proxy?: string;
70
+ }): Promise<Record<string, unknown>>;
71
+ /**
72
+ * Returns the raw bytes of a TikTok Video.
73
+ *
74
+ * Python uses `requests.get` / `httpx.AsyncClient` for streaming.
75
+ * TS uses `axios` equivalents.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const buf = await video.bytes() as Buffer;
80
+ * fs.writeFileSync('video.mp4', buf);
81
+ *
82
+ * // Streaming
83
+ * for await (const chunk of await video.bytes({ stream: true }) as AsyncGenerator<Buffer>) { ... }
84
+ * ```
85
+ */
86
+ bytes(options?: {
87
+ stream?: boolean;
88
+ } & Record<string, unknown>): Promise<Buffer | AsyncGenerator<Buffer>>;
89
+ /**
90
+ * Returns the comments of a TikTok Video.
91
+ *
92
+ * Python key: `has_more` (snake_case) — preserved in TS.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * for await (const comment of video.comments(20)) { ... }
97
+ * ```
98
+ */
99
+ comments(count?: number, cursor?: number, kwargs?: {
100
+ headers?: Record<string, string>;
101
+ sessionIndex?: number;
102
+ }): AsyncGenerator<Comment>;
103
+ /**
104
+ * Returns related videos of a TikTok Video.
105
+ * Note: Python's related_videos does NOT increment `found` after the inner loop — bug or intentional.
106
+ * We preserve that exact behaviour (no double-increment).
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * for await (const rel of video.relatedVideos(30)) { ... }
111
+ * ```
112
+ */
113
+ relatedVideos(count?: number, cursor?: number, kwargs?: {
114
+ headers?: Record<string, string>;
115
+ sessionIndex?: number;
116
+ }): AsyncGenerator<Video>;
117
+ private _extractFromData;
118
+ toString(): string;
119
+ }