commit
88bff6b727
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
lib
|
||||
!src/lib
|
||||
test.js
|
||||
bun.lockb
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@ -371,3 +371,27 @@
|
||||
### Changed
|
||||
|
||||
- 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 Get User Posts](#tiktok-get-user-posts)
|
||||
- [Tiktok Get User Favorite Videos](#tiktok-get-user-favorite-videos)
|
||||
- [Tiktok Collection](#tiktok-collection)
|
||||
- [Tiktok Playlist](#tiktok-playlist)
|
||||
- [API Response Types](#api-response-types)
|
||||
- [Tiktok Downloader](#tiktok-downloader-1)
|
||||
- [Version 1 Response](#version-1-response)
|
||||
@ -182,6 +184,22 @@ Tiktok.Downloader(url, {
|
||||
}).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 2 Response](#version-2-response)
|
||||
- [Version 3 Response](#version-3-response)
|
||||
@ -192,15 +210,47 @@ Tiktok.Downloader(url, {
|
||||
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
||||
|
||||
Tiktok.Search("username", {
|
||||
type: "user", // "user" | "live"
|
||||
type: "user", // "user" | "live" | "video"
|
||||
page: 1,
|
||||
cookie: "YOUR_COOKIE", // needed
|
||||
proxy: "YOUR_PROXY" // optional
|
||||
}).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)
|
||||
- [Live Search Response](live-search-response)
|
||||
- [Video Search Response](#video-search-response)
|
||||
|
||||
## Tiktok Stalk User Profile
|
||||
|
||||
@ -208,12 +258,21 @@ Tiktok.Search("username", {
|
||||
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
||||
|
||||
const username = "Tobz2k19"
|
||||
Tiktok.Stalker(username, {
|
||||
cookie: "YOUR_COOKIE", // optional, if response null
|
||||
Tiktok.StalkUser(username, {
|
||||
proxy: "YOUR_PROXY" // optional
|
||||
}).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 Video Comments
|
||||
@ -228,6 +287,19 @@ Tiktok.GetVideoComments(url, {
|
||||
}).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 Get User Posts
|
||||
@ -242,6 +314,19 @@ Tiktok.GetUserPosts(username, {
|
||||
}).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 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 Collection
|
||||
@ -301,59 +399,41 @@ tiktokdl collection 7507916135931218695 -p 1 -n 5
|
||||
tiktokdl collection 7507916135931218695 -n 5 -proxy "http://your-proxy-url"
|
||||
```
|
||||
|
||||
### Response Type
|
||||
- [Tiktok Collection Response](#tiktok-collection-1)
|
||||
|
||||
```typescript
|
||||
interface TiktokCollectionResponse {
|
||||
status: "success" | "error"
|
||||
message?: string
|
||||
result?: {
|
||||
itemList: Array<{
|
||||
id: string
|
||||
desc: string
|
||||
createTime: number
|
||||
author?: {
|
||||
id: string
|
||||
uniqueId: string
|
||||
nickname: string
|
||||
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
|
||||
}
|
||||
}
|
||||
## Tiktok Playlist
|
||||
|
||||
Get videos from a TikTok playlist (supports playlist ID or URL)
|
||||
|
||||
```javascript
|
||||
const Tiktok = require("@tobyg74/tiktok-api-dl")
|
||||
|
||||
const playlistIdOrUrl = "https://www.tiktok.com/@username/playlist/name-id"
|
||||
Tiktok.Playlist(playlistIdOrUrl, {
|
||||
page: 1,
|
||||
count: 5,
|
||||
proxy: "YOUR_PROXY"
|
||||
}).then((result) => console.log(result))
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
## Tiktok Downloader
|
||||
@ -446,8 +526,12 @@ interface SSSTikResponse {
|
||||
shareCount: string
|
||||
}
|
||||
images?: string[]
|
||||
video?: string
|
||||
music?: string
|
||||
video?: {
|
||||
playAddr: string
|
||||
}
|
||||
music?: {
|
||||
playUrl: 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
|
||||
|
||||
- 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",
|
||||
"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",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
@ -27,10 +27,6 @@
|
||||
"tiktok-stalk"
|
||||
],
|
||||
"author": "Tobz",
|
||||
"contributors": [
|
||||
"aqulzz",
|
||||
"nugraizy"
|
||||
],
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"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
|
||||
} from "../services/downloadManager"
|
||||
import { _tiktokurl } from "../constants/api"
|
||||
import path from "path"
|
||||
import * as fs from "fs"
|
||||
import axios from "axios"
|
||||
|
||||
const cookieManager = new CookieManager()
|
||||
|
||||
@ -86,7 +83,7 @@ cookieCommand
|
||||
|
||||
const searchCommand = program
|
||||
.command("search")
|
||||
.description("Search TikTok users or live streams")
|
||||
.description("Search TikTok users or live streams or videos")
|
||||
|
||||
searchCommand
|
||||
.command("user")
|
||||
@ -503,7 +500,7 @@ program
|
||||
.command("playlist")
|
||||
.description("Get videos from a TikTok playlist")
|
||||
.argument(
|
||||
"<url>",
|
||||
"<PlaylistIdOrUrl>",
|
||||
"Collection URL (e.g. https://www.tiktok.com/@username/playlist/name-id)"
|
||||
)
|
||||
.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()
|
||||
|
||||
92
src/index.ts
92
src/index.ts
@ -1,7 +1,7 @@
|
||||
/** Types */
|
||||
import { TiktokAPIResponse } from "./types/downloader/tiktokApi"
|
||||
import { SSSTikResponse } from "./types/downloader/ssstik"
|
||||
import { MusicalDownResponse } from "./types/downloader/musicaldown"
|
||||
import { TiktokAPIResponse } from "./types/downloader/tiktokApiDownloader"
|
||||
import { SSSTikResponse } from "./types/downloader/ssstikDownloader"
|
||||
import { MusicalDownResponse } from "./types/downloader/musicaldownDownloader"
|
||||
import { UserSearchResult } from "./types/search/userSearch"
|
||||
import { LiveSearchResult } from "./types/search/liveSearch"
|
||||
import { VideoSearchResult } from "./types/search/videoSearch"
|
||||
@ -12,9 +12,9 @@ import { TiktokUserFavoriteVideosResponse } from "./types/get/getUserLiked"
|
||||
import { TiktokCollectionResponse } from "./types/get/getCollection"
|
||||
|
||||
/** Services */
|
||||
import { extractPlaylistId, TiktokAPI } from "./utils/downloader/tiktokApi"
|
||||
import { SSSTik } from "./utils/downloader/ssstik"
|
||||
import { MusicalDown } from "./utils/downloader/musicalDown"
|
||||
import { TiktokAPI } from "./utils/downloader/tiktokAPIDownloader"
|
||||
import { SSSTik } from "./utils/downloader/ssstikDownloader"
|
||||
import { MusicalDown } from "./utils/downloader/musicaldownDownloader"
|
||||
import { StalkUser } from "./utils/get/getProfile"
|
||||
import { SearchUser } from "./utils/search/userSearch"
|
||||
import { SearchLive } from "./utils/search/liveSearch"
|
||||
@ -23,7 +23,6 @@ import { getUserPosts } from "./utils/get/getUserPosts"
|
||||
import { getUserLiked } from "./utils/get/getUserLiked"
|
||||
import { SearchVideo } from "./utils/search/videoSearch"
|
||||
import { getCollection } from "./utils/get/getCollection"
|
||||
import { extractCollectionId } from "./utils/downloader/tiktokApi"
|
||||
|
||||
/** Constants */
|
||||
import { DOWNLOADER_VERSIONS, SEARCH_TYPES } from "./constants"
|
||||
@ -31,6 +30,8 @@ import { ERROR_MESSAGES } from "./constants"
|
||||
import { validateCookie } from "./utils/validator"
|
||||
import { TiktokPlaylistResponse } from "./types/get/getPlaylist"
|
||||
import { getPlaylist } from "./utils/get/getPlaylist"
|
||||
import { extractPlaylistId } from "./utils/get/getPlaylist"
|
||||
import { extractCollectionId } from "./utils/get/getCollection"
|
||||
|
||||
/** Types */
|
||||
type DownloaderVersion = "v1" | "v2" | "v3"
|
||||
@ -82,7 +83,7 @@ export = {
|
||||
Downloader: async <T extends DownloaderVersion>(
|
||||
url: string,
|
||||
options?: {
|
||||
version: DownloaderVersion
|
||||
version: T
|
||||
proxy?: string
|
||||
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
|
||||
* @param {string} keyword - The query you want to search
|
||||
@ -164,7 +133,7 @@ export = {
|
||||
keyword: string,
|
||||
options?: {
|
||||
type?: T
|
||||
cookie?: string
|
||||
cookie: string | any[]
|
||||
page?: number
|
||||
proxy?: string
|
||||
}
|
||||
@ -240,11 +209,10 @@ export = {
|
||||
StalkUser: async (
|
||||
username: string,
|
||||
options?: {
|
||||
cookie?: string | any[]
|
||||
proxy?: string
|
||||
}
|
||||
): 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
|
||||
* @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 {string} [options.proxy] - Optional proxy URL
|
||||
* @param {string} [options.page] - Optional page for pagination
|
||||
@ -324,14 +324,14 @@ export = {
|
||||
* @returns {Promise<TiktokPlaylistResponse>}
|
||||
*/
|
||||
Playlist: async (
|
||||
url: string,
|
||||
playlistIdOrUrl: string,
|
||||
options?: {
|
||||
proxy?: string
|
||||
page?: number
|
||||
count?: number
|
||||
}
|
||||
): Promise<TiktokPlaylistResponse> => {
|
||||
const playlistId = extractPlaylistId(url)
|
||||
const playlistId = extractPlaylistId(playlistIdOrUrl)
|
||||
if (!playlistId) {
|
||||
return {
|
||||
status: "error",
|
||||
|
||||
@ -2,19 +2,19 @@ import chalk from "chalk"
|
||||
|
||||
export class Logger {
|
||||
static success(message: string): void {
|
||||
console.log(chalk.green("✓ " + message))
|
||||
console.log(chalk.green("✓ " + message))
|
||||
}
|
||||
|
||||
static error(message: string): void {
|
||||
console.error(chalk.red("✗ " + message))
|
||||
console.error(chalk.red("✗ " + message))
|
||||
}
|
||||
|
||||
static info(message: string): void {
|
||||
console.log(chalk.blue("ℹ " + message))
|
||||
console.log(chalk.blue("ℹ " + message))
|
||||
}
|
||||
|
||||
static warning(message: string): void {
|
||||
console.log(chalk.yellow("⚠ " + message))
|
||||
console.log(chalk.yellow("⚠ " + message))
|
||||
}
|
||||
|
||||
static result(message: string, color = chalk.cyan): void {
|
||||
|
||||
@ -61,7 +61,7 @@ async function handleMediaDownload(
|
||||
case "video": {
|
||||
const videoUrl =
|
||||
version === "v1"
|
||||
? result.video.downloadAddr[0]
|
||||
? result.video.playAddr[0]
|
||||
: version === "v2"
|
||||
? result.video.playAddr[0]
|
||||
: result.videoHD
|
||||
|
||||
@ -5,6 +5,7 @@ import { userAgent, webUserAgent } from "../constants/headers"
|
||||
import qs from "qs"
|
||||
import fs from "fs"
|
||||
import { createCipheriv } from "crypto"
|
||||
import path from "path"
|
||||
|
||||
export class TiktokService {
|
||||
/**
|
||||
@ -82,6 +83,7 @@ export class TiktokService {
|
||||
const baseUrl = `${TiktokService.BASE_URL}api/search/user/full/?`
|
||||
const queryParams = _userSearchParams(username, page)
|
||||
const xbogusParams = xbogus(`${baseUrl}${queryParams}`, userAgent)
|
||||
console.log(`${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 AES_KEY = "webapp1.0+202106"
|
||||
private static readonly AES_IV = "webapp1.0+202106"
|
||||
private signaturejs = fs.readFileSync("./helper/signature.js", "utf-8")
|
||||
private webmssdk = fs.readFileSync("./helper/webmssdk.js", "utf-8")
|
||||
private signaturejs = fs.readFileSync(
|
||||
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({
|
||||
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"
|
||||
|
||||
@ -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,
|
||||
MusicTiktokAPI,
|
||||
VideoTiktokAPI
|
||||
} from "../downloader/tiktokApi"
|
||||
} from "../downloader/tiktokApiDownloader"
|
||||
import { PlaylistAuthor } from "./getPlaylist"
|
||||
|
||||
export interface CollectionItem {
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
AuthorTiktokAPI,
|
||||
MusicTiktokAPI,
|
||||
VideoTiktokAPI
|
||||
} from "../downloader/tiktokApi"
|
||||
} from "../downloader/tiktokApiDownloader"
|
||||
|
||||
export interface PlaylistAuthor
|
||||
extends Omit<AuthorTiktokAPI, "username" | "uid"> {
|
||||
|
||||
@ -3,7 +3,7 @@ import { load } from "cheerio"
|
||||
import {
|
||||
MusicalDownResponse,
|
||||
GetMusicalDownReuqest
|
||||
} from "../../types/downloader/musicaldown"
|
||||
} from "../../types/downloader/musicaldownDownloader"
|
||||
import { _musicaldownapi, _musicaldownurl } from "../../constants/api"
|
||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||
@ -7,7 +7,7 @@ import {
|
||||
StatisticsSSSTik,
|
||||
SSSTikFetchTT,
|
||||
SSSTikResponse
|
||||
} from "../../types/downloader/ssstik"
|
||||
} from "../../types/downloader/ssstikDownloader"
|
||||
import { _ssstikapi, _ssstikurl } from "../../constants/api"
|
||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||
@ -17,21 +17,17 @@ import {
|
||||
StatisticsTiktokAPI,
|
||||
MusicTiktokAPI,
|
||||
ResponseParserTiktokAPI,
|
||||
VideoTiktokAPI,
|
||||
TiktokCollectionResponse
|
||||
} from "../../types/downloader/tiktokApi"
|
||||
VideoTiktokAPI
|
||||
} from "../../types/downloader/tiktokApiDownloader"
|
||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||
import { ERROR_MESSAGES } from "../../constants"
|
||||
import { TiktokPlaylistResponse } from "../../types/get/getPlaylist"
|
||||
|
||||
/** Constants */
|
||||
const TIKTOK_URL_REGEX =
|
||||
/https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/
|
||||
const USER_AGENT =
|
||||
"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 */
|
||||
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 {
|
||||
const response = await Axios(url, {
|
||||
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
|
||||
* @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 { ERROR_MESSAGES } from "../../constants"
|
||||
import retry from "async-retry"
|
||||
import { handleRedirect } from "../downloader/tiktokAPIDownloader"
|
||||
|
||||
/** Constants */
|
||||
const COLLECTION_URL_REGEX = /collection\/[^/]+-(\d+)/
|
||||
|
||||
/** Types */
|
||||
interface ProxyConfig {
|
||||
@ -44,9 +48,7 @@ export const getCollection = async (
|
||||
const response = await retry(
|
||||
async () => {
|
||||
const res = await Axios(
|
||||
_tiktokGetCollection(
|
||||
_getCollectionParams(collectionId, page, count)
|
||||
),
|
||||
_tiktokGetCollection(_getCollectionParams(collectionId, page, count)),
|
||||
{
|
||||
method: "GET",
|
||||
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 retry from "async-retry"
|
||||
import { TiktokPlaylistResponse } from "../../types/get/getPlaylist"
|
||||
import { handleRedirect } from "../downloader/tiktokAPIDownloader"
|
||||
|
||||
/** Constants */
|
||||
const PLAYLIST_URL_REGEX = /playlist\/[^/]+-(\d+)/
|
||||
|
||||
/** Types */
|
||||
interface ProxyConfig {
|
||||
@ -54,7 +58,7 @@ export const getPlaylist = async (
|
||||
"Accept-Language": "en-US,en;q=0.7",
|
||||
Referer: "https://www.tiktok.com/",
|
||||
Origin: "https://www.tiktok.com",
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
...createProxyAgent(proxy)
|
||||
}
|
||||
@ -78,7 +82,7 @@ export const getPlaylist = async (
|
||||
result: {
|
||||
hasMore: response.hasMore,
|
||||
itemList: response.itemList || [],
|
||||
extra: response.extra,
|
||||
extra: response.extra
|
||||
}
|
||||
}
|
||||
} 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 = (
|
||||
username: string,
|
||||
cookie?: string | any[],
|
||||
proxy?: string
|
||||
): Promise<TiktokStalkUserResponse> =>
|
||||
new Promise(async (resolve) => {
|
||||
@ -31,10 +30,6 @@ export const StalkUser = (
|
||||
Axios(`${_tiktokurl}/@${username}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
cookie:
|
||||
typeof cookie === "object"
|
||||
? cookie.map((v: any) => `${v.name}=${v.value}`).join("; ")
|
||||
: cookie,
|
||||
"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"
|
||||
},
|
||||
|
||||
@ -3,7 +3,6 @@ import { _tiktokSearchVideoFull } from "../../constants/api"
|
||||
import { _liveSearchParams, _videoSearchParams } from "../../constants/params"
|
||||
import { SocksProxyAgent } from "socks-proxy-agent"
|
||||
import { HttpsProxyAgent } from "https-proxy-agent"
|
||||
import { TiktokService } from "../../services/tiktokService"
|
||||
import retry from "async-retry"
|
||||
import {
|
||||
TiktokVideoSearchResponse,
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { Collection } from "../src/utils/downloader/tiktokApi"
|
||||
import Tiktok from "../src/index"
|
||||
|
||||
async function testCollection() {
|
||||
try {
|
||||
// You can use either a collection ID or URL
|
||||
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/"
|
||||
|
||||
console.log("Testing Collection method...")
|
||||
const result = await Collection(collectionId, {
|
||||
const result = await Tiktok.Collection(collectionId, {
|
||||
page: 1,
|
||||
count: 5, // Optional: Number of items to fetch
|
||||
proxy: undefined // Optional: Add your proxy if needed
|
||||
@ -29,7 +30,9 @@ async function testCollection() {
|
||||
console.log(`ID: ${item.id}`)
|
||||
console.log(`Description: ${item.desc}`)
|
||||
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
|
||||
if (item.video?.playAddr?.[0]) {
|
||||
@ -50,7 +53,7 @@ async function testCollection() {
|
||||
// Log hashtags if available
|
||||
if (item.textExtra?.length > 0) {
|
||||
console.log("\nHashtags:")
|
||||
item.textExtra.forEach(tag => {
|
||||
item.textExtra.forEach((tag) => {
|
||||
if (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