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.
- package/LICENSE +21 -0
- package/LICENSE.txt +21 -0
- package/README.md +80 -0
- package/dist/api/comment.d.ts +42 -0
- package/dist/api/comment.js +84 -0
- package/dist/api/hashtag.d.ts +52 -0
- package/dist/api/hashtag.js +118 -0
- package/dist/api/playlist.d.ts +53 -0
- package/dist/api/playlist.js +112 -0
- package/dist/api/search.d.ts +38 -0
- package/dist/api/search.js +98 -0
- package/dist/api/sound.d.ts +57 -0
- package/dist/api/sound.js +133 -0
- package/dist/api/trending.d.ts +21 -0
- package/dist/api/trending.js +52 -0
- package/dist/api/user.d.ts +187 -0
- package/dist/api/user.js +498 -0
- package/dist/api/video.d.ts +119 -0
- package/dist/api/video.js +399 -0
- package/dist/exceptions.d.ts +24 -0
- package/dist/exceptions.js +61 -0
- package/dist/helpers.d.ts +23 -0
- package/dist/helpers.js +66 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +35 -0
- package/dist/stealth/index.d.ts +70 -0
- package/dist/stealth/index.js +128 -0
- package/dist/stealth/js/chrome_app.d.ts +1 -0
- package/dist/stealth/js/chrome_app.js +27 -0
- package/dist/stealth/js/chrome_csi.d.ts +1 -0
- package/dist/stealth/js/chrome_csi.js +14 -0
- package/dist/stealth/js/chrome_hairline.d.ts +1 -0
- package/dist/stealth/js/chrome_hairline.js +16 -0
- package/dist/stealth/js/chrome_load_times.d.ts +1 -0
- package/dist/stealth/js/chrome_load_times.js +28 -0
- package/dist/stealth/js/chrome_runtime_script.d.ts +1 -0
- package/dist/stealth/js/chrome_runtime_script.js +84 -0
- package/dist/stealth/js/generate_magic_arrays.d.ts +1 -0
- package/dist/stealth/js/generate_magic_arrays.js +28 -0
- package/dist/stealth/js/iframe_contentWindow.d.ts +1 -0
- package/dist/stealth/js/iframe_contentWindow.js +22 -0
- package/dist/stealth/js/media_codecs.d.ts +1 -0
- package/dist/stealth/js/media_codecs.js +16 -0
- package/dist/stealth/js/navigator_hardwareConcurrency.d.ts +1 -0
- package/dist/stealth/js/navigator_hardwareConcurrency.js +6 -0
- package/dist/stealth/js/navigator_languages.d.ts +1 -0
- package/dist/stealth/js/navigator_languages.js +6 -0
- package/dist/stealth/js/navigator_permissions.d.ts +1 -0
- package/dist/stealth/js/navigator_permissions.js +11 -0
- package/dist/stealth/js/navigator_platform.d.ts +1 -0
- package/dist/stealth/js/navigator_platform.js +8 -0
- package/dist/stealth/js/navigator_plugins_script.d.ts +1 -0
- package/dist/stealth/js/navigator_plugins_script.js +37 -0
- package/dist/stealth/js/navigator_userAgent_script.d.ts +1 -0
- package/dist/stealth/js/navigator_userAgent_script.js +8 -0
- package/dist/stealth/js/navigator_vendor_script.d.ts +1 -0
- package/dist/stealth/js/navigator_vendor_script.js +6 -0
- package/dist/stealth/js/utils_script.d.ts +1 -0
- package/dist/stealth/js/utils_script.js +119 -0
- package/dist/stealth/js/webgl_vendor_script.d.ts +1 -0
- package/dist/stealth/js/webgl_vendor_script.js +16 -0
- package/dist/stealth/js/window_outerdimensions.d.ts +1 -0
- package/dist/stealth/js/window_outerdimensions.js +9 -0
- package/dist/tiktok.d.ts +96 -0
- package/dist/tiktok.js +758 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.js +6 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 David Teather
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 David Teather
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# UnTikTok-Api
|
|
2
|
+
|
|
3
|
+
> An unofficial TikTok API wrapper in TypeScript, originally ported from the [TikTok-Api](https://github.com/davidteather/TikTok-Api) Python library.
|
|
4
|
+
>
|
|
5
|
+
> **Disclaimer:** This project is not affiliated with, endorsed by, or connected to TikTok, ByteDance, or the original author of the Python TikTok-Api. It is an independent, open-source TypeScript port designed for integration into Node.js applications and AI tools.
|
|
6
|
+
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://playwright.dev/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [API Reference](#api-reference)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [Common Issues](#common-issues)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
This API allows you to extract and automate interactions with TikTok data without requiring an official developer API key. It achieves this by using Playwright and stealth scripts to interface directly with TikTok's web endpoints.
|
|
27
|
+
|
|
28
|
+
**Capabilities include:**
|
|
29
|
+
- **Trending Feeds:** Fetch the most viral and trending videos on the platform.
|
|
30
|
+
- **User Profiles:** Retrieve a user's uploaded videos, liked videos, **favorited videos**, **reposted videos**, **pinned videos**, **followers/following lists**, profile information, and **live stream status**.
|
|
31
|
+
- **Hashtags:** Fetch videos under specific hashtags.
|
|
32
|
+
- **Search:** Search for specific users or videos by keyword.
|
|
33
|
+
- **Comments:** Extract comments and replies from specific videos, including support for **TikTok Stickers**.
|
|
34
|
+
- **Sounds/Music:** Retrieve videos associated with a specific audio track or sound.
|
|
35
|
+
- **Downloads:** Download raw video bytes (without watermarks) and audio streams directly.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
First, clone the repository and navigate into the project directory:
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/AlGhozaliRamadhan/UnTikTok-Api.git
|
|
44
|
+
cd UnTikTok-Api
|
|
45
|
+
npm install
|
|
46
|
+
npx playwright install chromium
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
TypeScript check (no emit):
|
|
50
|
+
```bash
|
|
51
|
+
npm run typecheck
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Build to `dist/`:
|
|
55
|
+
```bash
|
|
56
|
+
npm run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
Looking to get started immediately? Check out the [Quick Start Guide](./docs/quick_start.md).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
For a full list of classes, methods, and constructor options, check out the [API Reference](./docs/api_reference.md).
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Examples & Guides
|
|
72
|
+
|
|
73
|
+
For detailed examples and tutorials on how to use specific features, check out the dedicated guides in the `docs/` folder:
|
|
74
|
+
|
|
75
|
+
- [Trending Videos](./docs/trending.md)
|
|
76
|
+
- [User Data (Videos, Likes, Reposts) 🆕](./docs/user.md)
|
|
77
|
+
- [Hashtag Videos](./docs/hashtag.md)
|
|
78
|
+
- [Search Users](./docs/search.md)
|
|
79
|
+
- [Download Videos](./docs/download.md)
|
|
80
|
+
- [Session Caching (Bot evasion)](./docs/session_caching.md)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { TikTokApi } from "../tiktok";
|
|
2
|
+
import type { User } from "./user";
|
|
3
|
+
export declare class Comment {
|
|
4
|
+
/** Static reference to the parent TikTokApi instance */
|
|
5
|
+
parent: TikTokApi;
|
|
6
|
+
/** The id of the comment */
|
|
7
|
+
id: string;
|
|
8
|
+
/** The author of the comment */
|
|
9
|
+
author: User;
|
|
10
|
+
/** The text content of the comment */
|
|
11
|
+
text: string;
|
|
12
|
+
/** The number of likes on the comment */
|
|
13
|
+
likesCount: number;
|
|
14
|
+
/** The raw data associated with this comment */
|
|
15
|
+
asDict: Record<string, unknown>;
|
|
16
|
+
constructor(parent: TikTokApi, data?: Record<string, unknown>);
|
|
17
|
+
private _extractFromData;
|
|
18
|
+
/**
|
|
19
|
+
* Returns whether this comment contains a TikTok sticker.
|
|
20
|
+
* Note: The sticker image URL is not retrievable via this endpoint.
|
|
21
|
+
*/
|
|
22
|
+
get isSticker(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the comment text with the "[Sticker]" placeholder stripped.
|
|
25
|
+
*/
|
|
26
|
+
get stickerText(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Returns reply comments for this comment.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* for await (const reply of comment.replies()) {
|
|
33
|
+
* console.log(reply.text);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
replies(count?: number, cursor?: number, kwargs?: {
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
sessionIndex?: number;
|
|
40
|
+
}): AsyncGenerator<Comment>;
|
|
41
|
+
toString(): string;
|
|
42
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// api/comment.ts
|
|
4
|
+
// Mirrors TikTokApi/api/comment.py
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.Comment = void 0;
|
|
8
|
+
const exceptions_1 = require("../exceptions");
|
|
9
|
+
class Comment {
|
|
10
|
+
constructor(parent, data) {
|
|
11
|
+
this.parent = parent;
|
|
12
|
+
this.asDict = data ?? {};
|
|
13
|
+
if (data) {
|
|
14
|
+
this._extractFromData();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
_extractFromData() {
|
|
18
|
+
const data = this.asDict;
|
|
19
|
+
this.id = data["cid"];
|
|
20
|
+
this.text = data["text"];
|
|
21
|
+
const usr = data["user"];
|
|
22
|
+
this.author = this.parent.user({
|
|
23
|
+
userId: usr["uid"],
|
|
24
|
+
username: usr["unique_id"],
|
|
25
|
+
secUid: usr["sec_uid"],
|
|
26
|
+
});
|
|
27
|
+
this.likesCount = data["digg_count"];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns whether this comment contains a TikTok sticker.
|
|
31
|
+
* Note: The sticker image URL is not retrievable via this endpoint.
|
|
32
|
+
*/
|
|
33
|
+
get isSticker() {
|
|
34
|
+
return (this.text || "").includes("[Sticker]");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the comment text with the "[Sticker]" placeholder stripped.
|
|
38
|
+
*/
|
|
39
|
+
get stickerText() {
|
|
40
|
+
return (this.text || "").replace(/\[Sticker\]/g, "").trim();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns reply comments for this comment.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* for await (const reply of comment.replies()) {
|
|
48
|
+
* console.log(reply.text);
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
async *replies(count = 20, cursor = 0, kwargs = {}) {
|
|
53
|
+
let found = 0;
|
|
54
|
+
while (found < count) {
|
|
55
|
+
const params = {
|
|
56
|
+
count: 20,
|
|
57
|
+
cursor,
|
|
58
|
+
item_id: this.author.userId,
|
|
59
|
+
comment_id: this.id,
|
|
60
|
+
};
|
|
61
|
+
const resp = await this.parent.makeRequest({
|
|
62
|
+
url: "https://www.tiktok.com/api/comment/list/reply/",
|
|
63
|
+
params,
|
|
64
|
+
headers: kwargs.headers,
|
|
65
|
+
sessionIndex: kwargs.sessionIndex,
|
|
66
|
+
});
|
|
67
|
+
if (resp == null) {
|
|
68
|
+
throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
|
|
69
|
+
}
|
|
70
|
+
const comments = resp["comments"] ?? [];
|
|
71
|
+
for (const comment of comments) {
|
|
72
|
+
yield this.parent.comment({ data: comment });
|
|
73
|
+
found++;
|
|
74
|
+
}
|
|
75
|
+
if (!resp["has_more"])
|
|
76
|
+
return;
|
|
77
|
+
cursor = resp["cursor"];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
toString() {
|
|
81
|
+
return `TikTokApi.comment(comment_id='${this.id}', text='${this.text}')`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.Comment = Comment;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { TikTokApi } from "../tiktok";
|
|
2
|
+
import type { Video } from "./video";
|
|
3
|
+
export interface HashtagOptions {
|
|
4
|
+
name?: string | null;
|
|
5
|
+
id?: string | null;
|
|
6
|
+
data?: Record<string, unknown> | null;
|
|
7
|
+
}
|
|
8
|
+
export declare class Hashtag {
|
|
9
|
+
/** Static reference to the parent TikTokApi instance */
|
|
10
|
+
parent: TikTokApi;
|
|
11
|
+
/** The ID of the hashtag */
|
|
12
|
+
id?: string;
|
|
13
|
+
/** The name of the hashtag (without #) */
|
|
14
|
+
name?: string;
|
|
15
|
+
/** The raw data associated with this hashtag */
|
|
16
|
+
asDict?: Record<string, unknown>;
|
|
17
|
+
/** Additional split name if available */
|
|
18
|
+
splitName?: string;
|
|
19
|
+
/** Stats if available */
|
|
20
|
+
stats?: Record<string, unknown>;
|
|
21
|
+
constructor(parent: TikTokApi, { name, id, data }?: HashtagOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Returns all information sent by TikTok related to this hashtag.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const hashtag = api.hashtag({ name: 'funny' });
|
|
28
|
+
* const hashtagData = await hashtag.info();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
info(kwargs?: {
|
|
32
|
+
msToken?: string;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
sessionIndex?: number;
|
|
35
|
+
}): Promise<Record<string, unknown>>;
|
|
36
|
+
/**
|
|
37
|
+
* Returns TikTok videos with this hashtag.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* for await (const video of api.hashtag({ name: 'funny' }).videos()) {
|
|
42
|
+
* console.log(video.id);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
videos(count?: number, cursor?: number, kwargs?: {
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
sessionIndex?: number;
|
|
49
|
+
}): AsyncGenerator<Video>;
|
|
50
|
+
private _extractFromData;
|
|
51
|
+
toString(): string;
|
|
52
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// api/hashtag.ts
|
|
4
|
+
// Mirrors TikTokApi/api/hashtag.py
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.Hashtag = void 0;
|
|
8
|
+
const exceptions_1 = require("../exceptions");
|
|
9
|
+
class Hashtag {
|
|
10
|
+
constructor(parent, { name, id, data } = {}) {
|
|
11
|
+
this.parent = parent;
|
|
12
|
+
if (name != null)
|
|
13
|
+
this.name = name;
|
|
14
|
+
if (id != null)
|
|
15
|
+
this.id = id;
|
|
16
|
+
if (data) {
|
|
17
|
+
this.asDict = data;
|
|
18
|
+
this._extractFromData();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns all information sent by TikTok related to this hashtag.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const hashtag = api.hashtag({ name: 'funny' });
|
|
27
|
+
* const hashtagData = await hashtag.info();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
async info(kwargs = {}) {
|
|
31
|
+
if (!this.name) {
|
|
32
|
+
throw new TypeError("You must provide the name when creating this class to use this method.");
|
|
33
|
+
}
|
|
34
|
+
const urlParams = {
|
|
35
|
+
challengeName: this.name,
|
|
36
|
+
msToken: kwargs.msToken,
|
|
37
|
+
};
|
|
38
|
+
const resp = await this.parent.makeRequest({
|
|
39
|
+
url: "https://www.tiktok.com/api/challenge/detail/",
|
|
40
|
+
params: urlParams,
|
|
41
|
+
headers: kwargs.headers,
|
|
42
|
+
sessionIndex: kwargs.sessionIndex,
|
|
43
|
+
});
|
|
44
|
+
if (resp == null) {
|
|
45
|
+
throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
|
|
46
|
+
}
|
|
47
|
+
this.asDict = resp;
|
|
48
|
+
this._extractFromData();
|
|
49
|
+
return resp;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns TikTok videos with this hashtag.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* for await (const video of api.hashtag({ name: 'funny' }).videos()) {
|
|
57
|
+
* console.log(video.id);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
async *videos(count = 30, cursor = 0, kwargs = {}) {
|
|
62
|
+
if (!this.id) {
|
|
63
|
+
await this.info(kwargs);
|
|
64
|
+
}
|
|
65
|
+
let found = 0;
|
|
66
|
+
while (found < count) {
|
|
67
|
+
const params = {
|
|
68
|
+
challengeID: this.id,
|
|
69
|
+
count: 30,
|
|
70
|
+
cursor,
|
|
71
|
+
};
|
|
72
|
+
const resp = await this.parent.makeRequest({
|
|
73
|
+
url: "https://www.tiktok.com/api/challenge/item_list/",
|
|
74
|
+
params,
|
|
75
|
+
headers: kwargs.headers,
|
|
76
|
+
sessionIndex: kwargs.sessionIndex,
|
|
77
|
+
});
|
|
78
|
+
if (resp == null) {
|
|
79
|
+
throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
|
|
80
|
+
}
|
|
81
|
+
const itemList = resp["itemList"] ?? [];
|
|
82
|
+
for (const item of itemList) {
|
|
83
|
+
yield this.parent.video({ data: item });
|
|
84
|
+
found++;
|
|
85
|
+
}
|
|
86
|
+
if (!resp["hasMore"])
|
|
87
|
+
return;
|
|
88
|
+
cursor = resp["cursor"];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
_extractFromData() {
|
|
92
|
+
const data = this.asDict ?? {};
|
|
93
|
+
const keys = Object.keys(data);
|
|
94
|
+
if (keys.includes("title")) {
|
|
95
|
+
this.id = data["id"];
|
|
96
|
+
this.name = data["title"];
|
|
97
|
+
}
|
|
98
|
+
if (keys.includes("challengeInfo")) {
|
|
99
|
+
const challengeInfo = data["challengeInfo"];
|
|
100
|
+
if (challengeInfo["challenge"]) {
|
|
101
|
+
const challenge = challengeInfo["challenge"];
|
|
102
|
+
this.id = challenge["id"];
|
|
103
|
+
this.name = challenge["title"];
|
|
104
|
+
this.splitName = challenge["splitTitle"];
|
|
105
|
+
}
|
|
106
|
+
if (challengeInfo["stats"]) {
|
|
107
|
+
this.stats = challengeInfo["stats"];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!this.id || !this.name) {
|
|
111
|
+
this.parent.logger.error(`Failed to create Hashtag with data: ${JSON.stringify(data)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
toString() {
|
|
115
|
+
return `TikTokApi.hashtag(id='${this.id}', name='${this.name}')`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.Hashtag = Hashtag;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { TikTokApi } from "../tiktok";
|
|
2
|
+
import type { Video } from "./video";
|
|
3
|
+
import type { User } from "./user";
|
|
4
|
+
export interface PlaylistOptions {
|
|
5
|
+
id?: string | null;
|
|
6
|
+
data?: Record<string, unknown> | null;
|
|
7
|
+
}
|
|
8
|
+
export declare class Playlist {
|
|
9
|
+
/** Static reference to the parent TikTokApi instance */
|
|
10
|
+
parent: TikTokApi;
|
|
11
|
+
/** The ID of the playlist */
|
|
12
|
+
id?: string;
|
|
13
|
+
/** The name of the playlist */
|
|
14
|
+
name?: string;
|
|
15
|
+
/** The video count of the playlist */
|
|
16
|
+
videoCount?: number;
|
|
17
|
+
/** The creator of the playlist */
|
|
18
|
+
creator?: User;
|
|
19
|
+
/** The cover URL of the playlist */
|
|
20
|
+
coverUrl?: string;
|
|
21
|
+
/** The raw data associated with this playlist */
|
|
22
|
+
asDict?: Record<string, unknown>;
|
|
23
|
+
constructor(parent: TikTokApi, { id, data }?: PlaylistOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Returns information associated with this Playlist.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const info = await api.playlist({ id: '7426714779919797038' }).info();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
info(kwargs?: {
|
|
33
|
+
msToken?: string;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
sessionIndex?: number;
|
|
36
|
+
}): Promise<Record<string, unknown>>;
|
|
37
|
+
/**
|
|
38
|
+
* Returns videos in this playlist.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* for await (const video of api.playlist({ id: '7426714779919797038' }).videos()) {
|
|
43
|
+
* console.log(video.id);
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
videos(count?: number, cursor?: number, kwargs?: {
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
sessionIndex?: number;
|
|
50
|
+
}): AsyncGenerator<Video>;
|
|
51
|
+
private _extractFromData;
|
|
52
|
+
toString(): string;
|
|
53
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// api/playlist.ts
|
|
4
|
+
// Mirrors TikTokApi/api/playlist.py
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.Playlist = void 0;
|
|
8
|
+
const exceptions_1 = require("../exceptions");
|
|
9
|
+
class Playlist {
|
|
10
|
+
constructor(parent, { id, data } = {}) {
|
|
11
|
+
this.parent = parent;
|
|
12
|
+
if (!id && !data?.["id"]) {
|
|
13
|
+
throw new TypeError("You must provide id parameter.");
|
|
14
|
+
}
|
|
15
|
+
this.id = id ?? undefined;
|
|
16
|
+
if (data) {
|
|
17
|
+
this.asDict = data;
|
|
18
|
+
this._extractFromData();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns information associated with this Playlist.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const info = await api.playlist({ id: '7426714779919797038' }).info();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
async info(kwargs = {}) {
|
|
30
|
+
const id = this.id;
|
|
31
|
+
if (!id) {
|
|
32
|
+
throw new TypeError("You must provide the playlist id when creating this class to use this method.");
|
|
33
|
+
}
|
|
34
|
+
const urlParams = {
|
|
35
|
+
mixId: id,
|
|
36
|
+
msToken: kwargs.msToken,
|
|
37
|
+
};
|
|
38
|
+
const resp = await this.parent.makeRequest({
|
|
39
|
+
url: "https://www.tiktok.com/api/mix/detail/",
|
|
40
|
+
params: urlParams,
|
|
41
|
+
headers: kwargs.headers,
|
|
42
|
+
sessionIndex: kwargs.sessionIndex,
|
|
43
|
+
});
|
|
44
|
+
if (resp == null) {
|
|
45
|
+
throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
|
|
46
|
+
}
|
|
47
|
+
this.asDict = resp["mixInfo"];
|
|
48
|
+
this._extractFromData();
|
|
49
|
+
return resp;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns videos in this playlist.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* for await (const video of api.playlist({ id: '7426714779919797038' }).videos()) {
|
|
57
|
+
* console.log(video.id);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
async *videos(count = 30, cursor = 0, kwargs = {}) {
|
|
62
|
+
if (!this.id) {
|
|
63
|
+
await this.info(kwargs);
|
|
64
|
+
}
|
|
65
|
+
let found = 0;
|
|
66
|
+
while (found < count) {
|
|
67
|
+
const params = {
|
|
68
|
+
mixId: this.id,
|
|
69
|
+
count: Math.min(count, 30),
|
|
70
|
+
cursor,
|
|
71
|
+
};
|
|
72
|
+
const resp = await this.parent.makeRequest({
|
|
73
|
+
url: "https://www.tiktok.com/api/mix/item_list/",
|
|
74
|
+
params,
|
|
75
|
+
headers: kwargs.headers,
|
|
76
|
+
sessionIndex: kwargs.sessionIndex,
|
|
77
|
+
});
|
|
78
|
+
if (resp == null) {
|
|
79
|
+
throw new exceptions_1.InvalidResponseException(resp, "TikTok returned an invalid response.");
|
|
80
|
+
}
|
|
81
|
+
const itemList = resp["itemList"] ?? [];
|
|
82
|
+
for (const item of itemList) {
|
|
83
|
+
yield this.parent.video({ data: item });
|
|
84
|
+
found++;
|
|
85
|
+
}
|
|
86
|
+
if (!resp["hasMore"])
|
|
87
|
+
return;
|
|
88
|
+
cursor = resp["cursor"];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
_extractFromData() {
|
|
92
|
+
let data = this.asDict ?? {};
|
|
93
|
+
if ("mixInfo" in data) {
|
|
94
|
+
data = data["mixInfo"];
|
|
95
|
+
}
|
|
96
|
+
this.id = data["id"] ?? data["mixId"];
|
|
97
|
+
this.name = data["name"] ?? data["mixName"];
|
|
98
|
+
this.videoCount = data["videoCount"];
|
|
99
|
+
const creatorData = data["creator"];
|
|
100
|
+
if (creatorData) {
|
|
101
|
+
this.creator = this.parent.user({ data: creatorData });
|
|
102
|
+
}
|
|
103
|
+
this.coverUrl = data["cover"];
|
|
104
|
+
if (!this.id || !this.name) {
|
|
105
|
+
this.parent.logger.error(`Failed to create Playlist with data: ${JSON.stringify(data)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
toString() {
|
|
109
|
+
return `TikTokApi.playlist(id='${this.id}')`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.Playlist = Playlist;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TikTokApi } from "../tiktok";
|
|
2
|
+
import type { User } from "./user";
|
|
3
|
+
import type { Video } from "./video";
|
|
4
|
+
export declare class Search {
|
|
5
|
+
/** Static reference to the parent TikTokApi instance */
|
|
6
|
+
parent: TikTokApi;
|
|
7
|
+
constructor(parent: TikTokApi);
|
|
8
|
+
/**
|
|
9
|
+
* Searches for users.
|
|
10
|
+
*
|
|
11
|
+
* Note: Your ms_token must have been used for a TikTok search already.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* for await (const user of api.search.users('david teather')) {
|
|
16
|
+
* console.log(user.username);
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
users(searchTerm: string, count?: number, cursor?: number, kwargs?: {
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
sessionIndex?: number;
|
|
23
|
+
}): AsyncGenerator<User>;
|
|
24
|
+
/**
|
|
25
|
+
* Searches for a specific type of object.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* for await (const user of api.search.searchType('david teather', 'user')) {
|
|
30
|
+
* console.log(user);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
searchType(searchTerm: string, objType: "user" | "item", count?: number, cursor?: number, kwargs?: {
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
sessionIndex?: number;
|
|
37
|
+
}): AsyncGenerator<User | Video>;
|
|
38
|
+
}
|