@nxgiang/tiktok-api 1.3.7
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/CHANGELOG.md +492 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +95 -0
- package/LICENSE +201 -0
- package/README.md +1663 -0
- package/bun.lock +367 -0
- package/helper/signature.js +390 -0
- package/helper/webmssdk.js +4586 -0
- package/helper/xbogus.js +563 -0
- package/install.sh +51 -0
- package/lib/cli/index.d.ts +2 -0
- package/lib/cli/index.js +809 -0
- package/lib/constants/api.d.ts +22 -0
- package/lib/constants/api.js +39 -0
- package/lib/constants/headers.d.ts +2 -0
- package/lib/constants/headers.js +5 -0
- package/lib/constants/index.d.ts +23 -0
- package/lib/constants/index.js +26 -0
- package/lib/constants/params.d.ts +19 -0
- package/lib/constants/params.js +531 -0
- package/lib/index.d.ts +93 -0
- package/lib/index.js +137 -0
- package/lib/lib/logger.d.ts +8 -0
- package/lib/lib/logger.js +25 -0
- package/lib/services/cookieManager.d.ts +10 -0
- package/lib/services/cookieManager.js +51 -0
- package/lib/services/downloadManager.d.ts +5 -0
- package/lib/services/downloadManager.js +188 -0
- package/lib/services/tiktokService.d.ts +14 -0
- package/lib/services/tiktokService.js +78 -0
- package/lib/types/common.d.ts +65 -0
- package/lib/types/common.js +2 -0
- package/lib/types/cookieManager.d.ts +13 -0
- package/lib/types/cookieManager.js +2 -0
- package/lib/types/downloader/musicaldownDownloader.d.ts +27 -0
- package/lib/types/downloader/musicaldownDownloader.js +2 -0
- package/lib/types/downloader/ssstikDownloader.d.ts +30 -0
- package/lib/types/downloader/ssstikDownloader.js +2 -0
- package/lib/types/downloader/tiktokApiDownloader.d.ts +38 -0
- package/lib/types/downloader/tiktokApiDownloader.js +2 -0
- package/lib/types/get/getCollection.d.ts +53 -0
- package/lib/types/get/getCollection.js +2 -0
- package/lib/types/get/getComments.d.ts +26 -0
- package/lib/types/get/getComments.js +2 -0
- package/lib/types/get/getMusicDetail.d.ts +49 -0
- package/lib/types/get/getMusicDetail.js +2 -0
- package/lib/types/get/getMusicVideos.d.ts +93 -0
- package/lib/types/get/getMusicVideos.js +2 -0
- package/lib/types/get/getPlaylist.d.ts +65 -0
- package/lib/types/get/getPlaylist.js +2 -0
- package/lib/types/get/getProfile.d.ts +71 -0
- package/lib/types/get/getProfile.js +2 -0
- package/lib/types/get/getTrendings.d.ts +61 -0
- package/lib/types/get/getTrendings.js +2 -0
- package/lib/types/get/getUserLiked.d.ts +90 -0
- package/lib/types/get/getUserLiked.js +2 -0
- package/lib/types/get/getUserPosts.d.ts +68 -0
- package/lib/types/get/getUserPosts.js +2 -0
- package/lib/types/get/getUserReposts.d.ts +104 -0
- package/lib/types/get/getUserReposts.js +2 -0
- package/lib/types/search/index.d.ts +15 -0
- package/lib/types/search/index.js +2 -0
- package/lib/types/search/liveSearch.d.ts +48 -0
- package/lib/types/search/liveSearch.js +2 -0
- package/lib/types/search/userSearch.d.ts +32 -0
- package/lib/types/search/userSearch.js +2 -0
- package/lib/types/search/videoSearch.d.ts +62 -0
- package/lib/types/search/videoSearch.js +2 -0
- package/lib/utils/downloader/musicaldownDownloader.d.ts +2 -0
- package/lib/utils/downloader/musicaldownDownloader.js +193 -0
- package/lib/utils/downloader/ssstikDownloader.d.ts +2 -0
- package/lib/utils/downloader/ssstikDownloader.js +177 -0
- package/lib/utils/downloader/tiktokAPIDownloader.d.ts +3 -0
- package/lib/utils/downloader/tiktokAPIDownloader.js +221 -0
- package/lib/utils/get/getCollection.d.ts +7 -0
- package/lib/utils/get/getCollection.js +113 -0
- package/lib/utils/get/getComments.d.ts +2 -0
- package/lib/utils/get/getComments.js +139 -0
- package/lib/utils/get/getMusicDetail.d.ts +2 -0
- package/lib/utils/get/getMusicDetail.js +68 -0
- package/lib/utils/get/getMusicVideos.d.ts +2 -0
- package/lib/utils/get/getMusicVideos.js +249 -0
- package/lib/utils/get/getPlaylist.d.ts +7 -0
- package/lib/utils/get/getPlaylist.js +115 -0
- package/lib/utils/get/getProfile.d.ts +2 -0
- package/lib/utils/get/getProfile.js +92 -0
- package/lib/utils/get/getTrendings.d.ts +7 -0
- package/lib/utils/get/getTrendings.js +120 -0
- package/lib/utils/get/getUserLiked.d.ts +2 -0
- package/lib/utils/get/getUserLiked.js +204 -0
- package/lib/utils/get/getUserPosts.d.ts +2 -0
- package/lib/utils/get/getUserPosts.js +199 -0
- package/lib/utils/get/getUserRepost.d.ts +2 -0
- package/lib/utils/get/getUserRepost.js +239 -0
- package/lib/utils/search/liveSearch.d.ts +2 -0
- package/lib/utils/search/liveSearch.js +99 -0
- package/lib/utils/search/userSearch.d.ts +2 -0
- package/lib/utils/search/userSearch.js +76 -0
- package/lib/utils/search/videoSearch.d.ts +2 -0
- package/lib/utils/search/videoSearch.js +140 -0
- package/lib/utils/urlExtractors.d.ts +3 -0
- package/lib/utils/urlExtractors.js +37 -0
- package/lib/utils/validator.d.ts +1 -0
- package/lib/utils/validator.js +13 -0
- package/package.json +60 -0
- package/test/collection-test.ts +73 -0
- package/test/comments-test.ts +54 -0
- package/test/downloader-v1-test.ts +49 -0
- package/test/downloader-v2-test.ts +47 -0
- package/test/downloader-v3-test.ts +35 -0
- package/test/music-detail-test.ts +97 -0
- package/test/music-videos-test.ts +86 -0
- package/test/playlist-test.ts +48 -0
- package/test/profile-test.ts +49 -0
- package/test/search-live-test.ts +42 -0
- package/test/search-user-test.ts +46 -0
- package/test/search-video-test.ts +53 -0
- package/test/trending-test.ts +128 -0
- package/test/userliked-test.ts +65 -0
- package/test/userposts-test.ts +56 -0
- package/test/userreposts-test.ts +57 -0
- package/tobyg74-tiktok-api-1.3.7.tgz +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SSSTik = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const async_retry_1 = __importDefault(require("async-retry"));
|
|
9
|
+
const cheerio_1 = require("cheerio");
|
|
10
|
+
const api_1 = require("../../constants/api");
|
|
11
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
12
|
+
const socks_proxy_agent_1 = require("socks-proxy-agent");
|
|
13
|
+
const constants_1 = require("../../constants");
|
|
14
|
+
const TIKTOK_URL_REGEX = /https:\/\/(?:m|t|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/;
|
|
15
|
+
const USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0";
|
|
16
|
+
const createProxyAgent = (proxy) => {
|
|
17
|
+
if (!proxy)
|
|
18
|
+
return {};
|
|
19
|
+
const isHttpProxy = proxy.startsWith("http") || proxy.startsWith("https");
|
|
20
|
+
const isSocksProxy = proxy.startsWith("socks");
|
|
21
|
+
if (!isHttpProxy && !isSocksProxy)
|
|
22
|
+
return {};
|
|
23
|
+
return {
|
|
24
|
+
httpsAgent: isHttpProxy
|
|
25
|
+
? new https_proxy_agent_1.HttpsProxyAgent(proxy)
|
|
26
|
+
: new socks_proxy_agent_1.SocksProxyAgent(proxy)
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const validateTikTokUrl = (url) => {
|
|
30
|
+
return TIKTOK_URL_REGEX.test(url);
|
|
31
|
+
};
|
|
32
|
+
const extractTTValue = (html) => {
|
|
33
|
+
const regex = /s_tt\s*=\s*["']([^"']+)["']/;
|
|
34
|
+
const match = html.match(regex);
|
|
35
|
+
return match ? match[1] : null;
|
|
36
|
+
};
|
|
37
|
+
const parseAuthor = ($) => ({
|
|
38
|
+
avatar: $("img.result_author").attr("src") || "",
|
|
39
|
+
nickname: $("h2").text().trim()
|
|
40
|
+
});
|
|
41
|
+
const parseStatistics = ($) => ({
|
|
42
|
+
likeCount: $("#trending-actions > .justify-content-start").text().trim(),
|
|
43
|
+
commentCount: $("#trending-actions > .justify-content-center").text().trim(),
|
|
44
|
+
shareCount: $("#trending-actions > .justify-content-end").text().trim()
|
|
45
|
+
});
|
|
46
|
+
const parseImages = ($) => {
|
|
47
|
+
const images = [];
|
|
48
|
+
$("ul.splide__list > li").each((_, img) => {
|
|
49
|
+
const href = $(img).find("a").attr("href");
|
|
50
|
+
if (href)
|
|
51
|
+
images.push(href);
|
|
52
|
+
});
|
|
53
|
+
return images;
|
|
54
|
+
};
|
|
55
|
+
const createImageResponse = ($, author, statistics, images, music) => ({
|
|
56
|
+
type: "image",
|
|
57
|
+
desc: $("p.maintext").text().trim(),
|
|
58
|
+
author,
|
|
59
|
+
statistics,
|
|
60
|
+
images,
|
|
61
|
+
...(music && { music: { playUrl: [music] } })
|
|
62
|
+
});
|
|
63
|
+
const createVideoResponse = ($, author, statistics, video, music) => ({
|
|
64
|
+
type: "video",
|
|
65
|
+
desc: $("p.maintext").text().trim(),
|
|
66
|
+
author,
|
|
67
|
+
statistics,
|
|
68
|
+
video: { playAddr: [video] },
|
|
69
|
+
...(music && { music: { playUrl: [music] } })
|
|
70
|
+
});
|
|
71
|
+
const createMusicResponse = (music, direct) => ({
|
|
72
|
+
type: "music",
|
|
73
|
+
music: { playUrl: [music] },
|
|
74
|
+
direct: direct || ""
|
|
75
|
+
});
|
|
76
|
+
const fetchTT = async (proxy) => {
|
|
77
|
+
try {
|
|
78
|
+
const { data } = await (0, axios_1.default)(api_1._ssstikurl, {
|
|
79
|
+
method: "GET",
|
|
80
|
+
headers: { "User-Agent": USER_AGENT },
|
|
81
|
+
...createProxyAgent(proxy)
|
|
82
|
+
});
|
|
83
|
+
const ttValue = extractTTValue(data);
|
|
84
|
+
if (!ttValue) {
|
|
85
|
+
return {
|
|
86
|
+
status: "error",
|
|
87
|
+
message: constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
status: "success",
|
|
92
|
+
result: ttValue
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return {
|
|
97
|
+
status: "error",
|
|
98
|
+
message: error instanceof Error ? error.message : constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const SSSTik = async (url, proxy) => {
|
|
103
|
+
try {
|
|
104
|
+
if (!validateTikTokUrl(url)) {
|
|
105
|
+
return {
|
|
106
|
+
status: "error",
|
|
107
|
+
message: constants_1.ERROR_MESSAGES.INVALID_URL
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const tt = await fetchTT(proxy);
|
|
111
|
+
if (tt.status !== "success") {
|
|
112
|
+
return {
|
|
113
|
+
status: "error",
|
|
114
|
+
message: tt.message
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const response = await (0, async_retry_1.default)(async () => {
|
|
118
|
+
const res = await (0, axios_1.default)(api_1._ssstikapi, {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
122
|
+
Origin: api_1._ssstikurl,
|
|
123
|
+
Referer: `${api_1._ssstikurl}/en`,
|
|
124
|
+
"User-Agent": USER_AGENT
|
|
125
|
+
},
|
|
126
|
+
data: new URLSearchParams({
|
|
127
|
+
id: url,
|
|
128
|
+
locale: "en",
|
|
129
|
+
tt: tt.result
|
|
130
|
+
}),
|
|
131
|
+
...createProxyAgent(proxy)
|
|
132
|
+
});
|
|
133
|
+
if (res.status === 200 && res.data) {
|
|
134
|
+
return res.data;
|
|
135
|
+
}
|
|
136
|
+
throw new Error(constants_1.ERROR_MESSAGES.NETWORK_ERROR);
|
|
137
|
+
}, {
|
|
138
|
+
retries: 20,
|
|
139
|
+
minTimeout: 200,
|
|
140
|
+
maxTimeout: 1000
|
|
141
|
+
});
|
|
142
|
+
const $ = (0, cheerio_1.load)(response);
|
|
143
|
+
const author = parseAuthor($);
|
|
144
|
+
const statistics = parseStatistics($);
|
|
145
|
+
const video = $("a.without_watermark").attr("href");
|
|
146
|
+
const music = $("a.music").attr("href");
|
|
147
|
+
const direct = $("a.music_direct").attr("href");
|
|
148
|
+
const images = parseImages($);
|
|
149
|
+
let result;
|
|
150
|
+
if (images.length > 0) {
|
|
151
|
+
result = createImageResponse($, author, statistics, images, music);
|
|
152
|
+
}
|
|
153
|
+
else if (video) {
|
|
154
|
+
result = createVideoResponse($, author, statistics, video, music);
|
|
155
|
+
}
|
|
156
|
+
else if (music) {
|
|
157
|
+
result = createMusicResponse(music, direct);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
return {
|
|
161
|
+
status: "error",
|
|
162
|
+
message: constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
status: "success",
|
|
167
|
+
result
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
return {
|
|
172
|
+
status: "error",
|
|
173
|
+
message: error instanceof Error ? error.message : constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
exports.SSSTik = SSSTik;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { TiktokAPIResponse } from "../../types/downloader/tiktokApiDownloader";
|
|
2
|
+
export declare const handleRedirect: (url: string, proxy?: string) => Promise<string>;
|
|
3
|
+
export declare const TiktokAPI: (url: string, proxy?: string, showOriginalResponse?: boolean) => Promise<TiktokAPIResponse>;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TiktokAPI = exports.handleRedirect = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const async_retry_1 = __importDefault(require("async-retry"));
|
|
9
|
+
const api_1 = require("../../constants/api");
|
|
10
|
+
const params_1 = require("../../constants/params");
|
|
11
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
12
|
+
const socks_proxy_agent_1 = require("socks-proxy-agent");
|
|
13
|
+
const constants_1 = require("../../constants");
|
|
14
|
+
const TIKTOK_URL_REGEX = /https:\/\/(?:m|t|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/;
|
|
15
|
+
const USER_AGENT = "com.zhiliaoapp.musically/300904 (2018111632; U; Android 10; en_US; Pixel 4; Build/QQ3A.200805.001; Cronet/58.0.2991.0)";
|
|
16
|
+
const createProxyAgent = (proxy) => {
|
|
17
|
+
if (!proxy)
|
|
18
|
+
return {};
|
|
19
|
+
const isHttpProxy = proxy.startsWith("http") || proxy.startsWith("https");
|
|
20
|
+
const isSocksProxy = proxy.startsWith("socks");
|
|
21
|
+
if (!isHttpProxy && !isSocksProxy)
|
|
22
|
+
return {};
|
|
23
|
+
return {
|
|
24
|
+
httpsAgent: isHttpProxy
|
|
25
|
+
? new https_proxy_agent_1.HttpsProxyAgent(proxy)
|
|
26
|
+
: new socks_proxy_agent_1.SocksProxyAgent(proxy)
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const validateTikTokUrl = (url) => {
|
|
30
|
+
return TIKTOK_URL_REGEX.test(url);
|
|
31
|
+
};
|
|
32
|
+
const extractVideoId = (responseUrl) => {
|
|
33
|
+
const matches = responseUrl.match(/\d{17,21}/g);
|
|
34
|
+
return matches ? matches[0] : null;
|
|
35
|
+
};
|
|
36
|
+
const parseStatistics = (content) => ({
|
|
37
|
+
commentCount: content.statistics.comment_count,
|
|
38
|
+
likeCount: content.statistics.digg_count,
|
|
39
|
+
shareCount: content.statistics.share_count,
|
|
40
|
+
playCount: content.statistics.play_count,
|
|
41
|
+
downloadCount: content.statistics.download_count
|
|
42
|
+
});
|
|
43
|
+
const parseAuthor = (content) => ({
|
|
44
|
+
uid: content.author.uid,
|
|
45
|
+
username: content.author.unique_id,
|
|
46
|
+
uniqueId: content.author.unique_id,
|
|
47
|
+
nickname: content.author.nickname,
|
|
48
|
+
signature: content.author.signature,
|
|
49
|
+
region: content.author.region,
|
|
50
|
+
avatarThumb: content.author?.avatar_thumb?.url_list || [],
|
|
51
|
+
avatarMedium: content.author?.avatar_medium?.url_list || [],
|
|
52
|
+
url: `${api_1._tiktokDesktopUrl}/@${content.author.unique_id}`
|
|
53
|
+
});
|
|
54
|
+
const parseMusic = (content) => ({
|
|
55
|
+
id: content.music.id,
|
|
56
|
+
title: content.music.title,
|
|
57
|
+
author: content.music.author,
|
|
58
|
+
album: content.music.album,
|
|
59
|
+
playUrl: content.music?.play_url?.url_list || [],
|
|
60
|
+
coverLarge: content.music?.cover_large?.url_list || [],
|
|
61
|
+
coverMedium: content.music?.cover_medium?.url_list || [],
|
|
62
|
+
coverThumb: content.music?.cover_thumb?.url_list || [],
|
|
63
|
+
duration: content.music.duration,
|
|
64
|
+
isCommerceMusic: content.music.is_commerce_music,
|
|
65
|
+
isOriginalSound: content.music.is_original_sound,
|
|
66
|
+
isAuthorArtist: content.music.is_author_artist
|
|
67
|
+
});
|
|
68
|
+
const parseVideo = (content) => ({
|
|
69
|
+
ratio: content.video.ratio,
|
|
70
|
+
duration: content.video.duration,
|
|
71
|
+
playAddr: content.video?.play_addr?.url_list || [],
|
|
72
|
+
downloadAddr: content.video?.download_addr?.url_list || [],
|
|
73
|
+
cover: content.video?.cover?.url_list || [],
|
|
74
|
+
dynamicCover: content.video?.dynamic_cover?.url_list || [],
|
|
75
|
+
originCover: content.video?.origin_cover?.url_list || []
|
|
76
|
+
});
|
|
77
|
+
const parseTiktokData = (ID, data) => {
|
|
78
|
+
const content = data?.aweme_list?.find((v) => v.aweme_id === ID);
|
|
79
|
+
if (!content)
|
|
80
|
+
return { content: null };
|
|
81
|
+
return {
|
|
82
|
+
content,
|
|
83
|
+
statistics: parseStatistics(content),
|
|
84
|
+
author: parseAuthor(content),
|
|
85
|
+
music: parseMusic(content)
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const fetchTiktokData = async (ID, proxy) => {
|
|
89
|
+
try {
|
|
90
|
+
const response = await (0, async_retry_1.default)(async () => {
|
|
91
|
+
const res = await (0, axios_1.default)((0, api_1._tiktokvFeed)((0, params_1._tiktokApiParams)({ aweme_id: ID })), {
|
|
92
|
+
method: "OPTIONS",
|
|
93
|
+
headers: { "User-Agent": USER_AGENT },
|
|
94
|
+
...createProxyAgent(proxy)
|
|
95
|
+
});
|
|
96
|
+
if (res.data && res.data.status_code === 0) {
|
|
97
|
+
return res.data;
|
|
98
|
+
}
|
|
99
|
+
throw new Error(constants_1.ERROR_MESSAGES.NETWORK_ERROR);
|
|
100
|
+
}, {
|
|
101
|
+
retries: 20,
|
|
102
|
+
minTimeout: 200,
|
|
103
|
+
maxTimeout: 1000
|
|
104
|
+
});
|
|
105
|
+
return parseTiktokData(ID, response);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error("Error fetching TikTok data:", error);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const createImageResponse = (content, author, statistics, music) => ({
|
|
113
|
+
status: "success",
|
|
114
|
+
result: {
|
|
115
|
+
type: "image",
|
|
116
|
+
id: content.aweme_id,
|
|
117
|
+
createTime: content.create_time,
|
|
118
|
+
desc: content.desc,
|
|
119
|
+
isTurnOffComment: content.item_comment_settings === 3,
|
|
120
|
+
hashtag: content.text_extra
|
|
121
|
+
.filter((x) => x.hashtag_name !== undefined)
|
|
122
|
+
.map((v) => v.hashtag_name),
|
|
123
|
+
isADS: content.is_ads,
|
|
124
|
+
author,
|
|
125
|
+
statistics,
|
|
126
|
+
images: content.image_post_info.images?.map((v) => v?.display_image?.url_list[0]) || [],
|
|
127
|
+
music
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const createVideoResponse = (content, author, statistics, music) => ({
|
|
131
|
+
status: "success",
|
|
132
|
+
result: {
|
|
133
|
+
type: "video",
|
|
134
|
+
id: content.aweme_id,
|
|
135
|
+
createTime: content.create_time,
|
|
136
|
+
desc: content.desc,
|
|
137
|
+
isTurnOffComment: content.item_comment_settings === 3,
|
|
138
|
+
hashtag: content.text_extra
|
|
139
|
+
.filter((x) => x.hashtag_name !== undefined)
|
|
140
|
+
.map((v) => v.hashtag_name),
|
|
141
|
+
isADS: content.is_ads,
|
|
142
|
+
author,
|
|
143
|
+
statistics,
|
|
144
|
+
video: parseVideo(content),
|
|
145
|
+
music
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const handleRedirect = async (url, proxy) => {
|
|
149
|
+
try {
|
|
150
|
+
const response = await (0, axios_1.default)(url, {
|
|
151
|
+
method: "HEAD",
|
|
152
|
+
maxRedirects: 5,
|
|
153
|
+
validateStatus: (status) => status >= 200 && status < 400,
|
|
154
|
+
...createProxyAgent(proxy)
|
|
155
|
+
});
|
|
156
|
+
const finalUrl = response.request.res.responseUrl;
|
|
157
|
+
return finalUrl.split("?")[0];
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error("Error handling redirect:", error);
|
|
161
|
+
return url;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
exports.handleRedirect = handleRedirect;
|
|
165
|
+
const TiktokAPI = async (url, proxy, showOriginalResponse) => {
|
|
166
|
+
try {
|
|
167
|
+
if (!validateTikTokUrl(url)) {
|
|
168
|
+
return {
|
|
169
|
+
status: "error",
|
|
170
|
+
message: constants_1.ERROR_MESSAGES.INVALID_URL
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
url = url.replace("https://vm", "https://vt");
|
|
174
|
+
const request = await (0, axios_1.default)(url, {
|
|
175
|
+
method: "GET",
|
|
176
|
+
maxRedirects: 0,
|
|
177
|
+
headers: {
|
|
178
|
+
"User-Agent": USER_AGENT,
|
|
179
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
180
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
181
|
+
},
|
|
182
|
+
...createProxyAgent(proxy),
|
|
183
|
+
validateStatus: (status) => status >= 200 && status < 400,
|
|
184
|
+
});
|
|
185
|
+
const redirectUrl = request.headers.location ??
|
|
186
|
+
request.request?.res?.responseUrl ??
|
|
187
|
+
url;
|
|
188
|
+
const videoId = extractVideoId(redirectUrl);
|
|
189
|
+
if (!videoId) {
|
|
190
|
+
return {
|
|
191
|
+
status: "error",
|
|
192
|
+
message: constants_1.ERROR_MESSAGES.INVALID_URL
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const data = await fetchTiktokData(videoId, proxy);
|
|
196
|
+
if (!data?.content) {
|
|
197
|
+
return {
|
|
198
|
+
status: "error",
|
|
199
|
+
message: constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const { content, author, statistics, music } = data;
|
|
203
|
+
const response = content.image_post_info
|
|
204
|
+
? createImageResponse(content, author, statistics, music)
|
|
205
|
+
: createVideoResponse(content, author, statistics, music);
|
|
206
|
+
if (showOriginalResponse) {
|
|
207
|
+
return {
|
|
208
|
+
status: "success",
|
|
209
|
+
resultNotParsed: data
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return response;
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
status: "error",
|
|
217
|
+
message: error instanceof Error ? error.message : constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
exports.TiktokAPI = TiktokAPI;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TiktokCollectionResponse } from "../../types/get/getCollection";
|
|
2
|
+
export declare const getCollection: (collectionId: string, proxy?: string, page?: number, count?: number) => Promise<TiktokCollectionResponse>;
|
|
3
|
+
export declare const Collection: (collectionIdOrUrl: string, options?: {
|
|
4
|
+
page?: number;
|
|
5
|
+
proxy?: string;
|
|
6
|
+
count?: number;
|
|
7
|
+
}) => Promise<TiktokCollectionResponse>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Collection = exports.getCollection = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const api_1 = require("../../constants/api");
|
|
9
|
+
const params_1 = require("../../constants/params");
|
|
10
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
11
|
+
const socks_proxy_agent_1 = require("socks-proxy-agent");
|
|
12
|
+
const constants_1 = require("../../constants");
|
|
13
|
+
const async_retry_1 = __importDefault(require("async-retry"));
|
|
14
|
+
const tiktokAPIDownloader_1 = require("../downloader/tiktokAPIDownloader");
|
|
15
|
+
const urlExtractors_1 = require("../urlExtractors");
|
|
16
|
+
const createProxyAgent = (proxy) => {
|
|
17
|
+
if (!proxy)
|
|
18
|
+
return {};
|
|
19
|
+
if (proxy.startsWith("socks")) {
|
|
20
|
+
return {
|
|
21
|
+
httpsAgent: new socks_proxy_agent_1.SocksProxyAgent(proxy)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
httpsAgent: new https_proxy_agent_1.HttpsProxyAgent(proxy)
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const getCollection = async (collectionId, proxy, page = 1, count = 5) => {
|
|
29
|
+
try {
|
|
30
|
+
const response = await (0, async_retry_1.default)(async () => {
|
|
31
|
+
const res = await (0, axios_1.default)((0, api_1._tiktokGetCollection)((0, params_1._getCollectionParams)(collectionId, page, count)), {
|
|
32
|
+
method: "GET",
|
|
33
|
+
headers: {
|
|
34
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
35
|
+
Accept: "*/*",
|
|
36
|
+
"Accept-Language": "en-US,en;q=0.7",
|
|
37
|
+
Referer: "https://www.tiktok.com/",
|
|
38
|
+
Origin: "https://www.tiktok.com"
|
|
39
|
+
},
|
|
40
|
+
...createProxyAgent(proxy)
|
|
41
|
+
});
|
|
42
|
+
if (res.data && res.data.statusCode === 0) {
|
|
43
|
+
return res.data;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(constants_1.ERROR_MESSAGES.NETWORK_ERROR);
|
|
46
|
+
}, {
|
|
47
|
+
retries: 20,
|
|
48
|
+
minTimeout: 200,
|
|
49
|
+
maxTimeout: 1000
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
status: "success",
|
|
53
|
+
result: {
|
|
54
|
+
hasMore: response.hasMore,
|
|
55
|
+
itemList: response.itemList || [],
|
|
56
|
+
extra: response.extra
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return {
|
|
62
|
+
status: "error",
|
|
63
|
+
message: error instanceof Error ? error.message : constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
exports.getCollection = getCollection;
|
|
68
|
+
const Collection = async (collectionIdOrUrl, options) => {
|
|
69
|
+
try {
|
|
70
|
+
const processedUrl = collectionIdOrUrl.startsWith("http")
|
|
71
|
+
? await (0, tiktokAPIDownloader_1.handleRedirect)(collectionIdOrUrl, options?.proxy)
|
|
72
|
+
: collectionIdOrUrl;
|
|
73
|
+
const collectionId = (0, urlExtractors_1.extractCollectionId)(processedUrl);
|
|
74
|
+
if (!collectionId) {
|
|
75
|
+
return {
|
|
76
|
+
status: "error",
|
|
77
|
+
message: "Invalid collection ID or URL format"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const response = await (0, axios_1.default)((0, api_1._tiktokGetCollection)((0, params_1._getCollectionParams)(collectionId, options.page, options.count)), {
|
|
81
|
+
method: "GET",
|
|
82
|
+
headers: {
|
|
83
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
84
|
+
Accept: "*/*",
|
|
85
|
+
"Accept-Language": "en-US,en;q=0.7",
|
|
86
|
+
Referer: "https://www.tiktok.com/",
|
|
87
|
+
Origin: "https://www.tiktok.com"
|
|
88
|
+
},
|
|
89
|
+
...createProxyAgent(options?.proxy)
|
|
90
|
+
});
|
|
91
|
+
if (response.data && response.data.status_code === 0) {
|
|
92
|
+
const data = response.data;
|
|
93
|
+
return {
|
|
94
|
+
status: "success",
|
|
95
|
+
result: {
|
|
96
|
+
itemList: data.itemList || [],
|
|
97
|
+
hasMore: data.hasMore
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
status: "error",
|
|
103
|
+
message: constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
return {
|
|
108
|
+
status: "error",
|
|
109
|
+
message: error instanceof Error ? error.message : constants_1.ERROR_MESSAGES.NETWORK_ERROR
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
exports.Collection = Collection;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getComments = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const api_1 = require("../../constants/api");
|
|
9
|
+
const params_1 = require("../../constants/params");
|
|
10
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
11
|
+
const socks_proxy_agent_1 = require("socks-proxy-agent");
|
|
12
|
+
const TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/;
|
|
13
|
+
const getComments = async (url, proxy, commentLimit) => new Promise(async (resolve) => {
|
|
14
|
+
if (!TiktokURLregex.test(url)) {
|
|
15
|
+
return resolve({
|
|
16
|
+
status: "error",
|
|
17
|
+
message: "Invalid Tiktok URL. Make sure your url is correct!"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
url = url.replace("https://vm", "https://vt");
|
|
21
|
+
(0, axios_1.default)(url, {
|
|
22
|
+
method: "HEAD",
|
|
23
|
+
httpsAgent: (proxy &&
|
|
24
|
+
(proxy.startsWith("http") || proxy.startsWith("https")
|
|
25
|
+
? new https_proxy_agent_1.HttpsProxyAgent(proxy)
|
|
26
|
+
: proxy.startsWith("socks")
|
|
27
|
+
? new socks_proxy_agent_1.SocksProxyAgent(proxy)
|
|
28
|
+
: undefined)) ||
|
|
29
|
+
undefined
|
|
30
|
+
})
|
|
31
|
+
.then(async ({ request }) => {
|
|
32
|
+
const { responseUrl } = request.res;
|
|
33
|
+
let ID = responseUrl.match(/\d{17,21}/g);
|
|
34
|
+
if (ID === null)
|
|
35
|
+
return resolve({
|
|
36
|
+
status: "error",
|
|
37
|
+
message: "Failed to fetch tiktok url. Make sure your tiktok url is correct!"
|
|
38
|
+
});
|
|
39
|
+
ID = ID[0];
|
|
40
|
+
const resultComments = await parseComments(ID, commentLimit, proxy);
|
|
41
|
+
if (!resultComments.comments.length)
|
|
42
|
+
return resolve({
|
|
43
|
+
status: "error",
|
|
44
|
+
message: "No comments found!"
|
|
45
|
+
});
|
|
46
|
+
return resolve({
|
|
47
|
+
status: "success",
|
|
48
|
+
result: resultComments.comments,
|
|
49
|
+
totalComments: resultComments.total
|
|
50
|
+
});
|
|
51
|
+
})
|
|
52
|
+
.catch((e) => resolve({ status: "error", message: e.message }));
|
|
53
|
+
});
|
|
54
|
+
exports.getComments = getComments;
|
|
55
|
+
const requestComments = async (id, cursor = 0, proxy) => {
|
|
56
|
+
const { data } = await (0, axios_1.default)((0, api_1._tiktokGetComments)((0, params_1._getCommentsParams)(id, cursor)), {
|
|
57
|
+
method: "GET",
|
|
58
|
+
headers: {
|
|
59
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"
|
|
60
|
+
},
|
|
61
|
+
httpsAgent: (proxy &&
|
|
62
|
+
(proxy.startsWith("http") || proxy.startsWith("https")
|
|
63
|
+
? new https_proxy_agent_1.HttpsProxyAgent(proxy)
|
|
64
|
+
: proxy.startsWith("socks")
|
|
65
|
+
? new socks_proxy_agent_1.SocksProxyAgent(proxy)
|
|
66
|
+
: undefined)) ||
|
|
67
|
+
undefined
|
|
68
|
+
});
|
|
69
|
+
return data;
|
|
70
|
+
};
|
|
71
|
+
const parseComments = async (id, commentLimit, proxy) => {
|
|
72
|
+
const comments = [];
|
|
73
|
+
let cursor = 0;
|
|
74
|
+
let total = 0;
|
|
75
|
+
let hasMore = true;
|
|
76
|
+
while (hasMore) {
|
|
77
|
+
const result = await requestComments(id, cursor, proxy);
|
|
78
|
+
hasMore = result.has_more === 1;
|
|
79
|
+
cursor = hasMore ? result.cursor : 0;
|
|
80
|
+
if (result.comments) {
|
|
81
|
+
result.comments.forEach((v) => {
|
|
82
|
+
const comment = {
|
|
83
|
+
cid: v.cid,
|
|
84
|
+
text: v.text,
|
|
85
|
+
commentLanguage: v.comment_language,
|
|
86
|
+
createTime: v.create_time,
|
|
87
|
+
likeCount: v.digg_count,
|
|
88
|
+
isAuthorLiked: v.is_author_digged,
|
|
89
|
+
isCommentTranslatable: v.is_comment_translatable,
|
|
90
|
+
replyCommentTotal: v.reply_comment_total,
|
|
91
|
+
user: {
|
|
92
|
+
uid: v.user.uid,
|
|
93
|
+
avatarThumb: v.user.avatar_thumb.url_list,
|
|
94
|
+
nickname: v.user.nickname,
|
|
95
|
+
username: v.user.unique_id,
|
|
96
|
+
isVerified: v.user.custom_verify !== ""
|
|
97
|
+
},
|
|
98
|
+
url: v.share_info?.url || "",
|
|
99
|
+
replyComment: []
|
|
100
|
+
};
|
|
101
|
+
if (v.reply_comment !== null) {
|
|
102
|
+
v.reply_comment.forEach((v) => {
|
|
103
|
+
comment.replyComment.push({
|
|
104
|
+
cid: v.cid,
|
|
105
|
+
text: v.text,
|
|
106
|
+
commentLanguage: v.comment_language,
|
|
107
|
+
createTime: v.create_time,
|
|
108
|
+
likeCount: v.digg_count,
|
|
109
|
+
isAuthorLiked: v.is_author_digged,
|
|
110
|
+
isCommentTranslatable: v.is_comment_translatable,
|
|
111
|
+
replyCommentTotal: v.reply_comment_total,
|
|
112
|
+
user: {
|
|
113
|
+
uid: v.user.uid,
|
|
114
|
+
avatarThumb: v.user.avatar_thumb.url_list,
|
|
115
|
+
nickname: v.user.nickname,
|
|
116
|
+
username: v.user.unique_id,
|
|
117
|
+
isVerified: v.user.custom_verify !== ""
|
|
118
|
+
},
|
|
119
|
+
url: v.share_info?.url || "",
|
|
120
|
+
replyComment: []
|
|
121
|
+
});
|
|
122
|
+
total++;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
total++;
|
|
126
|
+
comments.push(comment);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (commentLimit && comments.length >= commentLimit) {
|
|
130
|
+
hasMore = false;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const response = commentLimit ? comments.slice(0, commentLimit) : comments;
|
|
135
|
+
return {
|
|
136
|
+
total: response.length,
|
|
137
|
+
comments: response
|
|
138
|
+
};
|
|
139
|
+
};
|