use page instead of cursor

This commit is contained in:
Tino Laomahei 2025-05-25 15:35:42 +12:00
parent baa8fa2cc8
commit 004e74d338
9 changed files with 109 additions and 148 deletions

View File

@ -402,8 +402,8 @@ program
"<collectionIdOrUrl>", "<collectionIdOrUrl>",
"Collection ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/collection/name-id)" "Collection ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/collection/name-id)"
) )
.option("-c, --cursor <cursor>", "Cursor for pagination", "0") .option("-p, --page <number>", "Page number", "1")
.option("-p, --proxy <proxy>", "Proxy URL (http/https/socks)") .option("--proxy <proxy>", "Proxy URL (http/https/socks)")
.option( .option(
"-n, --count <number>", "-n, --count <number>",
"Number of items to fetch", "Number of items to fetch",
@ -412,19 +412,18 @@ program
) )
.action(async (collectionIdOrUrl, options) => { .action(async (collectionIdOrUrl, options) => {
try { try {
Logger.info(`Fetching collection... (count: ${options.count})`) Logger.info(`Fetching page ${options.page} with ${options.count} items per page from collection...`)
const results = await Tiktok.Collection(collectionIdOrUrl, { const results = await Tiktok.Collection(collectionIdOrUrl, {
cursor: options.cursor, page: options.page,
proxy: options.proxy, proxy: options.proxy,
count: options.count count: options.count
}) })
if (results.status === "success" && results.result) { if (results.status === "success" && results.result) {
const { itemList, hasMore, cursor } = results.result const { itemList, hasMore } = results.result
Logger.info(`Found ${itemList.length} videos in collection`) Logger.info(`Found ${itemList.length} videos in collection`)
Logger.info(`Has more videos: ${hasMore}`) Logger.info(`Has more videos: ${hasMore}`)
Logger.info(`Next cursor: ${cursor}\n`)
for (const [index, video] of itemList.entries()) { for (const [index, video] of itemList.entries()) {
Logger.info(`---- VIDEO ${index + 1} ----`) Logger.info(`---- VIDEO ${index + 1} ----`)
@ -461,9 +460,8 @@ program
if (video.video) { if (video.video) {
Logger.info(`---- VIDEO URLs ----`) Logger.info(`---- VIDEO URLs ----`)
const videoUrl = `${_tiktokurl}/@${ const videoUrl = `${_tiktokurl}/@${video.author?.uniqueId || "unknown"
video.author?.uniqueId || "unknown" }/video/${video.id}`
}/video/${video.id}`
Logger.result(`Video URL: ${videoUrl}`, chalk.blue) Logger.result(`Video URL: ${videoUrl}`, chalk.blue)
} }
@ -479,7 +477,7 @@ program
if (hasMore) { if (hasMore) {
Logger.info("\nTo fetch more videos, use:") Logger.info("\nTo fetch more videos, use:")
Logger.info(`tiktokdl collection ${collectionIdOrUrl} -c ${cursor}`) Logger.info(`tiktokdl collection ${collectionIdOrUrl} -p ${parseInt(options.page) + 1}`)
} }
} else { } else {
Logger.error(`Error: ${results.message}`) Logger.error(`Error: ${results.message}`)

View File

@ -353,7 +353,12 @@ const generateOdinId = () => {
return `${prefix}${random}` return `${prefix}${random}`
} }
export const _getCollectionParams = (collectionId: string, cursor: string = "0", count: number = 5) => { export const _getCollectionParams = (collectionId: string, page: number = 1, count: number = 5) => {
let cursor = 0
if (page > 0) {
cursor = (page - 1) * count
}
return qs.stringify({ return qs.stringify({
WebIdLastTime: Date.now(), WebIdLastTime: Date.now(),
aid: 1988, aid: 1988,
@ -368,7 +373,7 @@ export const _getCollectionParams = (collectionId: string, cursor: string = "0",
collectionId, collectionId,
cookie_enabled: true, cookie_enabled: true,
count, count,
cursor, cursor: cursor.toString(),
data_collection_enabled: true, data_collection_enabled: true,
device_id: "7002566096994190854", device_id: "7002566096994190854",
device_platform: "web_pc", device_platform: "web_pc",

View File

@ -130,7 +130,7 @@ export = {
* @param {string} collectionIdOrUrl - Collection ID or URL (e.g. 7507916135931218695 or https://www.tiktok.com/@username/collection/name-id) * @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 {Object} options - The options for collection
* @param {string} [options.proxy] - Optional proxy URL * @param {string} [options.proxy] - Optional proxy URL
* @param {string} [options.cursor] - Optional cursor for pagination * @param {string} [options.page] - Optional page for pagination
* @param {number} [options.count] - Optional number of items to fetch * @param {number} [options.count] - Optional number of items to fetch
* @returns {Promise<TiktokCollectionResponse>} * @returns {Promise<TiktokCollectionResponse>}
*/ */
@ -138,7 +138,7 @@ export = {
collectionIdOrUrl: string, collectionIdOrUrl: string,
options?: { options?: {
proxy?: string proxy?: string
cursor?: string page?: number
count?: number count?: number
} }
): Promise<TiktokCollectionResponse> => { ): Promise<TiktokCollectionResponse> => {
@ -149,7 +149,7 @@ export = {
message: "Invalid collection ID or URL format" message: "Invalid collection ID or URL format"
} }
} }
return await getCollection(collectionId, options?.proxy, options?.cursor, options?.count) return await getCollection(collectionId, options?.proxy, options?.page, options?.count)
}, },
/** /**

View File

@ -49,6 +49,5 @@ export type TiktokCollectionResponse = {
}> }>
}> }>
hasMore: boolean hasMore: boolean
cursor: string
} }
} }

View File

@ -43,7 +43,6 @@ export interface TiktokCollectionResponse {
status: "success" | "error" status: "success" | "error"
message?: string message?: string
result?: { result?: {
cursor: string
hasMore: boolean hasMore: boolean
itemList: CollectionItem[] itemList: CollectionItem[]
extra?: { extra?: {

View File

@ -1,6 +1,6 @@
import Axios from "axios" import Axios from "axios"
import asyncRetry from "async-retry" import asyncRetry from "async-retry"
import { _tiktokvFeed, _tiktokurl } from "../../constants/api" import { _tiktokvFeed, _tiktokurl, _tiktokGetCollection } from "../../constants/api"
import { _tiktokApiParams, _getCollectionParams } from "../../constants/params" import { _tiktokApiParams, _getCollectionParams } from "../../constants/params"
import { import {
AuthorTiktokAPI, AuthorTiktokAPI,
@ -282,8 +282,9 @@ export const TiktokAPI = async (
export const Collection = async ( export const Collection = async (
collectionIdOrUrl: string, collectionIdOrUrl: string,
options?: { options?: {
cursor?: string page?: number,
proxy?: string proxy?: string,
count?: number
} }
): Promise<TiktokCollectionResponse> => { ): Promise<TiktokCollectionResponse> => {
try { try {
@ -296,55 +297,31 @@ export const Collection = async (
} }
const response = await Axios( const response = await Axios(
_tiktokvFeed(_getCollectionParams(collectionId, options?.cursor)), _tiktokGetCollection(
_getCollectionParams(collectionId, options.page, options.count)
),
{ {
method: "OPTIONS", method: "GET",
headers: { "User-Agent": USER_AGENT }, 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) ...createProxyAgent(options?.proxy)
} }
) )
if (response.data && response.data.status_code === 0) { if (response.data && response.data.status_code === 0) {
const data = response.data const data = response.data
const itemList = data.aweme_list.map((item: any) => ({
id: item.aweme_id,
desc: item.desc,
createTime: item.create_time,
author: {
uid: item.author.uid,
username: item.author.unique_id,
uniqueId: item.author.unique_id,
nickname: item.author.nickname,
signature: item.author.signature,
region: item.author.region,
avatarThumb: item.author?.avatar_thumb?.url_list || [],
avatarMedium: item.author?.avatar_medium?.url_list || [],
url: `${_tiktokurl}/@${item.author.unique_id}`
},
statistics: {
likeCount: item.statistics.digg_count,
commentCount: item.statistics.comment_count,
shareCount: item.statistics.share_count,
playCount: item.statistics.play_count
},
video: {
ratio: item.video.ratio,
duration: item.video.duration,
playAddr: item.video?.play_addr?.url_list || [],
downloadAddr: item.video?.download_addr?.url_list || [],
cover: item.video?.cover?.url_list || [],
dynamicCover: item.video?.dynamic_cover?.url_list || [],
originCover: item.video?.origin_cover?.url_list || []
},
textExtra: item.text_extra || []
}))
return { return {
status: "success", status: "success",
result: { result: {
itemList, itemList: data.itemList || [],
hasMore: data.has_more, hasMore: data.hasMore
cursor: data.cursor
} }
} }
} }

View File

@ -30,14 +30,14 @@ const createProxyAgent = (proxy?: string): ProxyConfig => {
* Get TikTok Collection * Get TikTok Collection
* @param {string} collectionId - Collection ID * @param {string} collectionId - Collection ID
* @param {string} proxy - Your Proxy (optional) * @param {string} proxy - Your Proxy (optional)
* @param {string} cursor - Cursor for pagination (optional) * @param {string} page - Page for pagination (optional)
* @param {number} count - Number of items to fetch (optional) * @param {number} count - Number of items to fetch (optional)
* @returns {Promise<TiktokCollectionResponse>} * @returns {Promise<TiktokCollectionResponse>}
*/ */
export const getCollection = async ( export const getCollection = async (
collectionId: string, collectionId: string,
proxy?: string, proxy?: string,
cursor: string = "0", page: number = 1,
count: number = 5 count: number = 5
): Promise<TiktokCollectionResponse> => { ): Promise<TiktokCollectionResponse> => {
try { try {
@ -45,7 +45,7 @@ export const getCollection = async (
async () => { async () => {
const res = await Axios( const res = await Axios(
_tiktokGetCollection( _tiktokGetCollection(
_getCollectionParams(collectionId, cursor, count) _getCollectionParams(collectionId, page, count)
), ),
{ {
method: "GET", method: "GET",
@ -77,9 +77,8 @@ export const getCollection = async (
return { return {
status: "success", status: "success",
result: { result: {
cursor: response.cursor,
hasMore: response.hasMore, hasMore: response.hasMore,
itemList: response.itemList, itemList: response.itemList || [],
extra: response.extra extra: response.extra
} }
} }

69
test/collection-test.ts Normal file
View File

@ -0,0 +1,69 @@
import { Collection } from "../src/utils/downloader/tiktokApi"
async function testCollection() {
try {
// You can use either a collection ID or URL
const collectionIdOrUrl = "https://www.tiktok.com/@getrex.co.nz/collection/big%20back-7507916135931218695"
console.log("Testing Collection method...")
const result = await Collection(collectionIdOrUrl, {
page: 2,
count: 5, // Optional: Number of items to fetch
proxy: undefined // Optional: Add your proxy if needed
})
if (result.status === "success" && result.result) {
console.log("\nCollection fetched successfully!")
console.log("========================")
console.log("Collection Overview:")
console.log("========================")
console.log(`Collection ID: ${collectionIdOrUrl}`)
console.log(`Total items fetched: ${result.result.itemList.length}`)
console.log(`Has more items: ${result.result.hasMore}`)
// Log all items
result.result.itemList.forEach((item, index) => {
console.log(`\nItem ${index + 1}:`)
console.log("-------------------")
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()}`)
// Log video URL
if (item.video?.playAddr?.[0]) {
console.log(`Video URL: ${item.video.playAddr[0]}`)
} else {
console.log("No video URL available")
}
// Log item statistics
if (item.statistics) {
console.log("\nStatistics:")
console.log(`- Likes: ${item.statistics.likeCount || 0}`)
console.log(`- Comments: ${item.statistics.commentCount || 0}`)
console.log(`- Shares: ${item.statistics.shareCount || 0}`)
console.log(`- Plays: ${item.statistics.playCount || 0}`)
}
// Log hashtags if available
if (item.textExtra?.length > 0) {
console.log("\nHashtags:")
item.textExtra.forEach(tag => {
if (tag.hashtagName) {
console.log(`- #${tag.hashtagName}`)
}
})
}
console.log("========================")
})
} else {
console.error("Error:", result.message)
}
} catch (error) {
console.error("Test failed:", error)
}
}
// Run the test
testCollection()

View File

@ -1,85 +0,0 @@
import Tiktok from "../src"
async function testCollection() {
try {
// Test collection ID from your example
const collectionId = "7507916135931218695"
console.log("Fetching collection...")
const collection = await Tiktok.Collection(collectionId, {
cursor: "0" // Optional: For pagination
})
console.log(collection)
if (collection.status === "success" && collection.result) {
const { itemList, hasMore, cursor } = collection.result
console.log(`\nFound ${itemList.length} videos in collection`)
console.log(`Has more videos: ${hasMore}`)
console.log(`Next cursor: ${cursor}\n`)
// Print details of first video
if (itemList.length > 0) {
const firstVideo = itemList[0]
console.log("First video details:")
console.log("-------------------")
console.log(`Description: ${firstVideo.desc}`)
console.log(`Author: ${firstVideo.author?.nickname || 'Unknown'}`)
console.log(
`Created: ${new Date(firstVideo.createTime * 1000).toLocaleString()}`
)
// Print statistics if available
if (firstVideo.statistics) {
console.log("\nStatistics:")
console.log(`- Likes: ${firstVideo.statistics.likeCount || 0}`)
console.log(`- Comments: ${firstVideo.statistics.commentCount || 0}`)
console.log(`- Shares: ${firstVideo.statistics.shareCount || 0}`)
console.log(`- Plays: ${firstVideo.statistics.playCount || 0}`)
}
// Print video URLs if available
if (firstVideo.video) {
console.log("\nVideo URLs:")
if (firstVideo.video.playAddr?.[0]) {
console.log(`- Play URL: ${firstVideo.video.playAddr[0]}`)
}
if (firstVideo.video.downloadAddr?.[0]) {
console.log(`- Download URL: ${firstVideo.video.downloadAddr[0]}`)
}
}
// Print hashtags if available
if (firstVideo.textExtra?.length > 0) {
console.log("\nHashtags:")
firstVideo.textExtra.forEach((tag) => {
if (tag.hashtagName) {
console.log(`- #${tag.hashtagName}`)
}
})
}
}
// If there are more videos, you can fetch the next page
if (hasMore) {
console.log("\nFetching next page...")
const nextPage = await Tiktok.Collection(collectionId, {
proxy: "http://your-proxy-url", // Optional: Add your proxy if needed
cursor: cursor
})
if (nextPage.status === "success" && nextPage.result) {
console.log(`Found ${nextPage.result.itemList.length} more videos`)
}
}
} else {
console.error("Error:", collection.message)
}
} catch (error) {
console.error("Test failed:", error)
}
}
// Run the test
testCollection()