@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
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { TiktokAPIResponse } from "./types/downloader/tiktokApiDownloader";
|
|
2
|
+
import { SSSTikResponse } from "./types/downloader/ssstikDownloader";
|
|
3
|
+
import { MusicalDownResponse } from "./types/downloader/musicaldownDownloader";
|
|
4
|
+
import { UserSearchResult } from "./types/search/userSearch";
|
|
5
|
+
import { LiveSearchResult } from "./types/search/liveSearch";
|
|
6
|
+
import { VideoSearchResult } from "./types/search/videoSearch";
|
|
7
|
+
import { TiktokStalkUserResponse } from "./types/get/getProfile";
|
|
8
|
+
import { TiktokVideoCommentsResponse } from "./types/get/getComments";
|
|
9
|
+
import { TiktokUserPostsResponse } from "./types/get/getUserPosts";
|
|
10
|
+
import { TiktokUserRepostsResponse } from "./types/get/getUserReposts";
|
|
11
|
+
import { TiktokUserFavoriteVideosResponse } from "./types/get/getUserLiked";
|
|
12
|
+
import { TiktokCollectionResponse } from "./types/get/getCollection";
|
|
13
|
+
import { TiktokTrendingResponse, TrendingCreator } from "./types/get/getTrendings";
|
|
14
|
+
import { TiktokMusicVideosResponse } from "./types/get/getMusicVideos";
|
|
15
|
+
import { TiktokMusicDetailResponse } from "./types/get/getMusicDetail";
|
|
16
|
+
import { TiktokPlaylistResponse } from "./types/get/getPlaylist";
|
|
17
|
+
type DownloaderVersion = "v1" | "v2" | "v3";
|
|
18
|
+
type SearchType = "user" | "live" | "video";
|
|
19
|
+
type TiktokDownloaderResponse<T extends DownloaderVersion> = T extends "v1" ? TiktokAPIResponse : T extends "v2" ? SSSTikResponse : T extends "v3" ? MusicalDownResponse : TiktokAPIResponse;
|
|
20
|
+
type SearchResult<T extends SearchType> = {
|
|
21
|
+
type: T;
|
|
22
|
+
} & (T extends "user" ? UserSearchResult : T extends "live" ? LiveSearchResult : VideoSearchResult);
|
|
23
|
+
type TiktokSearchResponse<T extends SearchType> = {
|
|
24
|
+
status: "success" | "error";
|
|
25
|
+
message?: string;
|
|
26
|
+
result?: SearchResult<T>[];
|
|
27
|
+
page?: number;
|
|
28
|
+
totalResults?: number;
|
|
29
|
+
};
|
|
30
|
+
declare const _default: {
|
|
31
|
+
Downloader: <T extends DownloaderVersion>(url: string, options?: {
|
|
32
|
+
version: T;
|
|
33
|
+
proxy?: string;
|
|
34
|
+
showOriginalResponse?: boolean;
|
|
35
|
+
}) => Promise<TiktokDownloaderResponse<T>>;
|
|
36
|
+
Search: <T extends SearchType>(keyword: string, options?: {
|
|
37
|
+
type?: T;
|
|
38
|
+
cookie: string | any[];
|
|
39
|
+
page?: number;
|
|
40
|
+
proxy?: string;
|
|
41
|
+
}) => Promise<TiktokSearchResponse<T>>;
|
|
42
|
+
StalkUser: (username: string, options?: {
|
|
43
|
+
proxy?: string;
|
|
44
|
+
}) => Promise<TiktokStalkUserResponse>;
|
|
45
|
+
GetVideoComments: (url: string, options?: {
|
|
46
|
+
commentLimit?: number;
|
|
47
|
+
proxy?: string;
|
|
48
|
+
}) => Promise<TiktokVideoCommentsResponse>;
|
|
49
|
+
GetUserPosts: (username: string, options?: {
|
|
50
|
+
postLimit?: number;
|
|
51
|
+
proxy?: string;
|
|
52
|
+
}) => Promise<TiktokUserPostsResponse>;
|
|
53
|
+
GetUserLiked: (username: string, options: {
|
|
54
|
+
cookie: string | any[];
|
|
55
|
+
postLimit?: number;
|
|
56
|
+
proxy?: string;
|
|
57
|
+
}) => Promise<TiktokUserFavoriteVideosResponse>;
|
|
58
|
+
GetUserReposts: (username: string, options?: {
|
|
59
|
+
postLimit?: number;
|
|
60
|
+
proxy?: string;
|
|
61
|
+
filterDeletedPost?: boolean;
|
|
62
|
+
}) => Promise<TiktokUserRepostsResponse>;
|
|
63
|
+
Collection: (collectionIdOrUrl: string, options?: {
|
|
64
|
+
proxy?: string;
|
|
65
|
+
page?: number;
|
|
66
|
+
count?: number;
|
|
67
|
+
}) => Promise<TiktokCollectionResponse>;
|
|
68
|
+
Playlist: (playlistIdOrUrl: string, options?: {
|
|
69
|
+
proxy?: string;
|
|
70
|
+
page?: number;
|
|
71
|
+
count?: number;
|
|
72
|
+
}) => Promise<TiktokPlaylistResponse>;
|
|
73
|
+
Trending: (options?: {
|
|
74
|
+
proxy?: string;
|
|
75
|
+
}) => Promise<TiktokTrendingResponse>;
|
|
76
|
+
GetVideosByMusicId: (musicIdOrUrl: string, options?: {
|
|
77
|
+
proxy?: string;
|
|
78
|
+
page?: number;
|
|
79
|
+
count?: number;
|
|
80
|
+
}) => Promise<TiktokMusicVideosResponse>;
|
|
81
|
+
GetMusicDetail: (musicIdOrUrl: string, options: {
|
|
82
|
+
cookie: string | any[];
|
|
83
|
+
proxy?: string;
|
|
84
|
+
}) => Promise<TiktokMusicDetailResponse>;
|
|
85
|
+
TrendingCreators: (options?: {
|
|
86
|
+
proxy?: string;
|
|
87
|
+
}) => Promise<{
|
|
88
|
+
status: "success" | "error";
|
|
89
|
+
message?: string;
|
|
90
|
+
result?: TrendingCreator[];
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
export = _default;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const tiktokAPIDownloader_1 = require("./utils/downloader/tiktokAPIDownloader");
|
|
3
|
+
const ssstikDownloader_1 = require("./utils/downloader/ssstikDownloader");
|
|
4
|
+
const musicaldownDownloader_1 = require("./utils/downloader/musicaldownDownloader");
|
|
5
|
+
const getProfile_1 = require("./utils/get/getProfile");
|
|
6
|
+
const userSearch_1 = require("./utils/search/userSearch");
|
|
7
|
+
const liveSearch_1 = require("./utils/search/liveSearch");
|
|
8
|
+
const getComments_1 = require("./utils/get/getComments");
|
|
9
|
+
const getUserPosts_1 = require("./utils/get/getUserPosts");
|
|
10
|
+
const getUserRepost_1 = require("./utils/get/getUserRepost");
|
|
11
|
+
const getUserLiked_1 = require("./utils/get/getUserLiked");
|
|
12
|
+
const videoSearch_1 = require("./utils/search/videoSearch");
|
|
13
|
+
const getCollection_1 = require("./utils/get/getCollection");
|
|
14
|
+
const getTrendings_1 = require("./utils/get/getTrendings");
|
|
15
|
+
const getMusicVideos_1 = require("./utils/get/getMusicVideos");
|
|
16
|
+
const getMusicDetail_1 = require("./utils/get/getMusicDetail");
|
|
17
|
+
const constants_1 = require("./constants");
|
|
18
|
+
const constants_2 = require("./constants");
|
|
19
|
+
const validator_1 = require("./utils/validator");
|
|
20
|
+
const getPlaylist_1 = require("./utils/get/getPlaylist");
|
|
21
|
+
const urlExtractors_1 = require("./utils/urlExtractors");
|
|
22
|
+
const handleError = (message) => {
|
|
23
|
+
return {
|
|
24
|
+
status: "error",
|
|
25
|
+
message
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
module.exports = {
|
|
29
|
+
Downloader: async (url, options) => {
|
|
30
|
+
const version = options?.version?.toLowerCase();
|
|
31
|
+
switch (version) {
|
|
32
|
+
case constants_1.DOWNLOADER_VERSIONS.V1:
|
|
33
|
+
return (await (0, tiktokAPIDownloader_1.TiktokAPI)(url, options?.proxy, options?.showOriginalResponse));
|
|
34
|
+
case constants_1.DOWNLOADER_VERSIONS.V2:
|
|
35
|
+
return (await (0, ssstikDownloader_1.SSSTik)(url, options?.proxy));
|
|
36
|
+
case constants_1.DOWNLOADER_VERSIONS.V3:
|
|
37
|
+
return (await (0, musicaldownDownloader_1.MusicalDown)(url, options?.proxy));
|
|
38
|
+
default:
|
|
39
|
+
return (await (0, tiktokAPIDownloader_1.TiktokAPI)(url, options?.proxy, options?.showOriginalResponse));
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
Search: async (keyword, options) => {
|
|
43
|
+
try {
|
|
44
|
+
const type = options?.type?.toLowerCase();
|
|
45
|
+
if (!type || !Object.values(constants_1.SEARCH_TYPES).includes(type)) {
|
|
46
|
+
throw new Error(constants_2.ERROR_MESSAGES.INVALID_SEARCH_TYPE);
|
|
47
|
+
}
|
|
48
|
+
switch (type) {
|
|
49
|
+
case constants_1.SEARCH_TYPES.USER:
|
|
50
|
+
const userResults = await (0, userSearch_1.SearchUser)(keyword, options.cookie, options?.page, options?.proxy);
|
|
51
|
+
return {
|
|
52
|
+
...userResults,
|
|
53
|
+
result: userResults.result?.map((user) => ({
|
|
54
|
+
type: "user",
|
|
55
|
+
...user
|
|
56
|
+
}))
|
|
57
|
+
};
|
|
58
|
+
case constants_1.SEARCH_TYPES.LIVE:
|
|
59
|
+
const liveResults = await (0, liveSearch_1.SearchLive)(keyword, options.cookie, options?.page, options?.proxy);
|
|
60
|
+
return {
|
|
61
|
+
...liveResults,
|
|
62
|
+
result: liveResults.result?.map((live) => ({
|
|
63
|
+
type: "live",
|
|
64
|
+
...live
|
|
65
|
+
}))
|
|
66
|
+
};
|
|
67
|
+
case constants_1.SEARCH_TYPES.VIDEO:
|
|
68
|
+
const videoResults = await (0, videoSearch_1.SearchVideo)(keyword, options.cookie, options?.page, options?.proxy);
|
|
69
|
+
return {
|
|
70
|
+
...videoResults,
|
|
71
|
+
result: videoResults.result?.map((video) => ({
|
|
72
|
+
type: "video",
|
|
73
|
+
...video
|
|
74
|
+
}))
|
|
75
|
+
};
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(constants_2.ERROR_MESSAGES.INVALID_SEARCH_TYPE);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
status: "error",
|
|
83
|
+
message: error.message
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
StalkUser: async (username, options) => {
|
|
88
|
+
return await (0, getProfile_1.StalkUser)(username, options?.proxy);
|
|
89
|
+
},
|
|
90
|
+
GetVideoComments: async (url, options) => {
|
|
91
|
+
return await (0, getComments_1.getComments)(url, options?.proxy, options?.commentLimit);
|
|
92
|
+
},
|
|
93
|
+
GetUserPosts: async (username, options) => {
|
|
94
|
+
return await (0, getUserPosts_1.getUserPosts)(username, options?.proxy, options?.postLimit);
|
|
95
|
+
},
|
|
96
|
+
GetUserLiked: async (username, options) => {
|
|
97
|
+
if (!(0, validator_1.validateCookie)(options?.cookie)) {
|
|
98
|
+
return handleError(constants_2.ERROR_MESSAGES.COOKIE_REQUIRED);
|
|
99
|
+
}
|
|
100
|
+
return await (0, getUserLiked_1.getUserLiked)(username, options.cookie, options?.proxy, options?.postLimit);
|
|
101
|
+
},
|
|
102
|
+
GetUserReposts: async (username, options) => {
|
|
103
|
+
return await (0, getUserRepost_1.getUserReposts)(username, options?.proxy, options?.postLimit, options?.filterDeletedPost);
|
|
104
|
+
},
|
|
105
|
+
Collection: async (collectionIdOrUrl, options) => {
|
|
106
|
+
const collectionId = (0, urlExtractors_1.extractCollectionId)(collectionIdOrUrl);
|
|
107
|
+
if (!collectionId) {
|
|
108
|
+
return {
|
|
109
|
+
status: "error",
|
|
110
|
+
message: "Invalid collection ID or URL format"
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return await (0, getCollection_1.getCollection)(collectionId, options?.proxy, options?.page, options?.count);
|
|
114
|
+
},
|
|
115
|
+
Playlist: async (playlistIdOrUrl, options) => {
|
|
116
|
+
const playlistId = (0, urlExtractors_1.extractPlaylistId)(playlistIdOrUrl);
|
|
117
|
+
if (!playlistId) {
|
|
118
|
+
return {
|
|
119
|
+
status: "error",
|
|
120
|
+
message: "Invalid playlist ID or URL format"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return await (0, getPlaylist_1.getPlaylist)(playlistId, options?.proxy, options?.page, options?.count);
|
|
124
|
+
},
|
|
125
|
+
Trending: async (options) => {
|
|
126
|
+
return await (0, getTrendings_1.getTrendings)(options?.proxy);
|
|
127
|
+
},
|
|
128
|
+
GetVideosByMusicId: async (musicIdOrUrl, options) => {
|
|
129
|
+
return await (0, getMusicVideos_1.getMusicVideos)(musicIdOrUrl, options?.proxy, options?.page, options?.count);
|
|
130
|
+
},
|
|
131
|
+
GetMusicDetail: async (musicIdOrUrl, options) => {
|
|
132
|
+
return await (0, getMusicDetail_1.getMusicDetail)(musicIdOrUrl, options.cookie, options?.proxy);
|
|
133
|
+
},
|
|
134
|
+
TrendingCreators: async (options) => {
|
|
135
|
+
return await (0, getTrendings_1.getTrendingCreators)(options?.proxy);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export declare class Logger {
|
|
3
|
+
static success(message: string): void;
|
|
4
|
+
static error(message: string): void;
|
|
5
|
+
static info(message: string): void;
|
|
6
|
+
static warning(message: string): void;
|
|
7
|
+
static result(message: string, color?: chalk.Chalk): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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.Logger = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
class Logger {
|
|
9
|
+
static success(message) {
|
|
10
|
+
console.log(chalk_1.default.green("✓ " + message));
|
|
11
|
+
}
|
|
12
|
+
static error(message) {
|
|
13
|
+
console.error(chalk_1.default.red("✗ " + message));
|
|
14
|
+
}
|
|
15
|
+
static info(message) {
|
|
16
|
+
console.log(chalk_1.default.blue("ℹ " + message));
|
|
17
|
+
}
|
|
18
|
+
static warning(message) {
|
|
19
|
+
console.log(chalk_1.default.yellow("⚠ " + message));
|
|
20
|
+
}
|
|
21
|
+
static result(message, color = chalk_1.default.cyan) {
|
|
22
|
+
console.log(color(message));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Logger = Logger;
|
|
@@ -0,0 +1,51 @@
|
|
|
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.CookieManager = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
class CookieManager {
|
|
10
|
+
constructor() {
|
|
11
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
12
|
+
const cookieDir = path_1.default.join(homeDir, ".tiktok-api");
|
|
13
|
+
if (!fs_1.default.existsSync(cookieDir)) {
|
|
14
|
+
fs_1.default.mkdirSync(cookieDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
this.cookieFile = path_1.default.join(cookieDir, "cookies.json");
|
|
17
|
+
this.cookieData = this.loadCookies();
|
|
18
|
+
}
|
|
19
|
+
loadCookies() {
|
|
20
|
+
try {
|
|
21
|
+
if (fs_1.default.existsSync(this.cookieFile)) {
|
|
22
|
+
const data = fs_1.default.readFileSync(this.cookieFile, "utf8");
|
|
23
|
+
return JSON.parse(data);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error("Error loading cookies:", error);
|
|
28
|
+
}
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
saveCookies() {
|
|
32
|
+
try {
|
|
33
|
+
fs_1.default.writeFileSync(this.cookieFile, JSON.stringify(this.cookieData, null, 2));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error("Error saving cookies:", error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
setCookie(value) {
|
|
40
|
+
this.cookieData["tiktok"] = value;
|
|
41
|
+
this.saveCookies();
|
|
42
|
+
}
|
|
43
|
+
getCookie() {
|
|
44
|
+
return this.cookieData["tiktok"] || null;
|
|
45
|
+
}
|
|
46
|
+
deleteCookie() {
|
|
47
|
+
delete this.cookieData["tiktok"];
|
|
48
|
+
this.saveCookies();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.CookieManager = CookieManager;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare function getDefaultDownloadPath(): string;
|
|
2
|
+
declare function downloadMedia(url: string, outputPath: string, filename: string, cookie?: string): Promise<void>;
|
|
3
|
+
declare function handleMediaDownload(data: any, outputPath: string, version: string): Promise<void>;
|
|
4
|
+
declare function downloadMusicFromDetail(musicIdOrUrl: string, cookie: string | any[], outputPath: string, proxy?: string): Promise<void>;
|
|
5
|
+
export { getDefaultDownloadPath, downloadMedia, handleMediaDownload, downloadMusicFromDetail };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getDefaultDownloadPath = getDefaultDownloadPath;
|
|
40
|
+
exports.downloadMedia = downloadMedia;
|
|
41
|
+
exports.handleMediaDownload = handleMediaDownload;
|
|
42
|
+
exports.downloadMusicFromDetail = downloadMusicFromDetail;
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const os = __importStar(require("os"));
|
|
45
|
+
const axios_1 = __importDefault(require("axios"));
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const logger_1 = require("../lib/logger");
|
|
48
|
+
function getDefaultDownloadPath() {
|
|
49
|
+
const platform = os.platform();
|
|
50
|
+
const homeDir = os.homedir();
|
|
51
|
+
switch (platform) {
|
|
52
|
+
case "win32":
|
|
53
|
+
return path.join(homeDir, "Downloads");
|
|
54
|
+
case "darwin":
|
|
55
|
+
return path.join(homeDir, "Downloads");
|
|
56
|
+
case "linux":
|
|
57
|
+
if (process.env.PREFIX && process.env.PREFIX.includes("com.termux")) {
|
|
58
|
+
return path.join(homeDir, "storage", "downloads");
|
|
59
|
+
}
|
|
60
|
+
return path.join(homeDir, "Downloads");
|
|
61
|
+
case "android":
|
|
62
|
+
if (process.env.EXTERNAL_STORAGE) {
|
|
63
|
+
return path.join(process.env.EXTERNAL_STORAGE, "Download");
|
|
64
|
+
}
|
|
65
|
+
return path.join(homeDir, "Download");
|
|
66
|
+
default:
|
|
67
|
+
const possiblePaths = [
|
|
68
|
+
path.join(homeDir, "Downloads"),
|
|
69
|
+
path.join(homeDir, "Download"),
|
|
70
|
+
path.join(homeDir, "downloads"),
|
|
71
|
+
homeDir
|
|
72
|
+
];
|
|
73
|
+
for (const downloadPath of possiblePaths) {
|
|
74
|
+
if (fs.existsSync(downloadPath)) {
|
|
75
|
+
return downloadPath;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return path.join(homeDir, "Downloads");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function downloadMedia(url, outputPath, filename, cookie) {
|
|
82
|
+
try {
|
|
83
|
+
const headers = {};
|
|
84
|
+
if (cookie) {
|
|
85
|
+
headers.Cookie = Array.isArray(cookie) ? cookie.join("; ") : cookie;
|
|
86
|
+
headers["User-Agent"] =
|
|
87
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
88
|
+
headers.Referer = "https://www.tiktok.com/";
|
|
89
|
+
}
|
|
90
|
+
const response = await (0, axios_1.default)({
|
|
91
|
+
method: "GET",
|
|
92
|
+
url: url,
|
|
93
|
+
responseType: "stream",
|
|
94
|
+
headers: headers
|
|
95
|
+
});
|
|
96
|
+
if (!fs.existsSync(outputPath)) {
|
|
97
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
const writer = fs.createWriteStream(path.join(outputPath, filename));
|
|
100
|
+
response.data.pipe(writer);
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
writer.on("finish", resolve);
|
|
103
|
+
writer.on("error", reject);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
throw new Error(`Failed to download media: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function handleMediaDownload(data, outputPath, version) {
|
|
111
|
+
if (data.status !== "success") {
|
|
112
|
+
throw new Error(data.message);
|
|
113
|
+
}
|
|
114
|
+
const { result } = data;
|
|
115
|
+
const author = result.author;
|
|
116
|
+
const username = version === "v1" ? author.username : author?.nickname || "";
|
|
117
|
+
logger_1.Logger.success(`${result.type.charAt(0).toUpperCase() + result.type.slice(1)} Successfully Fetched!`);
|
|
118
|
+
logger_1.Logger.info(`Media Type: ${result.type}`);
|
|
119
|
+
switch (result.type) {
|
|
120
|
+
case "video": {
|
|
121
|
+
const videoUrl = version === "v1"
|
|
122
|
+
? result.video.playAddr[0]
|
|
123
|
+
: version === "v2"
|
|
124
|
+
? result.video.playAddr[0]
|
|
125
|
+
: result.videoHD;
|
|
126
|
+
const videoName = `ttdl_${username}_${Date.now()}.mp4`;
|
|
127
|
+
logger_1.Logger.info("Downloading video...");
|
|
128
|
+
await downloadMedia(videoUrl, outputPath, videoName);
|
|
129
|
+
logger_1.Logger.success(`Video downloaded successfully to: ${path.join(outputPath, videoName)}`);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case "image": {
|
|
133
|
+
const userOutputPath = path.join(outputPath, `${username}_${Date.now()}`);
|
|
134
|
+
const images = result.images;
|
|
135
|
+
for (let i = 0; i < images.length; i++) {
|
|
136
|
+
const imageName = `ttdl_${username}_${Date.now()}_${i + 1}.png`;
|
|
137
|
+
logger_1.Logger.info(`Downloading image ${i + 1}/${images.length}...`);
|
|
138
|
+
await downloadMedia(images[i], userOutputPath, imageName);
|
|
139
|
+
logger_1.Logger.success(`Image downloaded successfully to: ${path.join(userOutputPath, imageName)}`);
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case "music": {
|
|
144
|
+
const musicName = `ttdl_${username}_${Date.now()}.mp3`;
|
|
145
|
+
logger_1.Logger.info("Downloading music...");
|
|
146
|
+
await downloadMedia(result.music, outputPath, musicName);
|
|
147
|
+
logger_1.Logger.success(`Music downloaded successfully to: ${path.join(outputPath, musicName)}`);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
default:
|
|
151
|
+
throw new Error(`Unsupported media type: ${result.type}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function downloadMusicFromDetail(musicIdOrUrl, cookie, outputPath, proxy) {
|
|
155
|
+
try {
|
|
156
|
+
logger_1.Logger.info(`Fetching music detail for: ${musicIdOrUrl}`);
|
|
157
|
+
const { getMusicDetail } = await import("../utils/get/getMusicDetail.js");
|
|
158
|
+
const result = await getMusicDetail(musicIdOrUrl, cookie, proxy);
|
|
159
|
+
if (result.status !== "success" || !result.result) {
|
|
160
|
+
throw new Error(result.message || "Failed to fetch music detail");
|
|
161
|
+
}
|
|
162
|
+
const { musicInfo } = result.result;
|
|
163
|
+
const music = musicInfo.music;
|
|
164
|
+
const author = musicInfo.artist || musicInfo.author;
|
|
165
|
+
logger_1.Logger.success("Music detail fetched successfully!");
|
|
166
|
+
logger_1.Logger.info(`Title: ${music.title}`);
|
|
167
|
+
logger_1.Logger.info(`Author: ${music.authorName}`);
|
|
168
|
+
logger_1.Logger.info(`Duration: ${music.duration} seconds`);
|
|
169
|
+
const safeTitle = music.title.replace(/[^a-z0-9]/gi, "_").substring(0, 50);
|
|
170
|
+
const safeAuthor = (author?.nickname || music.authorName || "Unknown")
|
|
171
|
+
.replace(/[^a-z0-9]/gi, "_")
|
|
172
|
+
.substring(0, 30);
|
|
173
|
+
const musicName = `ttdl_music_${safeAuthor}_${safeTitle}_${Date.now()}.mp3`;
|
|
174
|
+
logger_1.Logger.info("Downloading music...");
|
|
175
|
+
const cookieString = Array.isArray(cookie) ? cookie.join("; ") : cookie;
|
|
176
|
+
await downloadMedia(music.playUrl, outputPath, musicName, cookieString);
|
|
177
|
+
logger_1.Logger.success(`Music downloaded successfully to: ${path.join(outputPath, musicName)}`);
|
|
178
|
+
logger_1.Logger.info("\n==== MUSIC INFO ====");
|
|
179
|
+
logger_1.Logger.info(`Title: ${music.title}`);
|
|
180
|
+
logger_1.Logger.info(`Author: @${author?.uniqueId || "Unknown"} (${author?.nickname || music.authorName})`);
|
|
181
|
+
logger_1.Logger.info(`Duration: ${music.duration} seconds`);
|
|
182
|
+
logger_1.Logger.info(`Original: ${music.original ? "Yes" : "No"}`);
|
|
183
|
+
logger_1.Logger.info(`Videos using this music: ${musicInfo.stats.videoCount}`);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
throw new Error(`Failed to download music: ${error.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class TiktokService {
|
|
2
|
+
generateSignature(url: URL): string;
|
|
3
|
+
generateXBogus(url: URL, signature?: string): string;
|
|
4
|
+
generateXTTParams(params: any): string;
|
|
5
|
+
generateURLXbogus(username: string, page: number): string;
|
|
6
|
+
private getJsdomOptions;
|
|
7
|
+
private static readonly FILE_PATH;
|
|
8
|
+
private static readonly BASE_URL;
|
|
9
|
+
private static readonly AES_KEY;
|
|
10
|
+
private static readonly AES_IV;
|
|
11
|
+
private signaturejs;
|
|
12
|
+
private webmssdk;
|
|
13
|
+
private resourceLoader;
|
|
14
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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.TiktokService = void 0;
|
|
7
|
+
const jsdom_1 = require("jsdom");
|
|
8
|
+
const params_1 = require("../constants/params");
|
|
9
|
+
const xbogus_1 = __importDefault(require("../../helper/xbogus"));
|
|
10
|
+
const headers_1 = require("../constants/headers");
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const crypto_1 = require("crypto");
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
class TiktokService {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.signaturejs = fs_1.default.readFileSync(path_1.default.join(TiktokService.FILE_PATH, "signature.js"), "utf-8");
|
|
17
|
+
this.webmssdk = fs_1.default.readFileSync(path_1.default.join(TiktokService.FILE_PATH, "webmssdk.js"), "utf-8");
|
|
18
|
+
this.resourceLoader = new jsdom_1.ResourceLoader({
|
|
19
|
+
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35"
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
generateSignature(url) {
|
|
23
|
+
const stringUrl = url.toString();
|
|
24
|
+
const jsdomOptions = this.getJsdomOptions();
|
|
25
|
+
const { window } = new jsdom_1.JSDOM(``, jsdomOptions);
|
|
26
|
+
let _window = window;
|
|
27
|
+
_window.eval(this.signaturejs.toString());
|
|
28
|
+
_window.byted_acrawler.init({
|
|
29
|
+
aid: 24,
|
|
30
|
+
dfp: true
|
|
31
|
+
});
|
|
32
|
+
_window.eval(this.webmssdk);
|
|
33
|
+
const signature = _window.byted_acrawler.sign({ url: stringUrl });
|
|
34
|
+
return signature;
|
|
35
|
+
}
|
|
36
|
+
generateXBogus(url, signature) {
|
|
37
|
+
const jsdomOptions = this.getJsdomOptions();
|
|
38
|
+
const { window } = new jsdom_1.JSDOM(``, jsdomOptions);
|
|
39
|
+
let _window = window;
|
|
40
|
+
_window.eval(this.signaturejs.toString());
|
|
41
|
+
_window.byted_acrawler.init({
|
|
42
|
+
aid: 24,
|
|
43
|
+
dfp: true
|
|
44
|
+
});
|
|
45
|
+
_window.eval(this.webmssdk);
|
|
46
|
+
if (signature) {
|
|
47
|
+
url.searchParams.append("_signature", signature);
|
|
48
|
+
}
|
|
49
|
+
const xbogus = _window._0x32d649(url.searchParams.toString());
|
|
50
|
+
return xbogus;
|
|
51
|
+
}
|
|
52
|
+
generateXTTParams(params) {
|
|
53
|
+
const cipher = (0, crypto_1.createCipheriv)("aes-128-cbc", TiktokService.AES_KEY, TiktokService.AES_IV);
|
|
54
|
+
return Buffer.concat([cipher.update(params), cipher.final()]).toString("base64");
|
|
55
|
+
}
|
|
56
|
+
generateURLXbogus(username, page) {
|
|
57
|
+
const baseUrl = `${TiktokService.BASE_URL}api/search/user/full/?`;
|
|
58
|
+
const queryParams = (0, params_1._userSearchParams)(username, page);
|
|
59
|
+
const xbogusParams = (0, xbogus_1.default)(`${baseUrl}${queryParams}`, headers_1.userAgent);
|
|
60
|
+
return `${baseUrl}${(0, params_1._userSearchParams)(username, page, xbogusParams)}`;
|
|
61
|
+
}
|
|
62
|
+
getJsdomOptions() {
|
|
63
|
+
return {
|
|
64
|
+
url: TiktokService.BASE_URL,
|
|
65
|
+
referrer: TiktokService.BASE_URL,
|
|
66
|
+
contentType: "text/html",
|
|
67
|
+
includeNodeLocations: false,
|
|
68
|
+
runScripts: "outside-only",
|
|
69
|
+
pretendToBeVisual: true,
|
|
70
|
+
resources: new jsdom_1.ResourceLoader({ userAgent: headers_1.webUserAgent })
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.TiktokService = TiktokService;
|
|
75
|
+
TiktokService.FILE_PATH = path_1.default.join(__dirname, "../../helper");
|
|
76
|
+
TiktokService.BASE_URL = "https://www.tiktok.com/";
|
|
77
|
+
TiktokService.AES_KEY = "webapp1.0+202106";
|
|
78
|
+
TiktokService.AES_IV = "webapp1.0+202106";
|