commit
88bff6b727
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ pnpm-lock.yaml
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
lib
|
lib
|
||||||
|
!src/lib
|
||||||
test.js
|
test.js
|
||||||
bun.lockb
|
bun.lockb
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
|||||||
24
CHANGELOG.md
24
CHANGELOG.md
@ -371,3 +371,27 @@
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Update Documentation [4260ea6](https://github.com/TobyG74/tiktok-api-dl/commit/4260ea653ee50569f898cc0653cb35e4557992a9)
|
- Update Documentation [4260ea6](https://github.com/TobyG74/tiktok-api-dl/commit/4260ea653ee50569f898cc0653cb35e4557992a9)
|
||||||
|
|
||||||
|
### Version 1.3.2 - 03-06-2025
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add Tiktok Collection [5bd743a](https://github.com/TobyG74/tiktok-api-dl/pull/42/commits/5bd743a888cfafe932f083a2f887dbdd98e99d0c)
|
||||||
|
- Add Tiktok Collection Documentation [a37640e](https://github.com/TobyG74/tiktok-api-dl/pull/42/commits/a37640e332a43827bca8599881c931097d07256e)
|
||||||
|
- Add Tiktok Collection Types [baa8fa2](https://github.com/TobyG74/tiktok-api-dl/pull/42/commits/baa8fa2cc8d1bcc7aabc7fdef5a93677fed10be5)
|
||||||
|
- Add Tiktok Playlist [2fa9e6f](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/2fa9e6fef414c2825cf3072655998e1322210062)
|
||||||
|
- Add Tiktok Playlist Documentation [d0b1f2c](https://github.com/TobyG74/tiktok-api-dl/commit/db1686fc9b0480402db7bd6bd0c41f899ff56668)
|
||||||
|
- Add a New CLI Downloader for Tiktok Collection & Playlist [ae53775](https://github.com/TobyG74/tiktok-api-dl/commit/ae537757d8fa274e52060f445d4822c617f9ac93)
|
||||||
|
- Add Some Test Files [4f7cd9f](https://github.com/TobyG74/tiktok-api-dl/commit/4f7cd9f083d1798c18a0cad823b2e9b224893f14)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Repair Musicaldown Broken Logics [732c0d4](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/732c0d4c146d7ed743b5902fbd392717e2df5692)
|
||||||
|
- Fix Downloader Types [1ad6d8b](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/1ad6d8baea7c2ac45ffc6cccd089ce193d17f491)
|
||||||
|
- Fix Handle Downloader Function [a56bc8c](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/a56bc8c9d9bedd42305c92a7bb15edb3ecad390a)
|
||||||
|
- Fix Duplicate Types [9d53637](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/9d53637ac267568fc0f2d3c4556dd2367b4cbf19)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Moving the Function to the Function Owner's File [c83e329](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/c83e329f27bd744dd2274604e15e10ec2264e083)
|
||||||
|
- Removing Cookie Options on Tiktok Stalk User [c7af8d5](https://github.com/TobyG74/tiktok-api-dl/pull/45/commits/c7af8d53a99fbbf1bda453a9ee1293bfb7ac6cf4)
|
||||||
|
|||||||
249
README.md
249
README.md
@ -46,6 +46,8 @@
|
|||||||
- [Tiktok Video User Comments](#tiktok-video-comments)
|
- [Tiktok Video User Comments](#tiktok-video-comments)
|
||||||
- [Tiktok Get User Posts](#tiktok-get-user-posts)
|
- [Tiktok Get User Posts](#tiktok-get-user-posts)
|
||||||
- [Tiktok Get User Favorite Videos](#tiktok-get-user-favorite-videos)
|
- [Tiktok Get User Favorite Videos](#tiktok-get-user-favorite-videos)
|
||||||
|
- [Tiktok Collection](#tiktok-collection)
|
||||||
|
- [Tiktok Playlist](#tiktok-playlist)
|
||||||
- [API Response Types](#api-response-types)
|
- [API Response Types](#api-response-types)
|
||||||
- [Tiktok Downloader](#tiktok-downloader-1)
|
- [Tiktok Downloader](#tiktok-downloader-1)
|
||||||
- [Version 1 Response](#version-1-response)
|
- [Version 1 Response](#version-1-response)
|
||||||
@ -182,6 +184,22 @@ Tiktok.Downloader(url, {
|
|||||||
}).then((result) => console.log(result))
|
}).then((result) => console.log(result))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download Tiktok Video
|
||||||
|
tiktokdl download "https://vt.tiktok.com/xxxxxxxx"
|
||||||
|
|
||||||
|
# Download Tiktok Video with version
|
||||||
|
tiktokdl download "https://vt.tiktok.com/xxxxxxxx" -v v1
|
||||||
|
|
||||||
|
# Download Tiktok Video with Custom Output Directory Path
|
||||||
|
tiktokdl download "https://vt.tiktok.com/xxxxxxxx" -v v1 -o "/path/to/save/video.mp4"
|
||||||
|
|
||||||
|
# Download Tiktok Video with Proxy
|
||||||
|
tiktokdl download "https://vt.tiktok.com/xxxxxxxx" -v v1 -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
- [Version 1 Response](#version-1-response)
|
- [Version 1 Response](#version-1-response)
|
||||||
- [Version 2 Response](#version-2-response)
|
- [Version 2 Response](#version-2-response)
|
||||||
- [Version 3 Response](#version-3-response)
|
- [Version 3 Response](#version-3-response)
|
||||||
@ -192,15 +210,47 @@ Tiktok.Downloader(url, {
|
|||||||
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
||||||
|
|
||||||
Tiktok.Search("username", {
|
Tiktok.Search("username", {
|
||||||
type: "user", // "user" | "live"
|
type: "user", // "user" | "live" | "video"
|
||||||
page: 1,
|
page: 1,
|
||||||
cookie: "YOUR_COOKIE", // needed
|
cookie: "YOUR_COOKIE", // needed
|
||||||
proxy: "YOUR_PROXY" // optional
|
proxy: "YOUR_PROXY" // optional
|
||||||
}).then((result) => console.log(result))
|
}).then((result) => console.log(result))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search Tiktok Users
|
||||||
|
tiktokdl search user <username>
|
||||||
|
|
||||||
|
# Search Tiktok Users with pagination
|
||||||
|
tiktokdl search user <username> -p 1
|
||||||
|
|
||||||
|
# Search Tiktok Users with proxy
|
||||||
|
tiktokdl search user <username> -p 1 -proxy "http://your-proxy-url"
|
||||||
|
|
||||||
|
# Search Tiktok Live Streams
|
||||||
|
tiktokdl search live <username>
|
||||||
|
|
||||||
|
# Search Tiktok Live Streams with pagination
|
||||||
|
tiktokdl search live <username> -p 1
|
||||||
|
|
||||||
|
# Search Tiktok Live Streams with proxy
|
||||||
|
tiktokdl search live <username> -p 1 -proxy "http://your-proxy-url"
|
||||||
|
|
||||||
|
# Search Tiktok Videos
|
||||||
|
tiktokdl search video <query>
|
||||||
|
|
||||||
|
# Search Tiktok Videos with pagination
|
||||||
|
tiktokdl search video <query> -p 1
|
||||||
|
|
||||||
|
# Search Tiktok Videos with proxy
|
||||||
|
tiktokdl search video <query> -p 1 -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
- [User Search Response](#user-search-response)
|
- [User Search Response](#user-search-response)
|
||||||
- [Live Search Response](live-search-response)
|
- [Live Search Response](live-search-response)
|
||||||
|
- [Video Search Response](#video-search-response)
|
||||||
|
|
||||||
## Tiktok Stalk User Profile
|
## Tiktok Stalk User Profile
|
||||||
|
|
||||||
@ -208,12 +258,21 @@ Tiktok.Search("username", {
|
|||||||
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
||||||
|
|
||||||
const username = "Tobz2k19"
|
const username = "Tobz2k19"
|
||||||
Tiktok.Stalker(username, {
|
Tiktok.StalkUser(username, {
|
||||||
cookie: "YOUR_COOKIE", // optional, if response null
|
|
||||||
proxy: "YOUR_PROXY" // optional
|
proxy: "YOUR_PROXY" // optional
|
||||||
}).then((result) => console.log(result))
|
}).then((result) => console.log(result))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stalk User Profile
|
||||||
|
tiktokdl stalk <username>
|
||||||
|
|
||||||
|
# Stalk User Profile with proxy
|
||||||
|
tiktokdl stalk <username> -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
- [Tiktok Stalk User Response](#tiktok-stalk-user-profile-1)
|
- [Tiktok Stalk User Response](#tiktok-stalk-user-profile-1)
|
||||||
|
|
||||||
## Tiktok Video Comments
|
## Tiktok Video Comments
|
||||||
@ -228,6 +287,19 @@ Tiktok.GetVideoComments(url, {
|
|||||||
}).then((result) => console.log(result))
|
}).then((result) => console.log(result))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get Video Comments
|
||||||
|
tiktokdl getvideocomments "https://vt.tiktok.com/xxxxxxxx"
|
||||||
|
|
||||||
|
# Get Video Comments with limit of comments
|
||||||
|
tiktokdl getvideocomments "https://vt.tiktok.com/xxxxxxxx" -l 10
|
||||||
|
|
||||||
|
# Get Video Comments with proxy
|
||||||
|
tiktokdl getvideocomments "https://vt.tiktok.com/xxxxxxxx" -l 10 -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
- [Tiktok Video Comments Response](#tiktok-video-comments-1)
|
- [Tiktok Video Comments Response](#tiktok-video-comments-1)
|
||||||
|
|
||||||
## Tiktok Get User Posts
|
## Tiktok Get User Posts
|
||||||
@ -242,6 +314,19 @@ Tiktok.GetUserPosts(username, {
|
|||||||
}).then((result) => console.log(result))
|
}).then((result) => console.log(result))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get User Posts
|
||||||
|
tiktokdl getuserposts <username>
|
||||||
|
|
||||||
|
# Get User Posts with limit of posts
|
||||||
|
tiktokdl getuserposts <username> -l 10
|
||||||
|
|
||||||
|
# Get User Posts with proxy
|
||||||
|
tiktokdl getuserposts <username> -l 10 -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
- [Tiktok User Posts Response](#tiktok-user-posts)
|
- [Tiktok User Posts Response](#tiktok-user-posts)
|
||||||
|
|
||||||
## Tiktok Get User Liked Videos
|
## Tiktok Get User Liked Videos
|
||||||
@ -259,6 +344,19 @@ Tiktok.GetUserLiked(username, {
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get User Liked Videos
|
||||||
|
tiktokdl getuserliked <username>
|
||||||
|
|
||||||
|
# Get User Liked Videos with limit of posts
|
||||||
|
tiktokdl getuserliked <username> -l 10
|
||||||
|
|
||||||
|
# Get User Liked Videos with proxy
|
||||||
|
tiktokdl getuserliked <username> -l 10 -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
- [Tiktok User Liked Videos Response](#tiktok-user-liked-videos)
|
- [Tiktok User Liked Videos Response](#tiktok-user-liked-videos)
|
||||||
|
|
||||||
## Tiktok Collection
|
## Tiktok Collection
|
||||||
@ -301,59 +399,41 @@ tiktokdl collection 7507916135931218695 -p 1 -n 5
|
|||||||
tiktokdl collection 7507916135931218695 -n 5 -proxy "http://your-proxy-url"
|
tiktokdl collection 7507916135931218695 -n 5 -proxy "http://your-proxy-url"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Response Type
|
- [Tiktok Collection Response](#tiktok-collection-1)
|
||||||
|
|
||||||
```typescript
|
## Tiktok Playlist
|
||||||
interface TiktokCollectionResponse {
|
|
||||||
status: "success" | "error"
|
Get videos from a TikTok playlist (supports playlist ID or URL)
|
||||||
message?: string
|
|
||||||
result?: {
|
```javascript
|
||||||
itemList: Array<{
|
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
||||||
id: string
|
|
||||||
desc: string
|
const playlistIdOrUrl = "https://www.tiktok.com/@username/playlist/name-id"
|
||||||
createTime: number
|
Tiktok.Playlist(playlistIdOrUrl, {
|
||||||
author?: {
|
page: 1,
|
||||||
id: string
|
count: 5,
|
||||||
uniqueId: string
|
proxy: "YOUR_PROXY"
|
||||||
nickname: string
|
}).then((result) => console.log(result))
|
||||||
avatarThumb: string
|
|
||||||
avatarMedium: string
|
|
||||||
avatarLarger: string
|
|
||||||
signature: string
|
|
||||||
verified: boolean
|
|
||||||
}
|
|
||||||
statistics?: {
|
|
||||||
playCount: number
|
|
||||||
diggCount: number
|
|
||||||
shareCount: number
|
|
||||||
commentCount: number
|
|
||||||
collectCount: number
|
|
||||||
}
|
|
||||||
video?: {
|
|
||||||
id: string
|
|
||||||
height: number
|
|
||||||
width: number
|
|
||||||
duration: number
|
|
||||||
ratio: string
|
|
||||||
cover: string
|
|
||||||
originCover: string
|
|
||||||
dynamicCover: string
|
|
||||||
playAddr: string
|
|
||||||
downloadAddr: string
|
|
||||||
format: string
|
|
||||||
bitrate: number
|
|
||||||
}
|
|
||||||
textExtra?: Array<{
|
|
||||||
hashtagName: string
|
|
||||||
hashtagId: string
|
|
||||||
type: number
|
|
||||||
}>
|
|
||||||
}>
|
|
||||||
hasMore: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using playlist ID
|
||||||
|
tiktokdl playlist 7507916135931218695 -n 5
|
||||||
|
|
||||||
|
# Using playlist URL
|
||||||
|
tiktokdl playlist "https://www.tiktok.com/@username/playlist/name-id" -n 5
|
||||||
|
|
||||||
|
# With page for pagination
|
||||||
|
tiktokdl playlist 7507916135931218695 -p 1 -n 5
|
||||||
|
|
||||||
|
# With proxy
|
||||||
|
tiktokdl playlist 7507916135931218695 -n 5 -proxy "http://your-proxy-url"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [Tiktok Playlist Response](#tiktok-playlist-1)
|
||||||
|
|
||||||
# API Response Types
|
# API Response Types
|
||||||
|
|
||||||
## Tiktok Downloader
|
## Tiktok Downloader
|
||||||
@ -446,8 +526,12 @@ interface SSSTikResponse {
|
|||||||
shareCount: string
|
shareCount: string
|
||||||
}
|
}
|
||||||
images?: string[]
|
images?: string[]
|
||||||
video?: string
|
video?: {
|
||||||
music?: string
|
playAddr: string
|
||||||
|
}
|
||||||
|
music?: {
|
||||||
|
playUrl: string
|
||||||
|
}
|
||||||
direct?: string
|
direct?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -862,6 +946,61 @@ interface TiktokCollectionResponse {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Tiktok Playlist
|
||||||
|
|
||||||
|
### Playlist Response
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
status: "success" | "error"
|
||||||
|
message?: string
|
||||||
|
result?: {
|
||||||
|
hasMore: boolean
|
||||||
|
itemList: Array<{
|
||||||
|
id: string
|
||||||
|
desc: string
|
||||||
|
createTime: number
|
||||||
|
author: PlaylistAuthor
|
||||||
|
stats: Statistics
|
||||||
|
video: VideoTiktokAPI
|
||||||
|
music: MusicTiktokAPI
|
||||||
|
challenges: Array<{
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
desc: string
|
||||||
|
coverLarger: string
|
||||||
|
coverMedium: string
|
||||||
|
coverThumb: string
|
||||||
|
profileLarger: string
|
||||||
|
profileMedium: string
|
||||||
|
profileThumb: string
|
||||||
|
}>
|
||||||
|
collected: boolean
|
||||||
|
digged: boolean
|
||||||
|
duetDisplay: number
|
||||||
|
forFriend: boolean
|
||||||
|
officalItem: boolean
|
||||||
|
originalItem: boolean
|
||||||
|
privateItem: boolean
|
||||||
|
shareEnabled: boolean
|
||||||
|
stitchDisplay: number
|
||||||
|
textExtra: Array<{
|
||||||
|
awemeId: string
|
||||||
|
end: number
|
||||||
|
hashtagName: string
|
||||||
|
isCommerce: boolean
|
||||||
|
start: number
|
||||||
|
subType: number
|
||||||
|
type: number
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
extra?: {
|
||||||
|
fatal_item_ids: string[]
|
||||||
|
logid: string
|
||||||
|
now: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
- All changes will be documented in the [CHANGELOG.md](https://github.com/TobyG74/tiktok-api-dl/blob/master/CHANGELOG.md) file.
|
- All changes will be documented in the [CHANGELOG.md](https://github.com/TobyG74/tiktok-api-dl/blob/master/CHANGELOG.md) file.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tobyg74/tiktok-api-dl",
|
"name": "@tobyg74/tiktok-api-dl",
|
||||||
"version": "1.3.1-fix",
|
"version": "1.3.2",
|
||||||
"description": "Scraper for downloading media in the form of videos, images and audio from Tiktok. Also for stalking Tiktok Users",
|
"description": "Scraper for downloading media in the form of videos, images and audio from Tiktok. Also for stalking Tiktok Users",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
@ -27,10 +27,6 @@
|
|||||||
"tiktok-stalk"
|
"tiktok-stalk"
|
||||||
],
|
],
|
||||||
"author": "Tobz",
|
"author": "Tobz",
|
||||||
"contributors": [
|
|
||||||
"aqulzz",
|
|
||||||
"nugraizy"
|
|
||||||
],
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/TobyG74/tiktok-api-dl/issues"
|
"url": "https://github.com/TobyG74/tiktok-api-dl/issues"
|
||||||
|
|||||||
125
src/cli/index.ts
125
src/cli/index.ts
@ -10,9 +10,6 @@ import {
|
|||||||
handleMediaDownload
|
handleMediaDownload
|
||||||
} from "../services/downloadManager"
|
} from "../services/downloadManager"
|
||||||
import { _tiktokurl } from "../constants/api"
|
import { _tiktokurl } from "../constants/api"
|
||||||
import path from "path"
|
|
||||||
import * as fs from "fs"
|
|
||||||
import axios from "axios"
|
|
||||||
|
|
||||||
const cookieManager = new CookieManager()
|
const cookieManager = new CookieManager()
|
||||||
|
|
||||||
@ -86,7 +83,7 @@ cookieCommand
|
|||||||
|
|
||||||
const searchCommand = program
|
const searchCommand = program
|
||||||
.command("search")
|
.command("search")
|
||||||
.description("Search TikTok users or live streams")
|
.description("Search TikTok users or live streams or videos")
|
||||||
|
|
||||||
searchCommand
|
searchCommand
|
||||||
.command("user")
|
.command("user")
|
||||||
@ -503,7 +500,7 @@ program
|
|||||||
.command("playlist")
|
.command("playlist")
|
||||||
.description("Get videos from a TikTok playlist")
|
.description("Get videos from a TikTok playlist")
|
||||||
.argument(
|
.argument(
|
||||||
"<url>",
|
"<PlaylistIdOrUrl>",
|
||||||
"Collection URL (e.g. https://www.tiktok.com/@username/playlist/name-id)"
|
"Collection URL (e.g. https://www.tiktok.com/@username/playlist/name-id)"
|
||||||
)
|
)
|
||||||
.option("-p, --page <number>", "Page number", "1")
|
.option("-p, --page <number>", "Page number", "1")
|
||||||
@ -590,4 +587,122 @@ program
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Download all items in a TikTok playlist
|
||||||
|
program
|
||||||
|
.command("download-playlist")
|
||||||
|
.description("Download all videos from a TikTok playlist")
|
||||||
|
.argument(
|
||||||
|
"<PlaylistIdOrUrl>",
|
||||||
|
"Playlist URL (e.g. https://www.tiktok.com/@username/playlist/name-id)"
|
||||||
|
)
|
||||||
|
.option("-o, --output <path>", "Output directory path")
|
||||||
|
.option("-v, --version <version>", "Downloader version (v1/v2/v3)", "v1")
|
||||||
|
.option("-p, --proxy <proxy>", "Proxy URL (http/https/socks)")
|
||||||
|
.option(
|
||||||
|
"-c, --count <number>",
|
||||||
|
"Number of items to fetch (max: 20)",
|
||||||
|
(val) => parseInt(val),
|
||||||
|
20
|
||||||
|
)
|
||||||
|
.action(async (url, options) => {
|
||||||
|
try {
|
||||||
|
const outputPath = options.output || getDefaultDownloadPath()
|
||||||
|
const version = options.version.toLowerCase()
|
||||||
|
Logger.info(`Fetching playlist items...`)
|
||||||
|
const results = await Tiktok.Playlist(url, {
|
||||||
|
page: 1,
|
||||||
|
proxy: options.proxy,
|
||||||
|
count: options.count
|
||||||
|
})
|
||||||
|
if (results.status === "success" && results.result) {
|
||||||
|
const { itemList } = results.result
|
||||||
|
Logger.info(
|
||||||
|
`Found ${itemList.length} items in playlist. Starting download...`
|
||||||
|
)
|
||||||
|
for (const [index, item] of itemList.entries()) {
|
||||||
|
Logger.info(
|
||||||
|
`Downloading [${index + 1}/${itemList.length}]: ${item.id}`
|
||||||
|
)
|
||||||
|
const videoUrl = `https://www.tiktok.com/@${
|
||||||
|
item.author?.uniqueId || "unknown"
|
||||||
|
}/video/${item.id}`
|
||||||
|
try {
|
||||||
|
const data = await Tiktok.Downloader(videoUrl, {
|
||||||
|
version: version,
|
||||||
|
proxy: options.proxy
|
||||||
|
})
|
||||||
|
await handleMediaDownload(data, outputPath, version)
|
||||||
|
Logger.success(`Downloaded: ${videoUrl}`)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(`Failed to download ${videoUrl}: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.info("All downloads finished.")
|
||||||
|
} else {
|
||||||
|
Logger.error(`Error: ${results.message}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(`Error: ${error.message}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Download all items in a TikTok collection
|
||||||
|
program
|
||||||
|
.command("download-collection")
|
||||||
|
.description("Download all videos from a TikTok collection")
|
||||||
|
.argument(
|
||||||
|
"<collectionIdOrUrl>",
|
||||||
|
"Collection ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/collection/name-id)"
|
||||||
|
)
|
||||||
|
.option("-o, --output <path>", "Output directory path")
|
||||||
|
.option("-v, --version <version>", "Downloader version (v1/v2/v3)", "v1")
|
||||||
|
.option("-p, --proxy <proxy>", "Proxy URL (http/https/socks)")
|
||||||
|
.option(
|
||||||
|
"-n, --count <number>",
|
||||||
|
"Number of items to fetch",
|
||||||
|
(val) => parseInt(val),
|
||||||
|
20
|
||||||
|
)
|
||||||
|
.action(async (collectionIdOrUrl, options) => {
|
||||||
|
try {
|
||||||
|
const outputPath = options.output || getDefaultDownloadPath()
|
||||||
|
const version = options.version.toLowerCase()
|
||||||
|
Logger.info(`Fetching collection items...`)
|
||||||
|
const results = await Tiktok.Collection(collectionIdOrUrl, {
|
||||||
|
page: 1,
|
||||||
|
proxy: options.proxy,
|
||||||
|
count: options.count
|
||||||
|
})
|
||||||
|
if (results.status === "success" && results.result) {
|
||||||
|
const { itemList } = results.result
|
||||||
|
Logger.info(
|
||||||
|
`Found ${itemList.length} items in collection. Starting download...`
|
||||||
|
)
|
||||||
|
for (const [index, item] of itemList.entries()) {
|
||||||
|
Logger.info(
|
||||||
|
`Downloading [${index + 1}/${itemList.length}]: ${item.id}`
|
||||||
|
)
|
||||||
|
const videoUrl = `https://www.tiktok.com/@${
|
||||||
|
item.author?.uniqueId || "unknown"
|
||||||
|
}/video/${item.id}`
|
||||||
|
try {
|
||||||
|
const data = await Tiktok.Downloader(videoUrl, {
|
||||||
|
version: version,
|
||||||
|
proxy: options.proxy
|
||||||
|
})
|
||||||
|
await handleMediaDownload(data, outputPath, version)
|
||||||
|
Logger.success(`Downloaded: ${videoUrl}`)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(`Failed to download ${videoUrl}: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.info("All downloads finished.")
|
||||||
|
} else {
|
||||||
|
Logger.error(`Error: ${results.message}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(`Error: ${error.message}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
program.parse()
|
program.parse()
|
||||||
|
|||||||
92
src/index.ts
92
src/index.ts
@ -1,7 +1,7 @@
|
|||||||
/** Types */
|
/** Types */
|
||||||
import { TiktokAPIResponse } from "./types/downloader/tiktokApi"
|
import { TiktokAPIResponse } from "./types/downloader/tiktokApiDownloader"
|
||||||
import { SSSTikResponse } from "./types/downloader/ssstik"
|
import { SSSTikResponse } from "./types/downloader/ssstikDownloader"
|
||||||
import { MusicalDownResponse } from "./types/downloader/musicaldown"
|
import { MusicalDownResponse } from "./types/downloader/musicaldownDownloader"
|
||||||
import { UserSearchResult } from "./types/search/userSearch"
|
import { UserSearchResult } from "./types/search/userSearch"
|
||||||
import { LiveSearchResult } from "./types/search/liveSearch"
|
import { LiveSearchResult } from "./types/search/liveSearch"
|
||||||
import { VideoSearchResult } from "./types/search/videoSearch"
|
import { VideoSearchResult } from "./types/search/videoSearch"
|
||||||
@ -12,9 +12,9 @@ import { TiktokUserFavoriteVideosResponse } from "./types/get/getUserLiked"
|
|||||||
import { TiktokCollectionResponse } from "./types/get/getCollection"
|
import { TiktokCollectionResponse } from "./types/get/getCollection"
|
||||||
|
|
||||||
/** Services */
|
/** Services */
|
||||||
import { extractPlaylistId, TiktokAPI } from "./utils/downloader/tiktokApi"
|
import { TiktokAPI } from "./utils/downloader/tiktokAPIDownloader"
|
||||||
import { SSSTik } from "./utils/downloader/ssstik"
|
import { SSSTik } from "./utils/downloader/ssstikDownloader"
|
||||||
import { MusicalDown } from "./utils/downloader/musicalDown"
|
import { MusicalDown } from "./utils/downloader/musicaldownDownloader"
|
||||||
import { StalkUser } from "./utils/get/getProfile"
|
import { StalkUser } from "./utils/get/getProfile"
|
||||||
import { SearchUser } from "./utils/search/userSearch"
|
import { SearchUser } from "./utils/search/userSearch"
|
||||||
import { SearchLive } from "./utils/search/liveSearch"
|
import { SearchLive } from "./utils/search/liveSearch"
|
||||||
@ -23,7 +23,6 @@ import { getUserPosts } from "./utils/get/getUserPosts"
|
|||||||
import { getUserLiked } from "./utils/get/getUserLiked"
|
import { getUserLiked } from "./utils/get/getUserLiked"
|
||||||
import { SearchVideo } from "./utils/search/videoSearch"
|
import { SearchVideo } from "./utils/search/videoSearch"
|
||||||
import { getCollection } from "./utils/get/getCollection"
|
import { getCollection } from "./utils/get/getCollection"
|
||||||
import { extractCollectionId } from "./utils/downloader/tiktokApi"
|
|
||||||
|
|
||||||
/** Constants */
|
/** Constants */
|
||||||
import { DOWNLOADER_VERSIONS, SEARCH_TYPES } from "./constants"
|
import { DOWNLOADER_VERSIONS, SEARCH_TYPES } from "./constants"
|
||||||
@ -31,6 +30,8 @@ import { ERROR_MESSAGES } from "./constants"
|
|||||||
import { validateCookie } from "./utils/validator"
|
import { validateCookie } from "./utils/validator"
|
||||||
import { TiktokPlaylistResponse } from "./types/get/getPlaylist"
|
import { TiktokPlaylistResponse } from "./types/get/getPlaylist"
|
||||||
import { getPlaylist } from "./utils/get/getPlaylist"
|
import { getPlaylist } from "./utils/get/getPlaylist"
|
||||||
|
import { extractPlaylistId } from "./utils/get/getPlaylist"
|
||||||
|
import { extractCollectionId } from "./utils/get/getCollection"
|
||||||
|
|
||||||
/** Types */
|
/** Types */
|
||||||
type DownloaderVersion = "v1" | "v2" | "v3"
|
type DownloaderVersion = "v1" | "v2" | "v3"
|
||||||
@ -82,7 +83,7 @@ export = {
|
|||||||
Downloader: async <T extends DownloaderVersion>(
|
Downloader: async <T extends DownloaderVersion>(
|
||||||
url: string,
|
url: string,
|
||||||
options?: {
|
options?: {
|
||||||
version: DownloaderVersion
|
version: T
|
||||||
proxy?: string
|
proxy?: string
|
||||||
showOriginalResponse?: boolean
|
showOriginalResponse?: boolean
|
||||||
}
|
}
|
||||||
@ -118,38 +119,6 @@ export = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Get TikTok Collection
|
|
||||||
* @param {string} collectionIdOrUrl - Collection ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/collection/name-id)
|
|
||||||
* @param {Object} options - The options for collection
|
|
||||||
* @param {string} [options.proxy] - Optional proxy URL
|
|
||||||
* @param {string} [options.page] - Optional page for pagination
|
|
||||||
* @param {number} [options.count] - Optional number of items to fetch
|
|
||||||
* @returns {Promise<TiktokCollectionResponse>}
|
|
||||||
*/
|
|
||||||
Collection: async (
|
|
||||||
collectionIdOrUrl: string,
|
|
||||||
options?: {
|
|
||||||
proxy?: string
|
|
||||||
page?: number
|
|
||||||
count?: number
|
|
||||||
}
|
|
||||||
): Promise<TiktokCollectionResponse> => {
|
|
||||||
const collectionId = extractCollectionId(collectionIdOrUrl)
|
|
||||||
if (!collectionId) {
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message: "Invalid collection ID or URL format"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return await getCollection(
|
|
||||||
collectionId,
|
|
||||||
options?.proxy,
|
|
||||||
options?.page,
|
|
||||||
options?.count
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tiktok Search
|
* Tiktok Search
|
||||||
* @param {string} keyword - The query you want to search
|
* @param {string} keyword - The query you want to search
|
||||||
@ -164,7 +133,7 @@ export = {
|
|||||||
keyword: string,
|
keyword: string,
|
||||||
options?: {
|
options?: {
|
||||||
type?: T
|
type?: T
|
||||||
cookie?: string
|
cookie: string | any[]
|
||||||
page?: number
|
page?: number
|
||||||
proxy?: string
|
proxy?: string
|
||||||
}
|
}
|
||||||
@ -240,11 +209,10 @@ export = {
|
|||||||
StalkUser: async (
|
StalkUser: async (
|
||||||
username: string,
|
username: string,
|
||||||
options?: {
|
options?: {
|
||||||
cookie?: string | any[]
|
|
||||||
proxy?: string
|
proxy?: string
|
||||||
}
|
}
|
||||||
): Promise<TiktokStalkUserResponse> => {
|
): Promise<TiktokStalkUserResponse> => {
|
||||||
return await StalkUser(username, options?.cookie, options?.proxy)
|
return await StalkUser(username, options?.proxy)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,9 +282,41 @@ export = {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get TikTok Collection
|
||||||
|
* @param {string} collectionIdOrUrl - Collection ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/collection/name-id)
|
||||||
|
* @param {Object} options - The options for collection
|
||||||
|
* @param {string} [options.proxy] - Optional proxy URL
|
||||||
|
* @param {string} [options.page] - Optional page for pagination
|
||||||
|
* @param {number} [options.count] - Optional number of items to fetch
|
||||||
|
* @returns {Promise<TiktokCollectionResponse>}
|
||||||
|
*/
|
||||||
|
Collection: async (
|
||||||
|
collectionIdOrUrl: string,
|
||||||
|
options?: {
|
||||||
|
proxy?: string
|
||||||
|
page?: number
|
||||||
|
count?: number
|
||||||
|
}
|
||||||
|
): Promise<TiktokCollectionResponse> => {
|
||||||
|
const collectionId = extractCollectionId(collectionIdOrUrl)
|
||||||
|
if (!collectionId) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message: "Invalid collection ID or URL format"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await getCollection(
|
||||||
|
collectionId,
|
||||||
|
options?.proxy,
|
||||||
|
options?.page,
|
||||||
|
options?.count
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get TikTok Playlist
|
* Get TikTok Playlist
|
||||||
* @param {string} url - URL (e.g. https://www.tiktok.com/@username/playlist/name-id)
|
* @param {string} playlistIdOrUrl - Playlist ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/playlist/name-id)
|
||||||
* @param {Object} options - The options for playlist
|
* @param {Object} options - The options for playlist
|
||||||
* @param {string} [options.proxy] - Optional proxy URL
|
* @param {string} [options.proxy] - Optional proxy URL
|
||||||
* @param {string} [options.page] - Optional page for pagination
|
* @param {string} [options.page] - Optional page for pagination
|
||||||
@ -324,14 +324,14 @@ export = {
|
|||||||
* @returns {Promise<TiktokPlaylistResponse>}
|
* @returns {Promise<TiktokPlaylistResponse>}
|
||||||
*/
|
*/
|
||||||
Playlist: async (
|
Playlist: async (
|
||||||
url: string,
|
playlistIdOrUrl: string,
|
||||||
options?: {
|
options?: {
|
||||||
proxy?: string
|
proxy?: string
|
||||||
page?: number
|
page?: number
|
||||||
count?: number
|
count?: number
|
||||||
}
|
}
|
||||||
): Promise<TiktokPlaylistResponse> => {
|
): Promise<TiktokPlaylistResponse> => {
|
||||||
const playlistId = extractPlaylistId(url)
|
const playlistId = extractPlaylistId(playlistIdOrUrl)
|
||||||
if (!playlistId) {
|
if (!playlistId) {
|
||||||
return {
|
return {
|
||||||
status: "error",
|
status: "error",
|
||||||
|
|||||||
@ -2,19 +2,19 @@ import chalk from "chalk"
|
|||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
static success(message: string): void {
|
static success(message: string): void {
|
||||||
console.log(chalk.green("✓ " + message))
|
console.log(chalk.green("✓ " + message))
|
||||||
}
|
}
|
||||||
|
|
||||||
static error(message: string): void {
|
static error(message: string): void {
|
||||||
console.error(chalk.red("✗ " + message))
|
console.error(chalk.red("✗ " + message))
|
||||||
}
|
}
|
||||||
|
|
||||||
static info(message: string): void {
|
static info(message: string): void {
|
||||||
console.log(chalk.blue("ℹ " + message))
|
console.log(chalk.blue("ℹ " + message))
|
||||||
}
|
}
|
||||||
|
|
||||||
static warning(message: string): void {
|
static warning(message: string): void {
|
||||||
console.log(chalk.yellow("⚠ " + message))
|
console.log(chalk.yellow("⚠ " + message))
|
||||||
}
|
}
|
||||||
|
|
||||||
static result(message: string, color = chalk.cyan): void {
|
static result(message: string, color = chalk.cyan): void {
|
||||||
|
|||||||
@ -61,7 +61,7 @@ async function handleMediaDownload(
|
|||||||
case "video": {
|
case "video": {
|
||||||
const videoUrl =
|
const videoUrl =
|
||||||
version === "v1"
|
version === "v1"
|
||||||
? result.video.downloadAddr[0]
|
? result.video.playAddr[0]
|
||||||
: version === "v2"
|
: version === "v2"
|
||||||
? result.video.playAddr[0]
|
? result.video.playAddr[0]
|
||||||
: result.videoHD
|
: result.videoHD
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { userAgent, webUserAgent } from "../constants/headers"
|
|||||||
import qs from "qs"
|
import qs from "qs"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import { createCipheriv } from "crypto"
|
import { createCipheriv } from "crypto"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
export class TiktokService {
|
export class TiktokService {
|
||||||
/**
|
/**
|
||||||
@ -82,6 +83,7 @@ export class TiktokService {
|
|||||||
const baseUrl = `${TiktokService.BASE_URL}api/search/user/full/?`
|
const baseUrl = `${TiktokService.BASE_URL}api/search/user/full/?`
|
||||||
const queryParams = _userSearchParams(username, page)
|
const queryParams = _userSearchParams(username, page)
|
||||||
const xbogusParams = xbogus(`${baseUrl}${queryParams}`, userAgent)
|
const xbogusParams = xbogus(`${baseUrl}${queryParams}`, userAgent)
|
||||||
|
console.log(`${baseUrl}${_userSearchParams(username, page, xbogusParams)}`)
|
||||||
|
|
||||||
return `${baseUrl}${_userSearchParams(username, page, xbogusParams)}`
|
return `${baseUrl}${_userSearchParams(username, page, xbogusParams)}`
|
||||||
}
|
}
|
||||||
@ -102,11 +104,18 @@ export class TiktokService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly FILE_PATH = path.join(__dirname, "../../helper")
|
||||||
private static readonly BASE_URL = "https://www.tiktok.com/"
|
private static readonly BASE_URL = "https://www.tiktok.com/"
|
||||||
private static readonly AES_KEY = "webapp1.0+202106"
|
private static readonly AES_KEY = "webapp1.0+202106"
|
||||||
private static readonly AES_IV = "webapp1.0+202106"
|
private static readonly AES_IV = "webapp1.0+202106"
|
||||||
private signaturejs = fs.readFileSync("./helper/signature.js", "utf-8")
|
private signaturejs = fs.readFileSync(
|
||||||
private webmssdk = fs.readFileSync("./helper/webmssdk.js", "utf-8")
|
path.join(TiktokService.FILE_PATH, "signature.js"),
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
private webmssdk = fs.readFileSync(
|
||||||
|
path.join(TiktokService.FILE_PATH, "webmssdk.js"),
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
private resourceLoader = new ResourceLoader({
|
private resourceLoader = new ResourceLoader({
|
||||||
userAgent:
|
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"
|
"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"
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
import { BaseResponse, Content } from "../common"
|
|
||||||
|
|
||||||
export type GetMusicalDownReuqest = BaseResponse & {
|
|
||||||
request?: {
|
|
||||||
[key: string]: string
|
|
||||||
}
|
|
||||||
cookie?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MusicalDownResponse = BaseResponse & {
|
|
||||||
result?: Content
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GetMusicalDownMusic = BaseResponse & {
|
|
||||||
result?: string
|
|
||||||
}
|
|
||||||
32
src/types/downloader/musicaldownDownloader.ts
Normal file
32
src/types/downloader/musicaldownDownloader.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { BaseResponse } from "../common"
|
||||||
|
|
||||||
|
export type GetMusicalDownReuqest = BaseResponse & {
|
||||||
|
request?: {
|
||||||
|
[key: string]: string
|
||||||
|
}
|
||||||
|
cookie?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MusicalDownResponse = BaseResponse & {
|
||||||
|
result?: ContentMusicalDown
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ContentMusicalDown = {
|
||||||
|
type: "video" | "image"
|
||||||
|
author?: AuthorMusicalDown
|
||||||
|
desc?: string
|
||||||
|
images?: string[]
|
||||||
|
videoHD?: string
|
||||||
|
videoSD?: string
|
||||||
|
videoWatermark?: string
|
||||||
|
music?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AuthorMusicalDown = {
|
||||||
|
avatar: string
|
||||||
|
nickname: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetMusicalDownMusic = BaseResponse & {
|
||||||
|
result?: string
|
||||||
|
}
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { BaseResponse, Content, Author, Statistics } from "../common"
|
|
||||||
|
|
||||||
export type SSSTikFetchTT = BaseResponse & {
|
|
||||||
result?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SSSTikResponse = BaseResponse & {
|
|
||||||
result?: Content
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AuthorSSSTik = Author
|
|
||||||
|
|
||||||
export type StatisticsSSSTik = Statistics
|
|
||||||
35
src/types/downloader/ssstikDownloader.ts
Normal file
35
src/types/downloader/ssstikDownloader.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { BaseResponse } from "../common"
|
||||||
|
|
||||||
|
export type SSSTikFetchTT = BaseResponse & {
|
||||||
|
result?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SSSTikResponse = BaseResponse & {
|
||||||
|
result?: ResultContentSSSTik
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResultContentSSSTik = {
|
||||||
|
type: "video" | "music" | "image"
|
||||||
|
desc?: string
|
||||||
|
author?: AuthorSSSTik
|
||||||
|
statistics?: StatisticsSSSTik
|
||||||
|
video?: {
|
||||||
|
playAddr: string[]
|
||||||
|
}
|
||||||
|
music?: {
|
||||||
|
playUrl: string[]
|
||||||
|
}
|
||||||
|
images?: string[]
|
||||||
|
direct?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AuthorSSSTik = {
|
||||||
|
avatar: string
|
||||||
|
nickname: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StatisticsSSSTik = {
|
||||||
|
likeCount: string
|
||||||
|
commentCount: string
|
||||||
|
shareCount: string
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ import {
|
|||||||
StatisticsTiktokAPI,
|
StatisticsTiktokAPI,
|
||||||
MusicTiktokAPI,
|
MusicTiktokAPI,
|
||||||
VideoTiktokAPI
|
VideoTiktokAPI
|
||||||
} from "../downloader/tiktokApi"
|
} from "../downloader/tiktokApiDownloader"
|
||||||
import { PlaylistAuthor } from "./getPlaylist"
|
import { PlaylistAuthor } from "./getPlaylist"
|
||||||
|
|
||||||
export interface CollectionItem {
|
export interface CollectionItem {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {
|
|||||||
AuthorTiktokAPI,
|
AuthorTiktokAPI,
|
||||||
MusicTiktokAPI,
|
MusicTiktokAPI,
|
||||||
VideoTiktokAPI
|
VideoTiktokAPI
|
||||||
} from "../downloader/tiktokApi"
|
} from "../downloader/tiktokApiDownloader"
|
||||||
|
|
||||||
export interface PlaylistAuthor
|
export interface PlaylistAuthor
|
||||||
extends Omit<AuthorTiktokAPI, "username" | "uid"> {
|
extends Omit<AuthorTiktokAPI, "username" | "uid"> {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { load } from "cheerio"
|
|||||||
import {
|
import {
|
||||||
MusicalDownResponse,
|
MusicalDownResponse,
|
||||||
GetMusicalDownReuqest
|
GetMusicalDownReuqest
|
||||||
} from "../../types/downloader/musicaldown"
|
} from "../../types/downloader/musicaldownDownloader"
|
||||||
import { _musicaldownapi, _musicaldownurl } from "../../constants/api"
|
import { _musicaldownapi, _musicaldownurl } from "../../constants/api"
|
||||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
StatisticsSSSTik,
|
StatisticsSSSTik,
|
||||||
SSSTikFetchTT,
|
SSSTikFetchTT,
|
||||||
SSSTikResponse
|
SSSTikResponse
|
||||||
} from "../../types/downloader/ssstik"
|
} from "../../types/downloader/ssstikDownloader"
|
||||||
import { _ssstikapi, _ssstikurl } from "../../constants/api"
|
import { _ssstikapi, _ssstikurl } from "../../constants/api"
|
||||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||||
@ -17,21 +17,17 @@ import {
|
|||||||
StatisticsTiktokAPI,
|
StatisticsTiktokAPI,
|
||||||
MusicTiktokAPI,
|
MusicTiktokAPI,
|
||||||
ResponseParserTiktokAPI,
|
ResponseParserTiktokAPI,
|
||||||
VideoTiktokAPI,
|
VideoTiktokAPI
|
||||||
TiktokCollectionResponse
|
} from "../../types/downloader/tiktokApiDownloader"
|
||||||
} from "../../types/downloader/tiktokApi"
|
|
||||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||||
import { ERROR_MESSAGES } from "../../constants"
|
import { ERROR_MESSAGES } from "../../constants"
|
||||||
import { TiktokPlaylistResponse } from "../../types/get/getPlaylist"
|
|
||||||
|
|
||||||
/** Constants */
|
/** Constants */
|
||||||
const TIKTOK_URL_REGEX =
|
const TIKTOK_URL_REGEX =
|
||||||
/https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/
|
/https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/
|
||||||
const USER_AGENT =
|
const USER_AGENT =
|
||||||
"com.zhiliaoapp.musically/300904 (2018111632; U; Android 10; en_US; Pixel 4; Build/QQ3A.200805.001; Cronet/58.0.2991.0)"
|
"com.zhiliaoapp.musically/300904 (2018111632; U; Android 10; en_US; Pixel 4; Build/QQ3A.200805.001; Cronet/58.0.2991.0)"
|
||||||
const COLLECTION_URL_REGEX = /collection\/[^/]+-(\d+)/
|
|
||||||
const PLAYLIST_URL_REGEX = /playlist\/[^/]+-(\d+)/
|
|
||||||
|
|
||||||
/** Types */
|
/** Types */
|
||||||
interface ProxyConfig {
|
interface ProxyConfig {
|
||||||
@ -208,7 +204,10 @@ const createVideoResponse = (
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleRedirect = async (url: string, proxy?: string): Promise<string> => {
|
export const handleRedirect = async (
|
||||||
|
url: string,
|
||||||
|
proxy?: string
|
||||||
|
): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const response = await Axios(url, {
|
const response = await Axios(url, {
|
||||||
method: "HEAD",
|
method: "HEAD",
|
||||||
@ -228,22 +227,6 @@ const handleRedirect = async (url: string, proxy?: string): Promise<string> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const extractCollectionId = (input: string): string | null => {
|
|
||||||
// If it's already just a number, return it
|
|
||||||
if (/^\d+$/.test(input)) {
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to extract from URL
|
|
||||||
const match = input.match(COLLECTION_URL_REGEX)
|
|
||||||
return match ? match[1] : null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const extractPlaylistId = (url: string): string | null => {
|
|
||||||
const match = url.match(PLAYLIST_URL_REGEX)
|
|
||||||
return match ? match[1] : null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tiktok API Downloader
|
* Tiktok API Downloader
|
||||||
* @param {string} url - Tiktok URL
|
* @param {string} url - Tiktok URL
|
||||||
@ -314,133 +297,3 @@ export const TiktokAPI = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Collection = async (
|
|
||||||
collectionIdOrUrl: string,
|
|
||||||
options?: {
|
|
||||||
page?: number
|
|
||||||
proxy?: string
|
|
||||||
count?: number
|
|
||||||
}
|
|
||||||
): Promise<TiktokCollectionResponse> => {
|
|
||||||
try {
|
|
||||||
// Only handle redirects if the input is a URL
|
|
||||||
const processedUrl = collectionIdOrUrl.startsWith("http")
|
|
||||||
? await handleRedirect(collectionIdOrUrl, options?.proxy)
|
|
||||||
: collectionIdOrUrl
|
|
||||||
|
|
||||||
const collectionId = extractCollectionId(processedUrl)
|
|
||||||
if (!collectionId) {
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message: "Invalid collection ID or URL format"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await Axios(
|
|
||||||
_tiktokGetCollection(
|
|
||||||
_getCollectionParams(collectionId, options.page, options.count)
|
|
||||||
),
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"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",
|
|
||||||
Accept: "*/*",
|
|
||||||
"Accept-Language": "en-US,en;q=0.7",
|
|
||||||
Referer: "https://www.tiktok.com/",
|
|
||||||
Origin: "https://www.tiktok.com"
|
|
||||||
},
|
|
||||||
...createProxyAgent(options?.proxy)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (response.data && response.data.status_code === 0) {
|
|
||||||
const data = response.data
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: "success",
|
|
||||||
result: {
|
|
||||||
itemList: data.itemList || [],
|
|
||||||
hasMore: data.hasMore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message: ERROR_MESSAGES.NETWORK_ERROR
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message:
|
|
||||||
error instanceof Error ? error.message : ERROR_MESSAGES.NETWORK_ERROR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Playlist = async (
|
|
||||||
url: string,
|
|
||||||
options?: {
|
|
||||||
page?: number
|
|
||||||
proxy?: string
|
|
||||||
count?: number
|
|
||||||
}
|
|
||||||
): Promise<TiktokPlaylistResponse> => {
|
|
||||||
try {
|
|
||||||
const processedUrl = url.startsWith("http")
|
|
||||||
? await handleRedirect(url, options?.proxy)
|
|
||||||
: url
|
|
||||||
|
|
||||||
const playlistId = extractPlaylistId(processedUrl)
|
|
||||||
if (!playlistId) {
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message: "Invalid playlist ID or URL format"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await Axios(
|
|
||||||
_tiktokGetPlaylist(
|
|
||||||
_getPlaylistParams(playlistId, options.page, options.count)
|
|
||||||
),
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"User-Agent":
|
|
||||||
"Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0",
|
|
||||||
Accept: "*/*",
|
|
||||||
"Accept-Language": "en-US,en;q=0.7",
|
|
||||||
Referer: "https://www.tiktok.com/",
|
|
||||||
Origin: "https://www.tiktok.com"
|
|
||||||
},
|
|
||||||
...createProxyAgent(options?.proxy)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (response.data && response.data.status_code === 0) {
|
|
||||||
const data = response.data
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: "success",
|
|
||||||
result: {
|
|
||||||
itemList: data.itemList || [],
|
|
||||||
hasMore: data.hasMore,
|
|
||||||
extra: data.extra
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message: ERROR_MESSAGES.NETWORK_ERROR
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
status: "error",
|
|
||||||
message:
|
|
||||||
error instanceof Error ? error.message : ERROR_MESSAGES.NETWORK_ERROR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,6 +6,10 @@ import { SocksProxyAgent } from "socks-proxy-agent"
|
|||||||
import { TiktokCollectionResponse } from "../../types/get/getCollection"
|
import { TiktokCollectionResponse } from "../../types/get/getCollection"
|
||||||
import { ERROR_MESSAGES } from "../../constants"
|
import { ERROR_MESSAGES } from "../../constants"
|
||||||
import retry from "async-retry"
|
import retry from "async-retry"
|
||||||
|
import { handleRedirect } from "../downloader/tiktokAPIDownloader"
|
||||||
|
|
||||||
|
/** Constants */
|
||||||
|
const COLLECTION_URL_REGEX = /collection\/[^/]+-(\d+)/
|
||||||
|
|
||||||
/** Types */
|
/** Types */
|
||||||
interface ProxyConfig {
|
interface ProxyConfig {
|
||||||
@ -44,9 +48,7 @@ export const getCollection = async (
|
|||||||
const response = await retry(
|
const response = await retry(
|
||||||
async () => {
|
async () => {
|
||||||
const res = await Axios(
|
const res = await Axios(
|
||||||
_tiktokGetCollection(
|
_tiktokGetCollection(_getCollectionParams(collectionId, page, count)),
|
||||||
_getCollectionParams(collectionId, page, count)
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@ -90,3 +92,79 @@ export const getCollection = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Collection = async (
|
||||||
|
collectionIdOrUrl: string,
|
||||||
|
options?: {
|
||||||
|
page?: number
|
||||||
|
proxy?: string
|
||||||
|
count?: number
|
||||||
|
}
|
||||||
|
): Promise<TiktokCollectionResponse> => {
|
||||||
|
try {
|
||||||
|
// Only handle redirects if the input is a URL
|
||||||
|
const processedUrl = collectionIdOrUrl.startsWith("http")
|
||||||
|
? await handleRedirect(collectionIdOrUrl, options?.proxy)
|
||||||
|
: collectionIdOrUrl
|
||||||
|
|
||||||
|
const collectionId = extractCollectionId(processedUrl)
|
||||||
|
if (!collectionId) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message: "Invalid collection ID or URL format"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await Axios(
|
||||||
|
_tiktokGetCollection(
|
||||||
|
_getCollectionParams(collectionId, options.page, options.count)
|
||||||
|
),
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"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",
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "en-US,en;q=0.7",
|
||||||
|
Referer: "https://www.tiktok.com/",
|
||||||
|
Origin: "https://www.tiktok.com"
|
||||||
|
},
|
||||||
|
...createProxyAgent(options?.proxy)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.data && response.data.status_code === 0) {
|
||||||
|
const data = response.data
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "success",
|
||||||
|
result: {
|
||||||
|
itemList: data.itemList || [],
|
||||||
|
hasMore: data.hasMore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message: ERROR_MESSAGES.NETWORK_ERROR
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message:
|
||||||
|
error instanceof Error ? error.message : ERROR_MESSAGES.NETWORK_ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extractCollectionId = (input: string): string | null => {
|
||||||
|
// If it's already just a number, return it
|
||||||
|
if (/^\d+$/.test(input)) {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to extract from URL
|
||||||
|
const match = input.match(COLLECTION_URL_REGEX)
|
||||||
|
return match ? match[1] : null
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,10 @@ import { SocksProxyAgent } from "socks-proxy-agent"
|
|||||||
import { ERROR_MESSAGES } from "../../constants"
|
import { ERROR_MESSAGES } from "../../constants"
|
||||||
import retry from "async-retry"
|
import retry from "async-retry"
|
||||||
import { TiktokPlaylistResponse } from "../../types/get/getPlaylist"
|
import { TiktokPlaylistResponse } from "../../types/get/getPlaylist"
|
||||||
|
import { handleRedirect } from "../downloader/tiktokAPIDownloader"
|
||||||
|
|
||||||
|
/** Constants */
|
||||||
|
const PLAYLIST_URL_REGEX = /playlist\/[^/]+-(\d+)/
|
||||||
|
|
||||||
/** Types */
|
/** Types */
|
||||||
interface ProxyConfig {
|
interface ProxyConfig {
|
||||||
@ -54,7 +58,7 @@ export const getPlaylist = async (
|
|||||||
"Accept-Language": "en-US,en;q=0.7",
|
"Accept-Language": "en-US,en;q=0.7",
|
||||||
Referer: "https://www.tiktok.com/",
|
Referer: "https://www.tiktok.com/",
|
||||||
Origin: "https://www.tiktok.com",
|
Origin: "https://www.tiktok.com",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
...createProxyAgent(proxy)
|
...createProxyAgent(proxy)
|
||||||
}
|
}
|
||||||
@ -78,7 +82,7 @@ export const getPlaylist = async (
|
|||||||
result: {
|
result: {
|
||||||
hasMore: response.hasMore,
|
hasMore: response.hasMore,
|
||||||
itemList: response.itemList || [],
|
itemList: response.itemList || [],
|
||||||
extra: response.extra,
|
extra: response.extra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -89,3 +93,79 @@ export const getPlaylist = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Playlist = async (
|
||||||
|
url: string,
|
||||||
|
options?: {
|
||||||
|
page?: number
|
||||||
|
proxy?: string
|
||||||
|
count?: number
|
||||||
|
}
|
||||||
|
): Promise<TiktokPlaylistResponse> => {
|
||||||
|
try {
|
||||||
|
const processedUrl = url.startsWith("http")
|
||||||
|
? await handleRedirect(url, options?.proxy)
|
||||||
|
: url
|
||||||
|
|
||||||
|
const playlistId = extractPlaylistId(processedUrl)
|
||||||
|
if (!playlistId) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message: "Invalid playlist ID or URL format"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await Axios(
|
||||||
|
_tiktokGetPlaylist(
|
||||||
|
_getPlaylistParams(playlistId, options.page, options.count)
|
||||||
|
),
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0",
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "en-US,en;q=0.7",
|
||||||
|
Referer: "https://www.tiktok.com/",
|
||||||
|
Origin: "https://www.tiktok.com"
|
||||||
|
},
|
||||||
|
...createProxyAgent(options?.proxy)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.data && response.data.status_code === 0) {
|
||||||
|
const data = response.data
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "success",
|
||||||
|
result: {
|
||||||
|
itemList: data.itemList || [],
|
||||||
|
hasMore: data.hasMore,
|
||||||
|
extra: data.extra
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message: ERROR_MESSAGES.NETWORK_ERROR
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
message:
|
||||||
|
error instanceof Error ? error.message : ERROR_MESSAGES.NETWORK_ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extractPlaylistId = (input: string): string | null => {
|
||||||
|
// If it's already just a number, return it
|
||||||
|
if (/^\d+$/.test(input)) {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to extract from URL
|
||||||
|
const match = input.match(PLAYLIST_URL_REGEX)
|
||||||
|
return match ? match[1] : null
|
||||||
|
}
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import { SocksProxyAgent } from "socks-proxy-agent"
|
|||||||
|
|
||||||
export const StalkUser = (
|
export const StalkUser = (
|
||||||
username: string,
|
username: string,
|
||||||
cookie?: string | any[],
|
|
||||||
proxy?: string
|
proxy?: string
|
||||||
): Promise<TiktokStalkUserResponse> =>
|
): Promise<TiktokStalkUserResponse> =>
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
@ -31,10 +30,6 @@ export const StalkUser = (
|
|||||||
Axios(`${_tiktokurl}/@${username}`, {
|
Axios(`${_tiktokurl}/@${username}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
cookie:
|
|
||||||
typeof cookie === "object"
|
|
||||||
? cookie.map((v: any) => `${v.name}=${v.value}`).join("; ")
|
|
||||||
: cookie,
|
|
||||||
"User-Agent":
|
"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"
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { _tiktokSearchVideoFull } from "../../constants/api"
|
|||||||
import { _liveSearchParams, _videoSearchParams } from "../../constants/params"
|
import { _liveSearchParams, _videoSearchParams } from "../../constants/params"
|
||||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||||
import { TiktokService } from "../../services/tiktokService"
|
|
||||||
import retry from "async-retry"
|
import retry from "async-retry"
|
||||||
import {
|
import {
|
||||||
TiktokVideoSearchResponse,
|
TiktokVideoSearchResponse,
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
import { Collection } from "../src/utils/downloader/tiktokApi"
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
async function testCollection() {
|
async function testCollection() {
|
||||||
try {
|
try {
|
||||||
// You can use either a collection ID or URL
|
// You can use either a collection ID or URL
|
||||||
const collectionId = "7507916135931218695"
|
const collectionId = "7507916135931218695"
|
||||||
const collectionUrl = "https://www.tiktok.com/@getrex.co.nz/collection/big%20back-7507916135931218695"
|
const collectionUrl =
|
||||||
|
"https://www.tiktok.com/@getrex.co.nz/collection/big%20back-7507916135931218695"
|
||||||
const collectionShareableLink = "https://vt.tiktok.com/ZShvmqNjQ/"
|
const collectionShareableLink = "https://vt.tiktok.com/ZShvmqNjQ/"
|
||||||
|
|
||||||
console.log("Testing Collection method...")
|
console.log("Testing Collection method...")
|
||||||
const result = await Collection(collectionId, {
|
const result = await Tiktok.Collection(collectionId, {
|
||||||
page: 1,
|
page: 1,
|
||||||
count: 5, // Optional: Number of items to fetch
|
count: 5, // Optional: Number of items to fetch
|
||||||
proxy: undefined // Optional: Add your proxy if needed
|
proxy: undefined // Optional: Add your proxy if needed
|
||||||
@ -29,7 +30,9 @@ async function testCollection() {
|
|||||||
console.log(`ID: ${item.id}`)
|
console.log(`ID: ${item.id}`)
|
||||||
console.log(`Description: ${item.desc}`)
|
console.log(`Description: ${item.desc}`)
|
||||||
console.log(`Author: ${item.author.nickname}`)
|
console.log(`Author: ${item.author.nickname}`)
|
||||||
console.log(`Created: ${new Date(item.createTime * 1000).toLocaleString()}`)
|
console.log(
|
||||||
|
`Created: ${new Date(item.createTime * 1000).toLocaleString()}`
|
||||||
|
)
|
||||||
|
|
||||||
// Log video URL
|
// Log video URL
|
||||||
if (item.video?.playAddr?.[0]) {
|
if (item.video?.playAddr?.[0]) {
|
||||||
@ -50,7 +53,7 @@ async function testCollection() {
|
|||||||
// Log hashtags if available
|
// Log hashtags if available
|
||||||
if (item.textExtra?.length > 0) {
|
if (item.textExtra?.length > 0) {
|
||||||
console.log("\nHashtags:")
|
console.log("\nHashtags:")
|
||||||
item.textExtra.forEach(tag => {
|
item.textExtra.forEach((tag) => {
|
||||||
if (tag.hashtagName) {
|
if (tag.hashtagName) {
|
||||||
console.log(`- #${tag.hashtagName}`)
|
console.log(`- #${tag.hashtagName}`)
|
||||||
}
|
}
|
||||||
|
|||||||
54
test/comments-test.ts
Normal file
54
test/comments-test.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Test for Tiktok Video Comments
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testComments() {
|
||||||
|
try {
|
||||||
|
const url = "https://www.tiktok.com/@tobz2k19/video/7451777267107187986" // Change to a valid TikTok video URL
|
||||||
|
const result = await Tiktok.GetVideoComments(url, {
|
||||||
|
commentLimit: 10,
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
console.log("\nComments fetched successfully!")
|
||||||
|
console.log("========================")
|
||||||
|
console.log("Comments Overview:")
|
||||||
|
console.log("========================")
|
||||||
|
console.log(`Total comments fetched: ${result.result.length}`)
|
||||||
|
// Log all comments
|
||||||
|
result.result.forEach((comment, index) => {
|
||||||
|
console.log(`\nComment ${index + 1}:`)
|
||||||
|
console.log("-------------------")
|
||||||
|
console.log(`ID: ${comment.cid}`)
|
||||||
|
if (comment.user) {
|
||||||
|
console.log(
|
||||||
|
`Author: ${comment.user.nickname} (@${comment.user.username})`
|
||||||
|
)
|
||||||
|
console.log(`Verified: ${comment.user.isVerified ? "Yes" : "No"}`)
|
||||||
|
}
|
||||||
|
console.log(`Text: ${comment.text}`)
|
||||||
|
if (comment.createTime) {
|
||||||
|
console.log(
|
||||||
|
`Created: ${new Date(comment.createTime * 1000).toLocaleString()}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Log comment statistics
|
||||||
|
if (typeof comment.likeCount !== "undefined") {
|
||||||
|
console.log("\nStatistics:")
|
||||||
|
console.log(`- Likes: ${comment.likeCount}`)
|
||||||
|
}
|
||||||
|
if (typeof comment.replyCommentTotal !== "undefined") {
|
||||||
|
console.log(`- Replies: ${comment.replyCommentTotal}`)
|
||||||
|
}
|
||||||
|
if (comment.isAuthorLiked) console.log("👍 Liked by author")
|
||||||
|
if (comment.isCommentTranslatable) console.log("🌐 Translatable")
|
||||||
|
console.log("========================")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testComments()
|
||||||
49
test/downloader-v1-test.ts
Normal file
49
test/downloader-v1-test.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Test for Tiktok Downloader v1
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testDownloaderV1() {
|
||||||
|
try {
|
||||||
|
const url = "https://www.tiktok.com/@tobz2k19/video/7451777267107187986" // Change to a valid TikTok video URL
|
||||||
|
console.log(`\nTesting Downloader version: v1`)
|
||||||
|
const result = await Tiktok.Downloader(url, {
|
||||||
|
version: "v1",
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
const r = result.result
|
||||||
|
console.log(`Type: ${r.type}`)
|
||||||
|
console.log(`ID: ${r.id}`)
|
||||||
|
console.log(`Description: ${r.desc}`)
|
||||||
|
if (r.author) {
|
||||||
|
console.log(`Author: ${r.author.nickname}`)
|
||||||
|
}
|
||||||
|
if (r.statistics) {
|
||||||
|
console.log("Statistics:")
|
||||||
|
console.log(`- Likes: ${r.statistics.likeCount}`)
|
||||||
|
console.log(`- Comments: ${r.statistics.commentCount}`)
|
||||||
|
console.log(`- Shares: ${r.statistics.shareCount}`)
|
||||||
|
console.log(`- Plays: ${r.statistics.playCount}`)
|
||||||
|
}
|
||||||
|
if (r.video?.playAddr?.length) {
|
||||||
|
console.log(`Video URL: ${r.video.playAddr[0]}`)
|
||||||
|
}
|
||||||
|
if (r.images?.length) {
|
||||||
|
console.log(`Images: ${r.images.join(", ")}`)
|
||||||
|
}
|
||||||
|
if (r.music) {
|
||||||
|
console.log(`Music:`)
|
||||||
|
console.log(`- Title: ${r.music.title}`)
|
||||||
|
if (r.music.playUrl?.length) {
|
||||||
|
console.log(`- Music URL: ${r.music.playUrl[0]}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("========================")
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDownloaderV1()
|
||||||
47
test/downloader-v2-test.ts
Normal file
47
test/downloader-v2-test.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Test for Tiktok Downloader v2
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testDownloaderV2() {
|
||||||
|
try {
|
||||||
|
const url = "https://www.tiktok.com/@tobz2k19/video/7451777267107187986" // Change to a valid TikTok video URL
|
||||||
|
console.log(`\nTesting Downloader version: v2`)
|
||||||
|
const result = await Tiktok.Downloader(url, {
|
||||||
|
version: "v2",
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
const r = result.result
|
||||||
|
console.log(`Type: ${r.type}`)
|
||||||
|
if (r.desc) console.log(`Description: ${r.desc}`)
|
||||||
|
if (r.author && r.author.nickname) {
|
||||||
|
console.log(`Author: ${r.author.nickname}`)
|
||||||
|
} else if (r.author && r.author.avatar) {
|
||||||
|
// fallback for v2 author structure
|
||||||
|
console.log(`Author Avatar: ${r.author.avatar}`)
|
||||||
|
}
|
||||||
|
if (r.statistics) {
|
||||||
|
console.log("Statistics:")
|
||||||
|
if (r.statistics.likeCount !== undefined)
|
||||||
|
console.log(`- Likes: ${r.statistics.likeCount}`)
|
||||||
|
if (r.statistics.commentCount !== undefined)
|
||||||
|
console.log(`- Comments: ${r.statistics.commentCount}`)
|
||||||
|
if (r.statistics.shareCount !== undefined)
|
||||||
|
console.log(`- Shares: ${r.statistics.shareCount}`)
|
||||||
|
}
|
||||||
|
if (r.video?.playAddr?.length) {
|
||||||
|
console.log(`Video URL: ${r.video.playAddr[0]}`)
|
||||||
|
}
|
||||||
|
if (r.music?.playUrl?.length) {
|
||||||
|
console.log(`Music URL: ${r.music.playUrl[0]}`)
|
||||||
|
}
|
||||||
|
if (r.images?.length) console.log(`Images: ${r.images.join(", ")}`)
|
||||||
|
console.log("========================")
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDownloaderV2()
|
||||||
35
test/downloader-v3-test.ts
Normal file
35
test/downloader-v3-test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Test for Tiktok Downloader v3
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testDownloaderV3() {
|
||||||
|
try {
|
||||||
|
const url = "https://www.tiktok.com/@tobz2k19/video/7451777267107187986" // Change to a valid TikTok video URL
|
||||||
|
console.log(`\nTesting Downloader version: v3`)
|
||||||
|
const result = await Tiktok.Downloader(url, {
|
||||||
|
version: "v3",
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
const r = result.result
|
||||||
|
console.log(`Type: ${r.type}`)
|
||||||
|
if (r.desc) console.log(`Description: ${r.desc}`)
|
||||||
|
if (r.author && r.author.nickname) {
|
||||||
|
console.log(`Author: ${r.author.nickname}`)
|
||||||
|
} else if (r.author && r.author.avatar) {
|
||||||
|
// fallback for v3 author structure
|
||||||
|
console.log(`Author Avatar: ${r.author.avatar}`)
|
||||||
|
}
|
||||||
|
if (r.videoHD) console.log(`Video HD: ${r.videoHD}`)
|
||||||
|
if (r.videoWatermark) console.log(`Video Watermark: ${r.videoWatermark}`)
|
||||||
|
if (r.images?.length) console.log(`Images: ${r.images.join(", ")}`)
|
||||||
|
if (r.music) console.log(`Music: ${r.music}`)
|
||||||
|
console.log("========================")
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDownloaderV3()
|
||||||
48
test/playlist-test.ts
Normal file
48
test/playlist-test.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Test for Tiktok Playlist
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testPlaylist() {
|
||||||
|
try {
|
||||||
|
const playlistUrl =
|
||||||
|
"https://www.tiktok.com/@tobz2k19/playlist/tset-7511644672511626004" // Ganti dengan URL playlist yang valid jika perlu
|
||||||
|
console.log(`\nTesting Playlist: ${playlistUrl}`)
|
||||||
|
const result = await Tiktok.Playlist(playlistUrl, {
|
||||||
|
proxy: undefined,
|
||||||
|
page: 1,
|
||||||
|
count: 5
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
const { itemList, hasMore, extra } = result.result
|
||||||
|
console.log(`Total Videos: ${itemList.length}`)
|
||||||
|
itemList.forEach((item, idx) => {
|
||||||
|
console.log(`\n[${idx + 1}] ID: ${item.id}`)
|
||||||
|
console.log(`Description: ${item.desc}`)
|
||||||
|
if (item.author) {
|
||||||
|
console.log(`Author: ${item.author.nickname}`)
|
||||||
|
}
|
||||||
|
if (item.stats) {
|
||||||
|
console.log("Statistics:")
|
||||||
|
console.log(`- Likes: ${item.stats.diggCount}`)
|
||||||
|
console.log(`- Comments: ${item.stats.commentCount}`)
|
||||||
|
console.log(`- Shares: ${item.stats.shareCount}`)
|
||||||
|
console.log(`- Plays: ${item.stats.playCount}`)
|
||||||
|
}
|
||||||
|
if (item.video?.playAddr?.length) {
|
||||||
|
console.log(`Video URL: ${item.video.playAddr}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log("========================")
|
||||||
|
if (hasMore) {
|
||||||
|
console.log(
|
||||||
|
"There are more videos. Use the 'page' option to fetch next page."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testPlaylist()
|
||||||
41
test/profile-test.ts
Normal file
41
test/profile-test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Test for Tiktok Stalk User Profile
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testProfile() {
|
||||||
|
try {
|
||||||
|
const username = "tobz2k19" // Change to a valid TikTok username
|
||||||
|
const result = await Tiktok.StalkUser(username, {
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
const user = result.result.user
|
||||||
|
const stats = result.result.stats
|
||||||
|
console.log("\nProfile fetched successfully!")
|
||||||
|
console.log("========================")
|
||||||
|
console.log("User Profile:")
|
||||||
|
console.log("========================")
|
||||||
|
console.log(`Username: @${user.username}`)
|
||||||
|
console.log(`Nickname: ${user.nickname}`)
|
||||||
|
console.log(`Signature: ${user.signature}`)
|
||||||
|
console.log(`Verified: ${user.verified ? "Yes" : "No"}`)
|
||||||
|
console.log(`Region: ${user.region}`)
|
||||||
|
console.log(`Private Account: ${user.privateAccount ? "Yes" : "No"}`)
|
||||||
|
console.log(`Commerce User: ${user.commerceUser ? "Yes" : "No"}`)
|
||||||
|
console.log(`Avatar: ${user.avatarLarger}`)
|
||||||
|
console.log("\nStats:")
|
||||||
|
console.log(`- Followers: ${stats.followerCount}`)
|
||||||
|
console.log(`- Following: ${stats.followingCount}`)
|
||||||
|
console.log(`- Hearts: ${stats.heartCount}`)
|
||||||
|
console.log(`- Videos: ${stats.videoCount}`)
|
||||||
|
console.log(`- Likes: ${stats.likeCount}`)
|
||||||
|
console.log(`- Friends: ${stats.friendCount}`)
|
||||||
|
console.log("========================")
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testProfile()
|
||||||
42
test/search-live-test.ts
Normal file
42
test/search-live-test.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Test for Tiktok Search Live
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testSearchLive() {
|
||||||
|
try {
|
||||||
|
const keyword = "call of duty" // Change to a valid search keyword
|
||||||
|
const cookie = "" // Optional: provide a valid TikTok cookie if needed
|
||||||
|
console.log(`\nTesting Search type: live`)
|
||||||
|
const result = await Tiktok.Search(keyword, {
|
||||||
|
type: "live",
|
||||||
|
cookie,
|
||||||
|
page: 1,
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
console.log("Success! Parsed Result:")
|
||||||
|
result.result.forEach((item, index) => {
|
||||||
|
if (item.type === "live") {
|
||||||
|
const live = item as typeof item & { liveInfo: any }
|
||||||
|
if (live.liveInfo) {
|
||||||
|
console.log(`\nResult ${index + 1}:`)
|
||||||
|
console.log("-------------------")
|
||||||
|
console.log(`ID: ${live.liveInfo.id}`)
|
||||||
|
console.log(`Title: ${live.liveInfo.title}`)
|
||||||
|
console.log(`Hashtag: ${live.liveInfo.hashtag}`)
|
||||||
|
if (live.liveInfo.owner)
|
||||||
|
console.log(`Owner: ${live.liveInfo.owner.nickname}`)
|
||||||
|
if (live.liveInfo.stats)
|
||||||
|
console.log(`Viewers: ${live.liveInfo.stats.viewerCount}`)
|
||||||
|
console.log("========================")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSearchLive()
|
||||||
46
test/search-user-test.ts
Normal file
46
test/search-user-test.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Test for Tiktok Search User
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testSearchUser() {
|
||||||
|
try {
|
||||||
|
const keyword = "call of duty" // Change to a valid search keyword
|
||||||
|
const cookie = "" // Optional: provide a valid TikTok cookie if needed
|
||||||
|
console.log(`\nTesting Search type: user`)
|
||||||
|
const result = await Tiktok.Search(keyword, {
|
||||||
|
type: "user",
|
||||||
|
cookie,
|
||||||
|
page: 1,
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
console.log("Success! Parsed Result:")
|
||||||
|
result.result.forEach((item, index) => {
|
||||||
|
if (item.type === "user") {
|
||||||
|
const user = item as typeof item & {
|
||||||
|
uid: string
|
||||||
|
username: string
|
||||||
|
nickname: string
|
||||||
|
followerCount: number
|
||||||
|
isVerified: boolean
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
console.log(`\nResult ${index + 1}:`)
|
||||||
|
console.log("-------------------")
|
||||||
|
console.log(`UID: ${user.uid}`)
|
||||||
|
console.log(`Username: ${user.username}`)
|
||||||
|
console.log(`Nickname: ${user.nickname}`)
|
||||||
|
console.log(`Followers: ${user.followerCount}`)
|
||||||
|
console.log(`Verified: ${user.isVerified ? "Yes" : "No"}`)
|
||||||
|
console.log(`Profile URL: ${user.url}`)
|
||||||
|
console.log("========================")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSearchUser()
|
||||||
53
test/search-video-test.ts
Normal file
53
test/search-video-test.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Test for Tiktok Search Video
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testSearchVideo() {
|
||||||
|
try {
|
||||||
|
const keyword = "call of duty" // Change to a valid search keyword
|
||||||
|
const cookie = "" // Optional: provide a valid TikTok cookie if needed
|
||||||
|
console.log(`\nTesting Search type: video`)
|
||||||
|
const result = await Tiktok.Search(keyword, {
|
||||||
|
type: "video",
|
||||||
|
cookie,
|
||||||
|
page: 1,
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
console.log("Success! Parsed Result:")
|
||||||
|
result.result.forEach((item, index) => {
|
||||||
|
if (item.type === "video") {
|
||||||
|
const video = item as typeof item & {
|
||||||
|
id: string
|
||||||
|
desc: string
|
||||||
|
author: any
|
||||||
|
createTime: number
|
||||||
|
stats: any
|
||||||
|
}
|
||||||
|
console.log(`\nResult ${index + 1}:`)
|
||||||
|
console.log("-------------------")
|
||||||
|
console.log(`ID: ${video.id}`)
|
||||||
|
console.log(`Description: ${video.desc}`)
|
||||||
|
if (video.author) console.log(`Author: ${video.author.nickname}`)
|
||||||
|
if (video.createTime)
|
||||||
|
console.log(
|
||||||
|
`Created: ${new Date(video.createTime * 1000).toLocaleString()}`
|
||||||
|
)
|
||||||
|
if (video.stats) {
|
||||||
|
console.log("Statistics:")
|
||||||
|
console.log(`- Likes: ${video.stats.likeCount}`)
|
||||||
|
console.log(`- Comments: ${video.stats.commentCount}`)
|
||||||
|
console.log(`- Shares: ${video.stats.shareCount}`)
|
||||||
|
console.log(`- Plays: ${video.stats.playCount}`)
|
||||||
|
}
|
||||||
|
console.log("========================")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testSearchVideo()
|
||||||
65
test/userliked-test.ts
Normal file
65
test/userliked-test.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Test for Tiktok Get User Liked Videos
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testUserLiked() {
|
||||||
|
try {
|
||||||
|
const username = "Tobz2k19" // Change to a valid TikTok username
|
||||||
|
const cookie = "" // Optional: provide a valid TikTok cookie if needed
|
||||||
|
const result = await Tiktok.GetUserLiked(username, {
|
||||||
|
cookie,
|
||||||
|
postLimit: 5,
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
console.log("\nUser Liked Videos fetched successfully!")
|
||||||
|
console.log("========================")
|
||||||
|
console.log("Liked Videos Overview:")
|
||||||
|
console.log("========================")
|
||||||
|
console.log(`Total liked videos fetched: ${result.result.length}`)
|
||||||
|
result.result.forEach((liked, index) => {
|
||||||
|
console.log(`\nLiked Video ${index + 1}:`)
|
||||||
|
console.log("-------------------")
|
||||||
|
console.log(`ID: ${liked.id}`)
|
||||||
|
console.log(`Description: ${liked.desc}`)
|
||||||
|
if (liked.author) {
|
||||||
|
console.log(
|
||||||
|
`Author: ${liked.author.nickname} (@${liked.author.username})`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (liked.createTime) {
|
||||||
|
console.log(
|
||||||
|
`Created: ${new Date(
|
||||||
|
Number(liked.createTime) * 1000
|
||||||
|
).toLocaleString()}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (liked.stats) {
|
||||||
|
console.log("Statistics:")
|
||||||
|
console.log(`- Likes: ${liked.stats.diggCount}`)
|
||||||
|
console.log(`- Favorites: ${liked.stats.collectCount}`)
|
||||||
|
console.log(`- Comments: ${liked.stats.commentCount}`)
|
||||||
|
console.log(`- Shares: ${liked.stats.shareCount}`)
|
||||||
|
console.log(`- Plays: ${liked.stats.playCount}`)
|
||||||
|
console.log(`- Reposts: ${liked.stats.repostCount}`)
|
||||||
|
}
|
||||||
|
if (liked.video?.playAddr) {
|
||||||
|
console.log(`Video URL: ${liked.video.playAddr}`)
|
||||||
|
}
|
||||||
|
if (liked.imagePost?.length) {
|
||||||
|
console.log(
|
||||||
|
`Images: \n${liked.imagePost
|
||||||
|
.map((img) => img.images)
|
||||||
|
.join("\n - ")}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
console.log("========================")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testUserLiked()
|
||||||
55
test/userposts-test.ts
Normal file
55
test/userposts-test.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Test for Tiktok Get User Posts
|
||||||
|
import Tiktok from "../src/index"
|
||||||
|
|
||||||
|
async function testUserPosts() {
|
||||||
|
try {
|
||||||
|
const username = "Tobz2k19" // Change to a valid TikTok username
|
||||||
|
const result = await Tiktok.GetUserPosts(username, {
|
||||||
|
postLimit: 5,
|
||||||
|
proxy: undefined
|
||||||
|
})
|
||||||
|
if (result.status === "success" && result.result) {
|
||||||
|
console.log("\nUser Posts fetched successfully!")
|
||||||
|
console.log("========================")
|
||||||
|
console.log("Posts Overview:")
|
||||||
|
console.log("========================")
|
||||||
|
console.log(`Total posts fetched: ${result.result.length}`)
|
||||||
|
result.result.forEach((post, index) => {
|
||||||
|
console.log(`\nPost ${index + 1}:`)
|
||||||
|
console.log("-------------------")
|
||||||
|
console.log(`ID: ${post.id}`)
|
||||||
|
console.log(`Description: ${post.desc}`)
|
||||||
|
if (post.author) {
|
||||||
|
console.log(
|
||||||
|
`Author: ${post.author.nickname} (@${post.author.username})`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (post.createTime) {
|
||||||
|
console.log(
|
||||||
|
`Created: ${new Date(post.createTime * 1000).toLocaleString()}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (post.stats) {
|
||||||
|
console.log("Statistics:")
|
||||||
|
console.log(`- Likes: ${post.stats.likeCount}`)
|
||||||
|
console.log(`- Comments: ${post.stats.commentCount}`)
|
||||||
|
console.log(`- Shares: ${post.stats.shareCount}`)
|
||||||
|
console.log(`- Plays: ${post.stats.playCount}`)
|
||||||
|
}
|
||||||
|
if (post.video?.playAddr) {
|
||||||
|
console.log(`Video URL: ${post.video.playAddr}`)
|
||||||
|
}
|
||||||
|
if (post.imagePost?.length) {
|
||||||
|
console.log(`Images: ${post.imagePost.join(", ")}`)
|
||||||
|
}
|
||||||
|
console.log("========================")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error("Error:", result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Test failed:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testUserPosts()
|
||||||
Loading…
x
Reference in New Issue
Block a user