feat: add tiktok search live

This commit is contained in:
Tobi Saputra 2024-07-08 08:31:48 +07:00
parent a79fa9017b
commit f7d9b1be48
10 changed files with 195 additions and 24 deletions

View File

@ -2,7 +2,8 @@
export const _tiktokurl: string = "https://www.tiktok.com"
export const _tiktokSearchUserFull = (params: any): string => `${_tiktokurl}/api/search/user/full/?${params}`
export const _tiktokSearchVideoFull = (params: any): string => `${_tiktokurl}/api/search/item/full/?${params}`
export const _tiktokGetPosts = (params: any) => `${_tiktokurl}/api/post/item_list/?${params}`
export const _tiktokSearchLiveFull = (params: any): string => `${_tiktokurl}/api/search/live/full/?${params}`
export const _tiktokGetPosts = (params: any): string => `${_tiktokurl}/api/post/item_list/?${params}`
/** Tiktokv */
export const _tiktokvApi: string = `https://api.tiktokv.com`

View File

@ -35,7 +35,7 @@ export const _userPostsParams = () => {
)
}
export const _userSearchParams = (keyword: any, page: number = 1) => {
export const _userSearchParams = (keyword: string, page: number = 1) => {
let cursor = 0
for (let i = 1; i < page; i++) {
cursor += 10
@ -75,8 +75,51 @@ export const _userSearchParams = (keyword: any, page: number = 1) => {
})
}
export const _liveSearchParams = (keyword: string, page: number = 1) => {
let cursor = 0
for (let i = 1; i < page; i++) {
cursor += 12
}
let offset = `${cursor}`
return new URLSearchParams({
WebIdLastTime: "1720342268",
aid: "1988",
app_language: "en",
app_name: "tiktok_web",
browser_language: "en-US",
browser_name: "Mozilla",
browser_online: "true",
browser_platform: "Linux x86_64",
browser_version: "5.0 (X11)",
channel: "tiktok_web",
cookie_enabled: "true",
count: "20",
device_id: "7388813454814086664",
device_platform: "web_pc",
device_type: "web_h264",
focus_state: "true",
from_page: "search",
history_len: "10",
is_fullscreen: "false",
is_page_visible: "true",
keyword,
offset,
os: "linux",
priority_region: "",
referer: "",
region: "ID",
screen_height: "768",
screen_width: "1366",
tz_name: "Asia/Jakarta",
web_search_code: "{ tiktok: { client_params_x: { search_engine: { ies_mt_user_live_video_card_use_libra: 1, mt_search_general_user_live_card: 1 } }, search_server: {} } }",
webcast_language: "en"
})
}
export const _tiktokApiParams = (args: any) => {
return {
return new URLSearchParams({
...args,
version_name: "1.1.9",
version_code: "2018111632",
@ -108,7 +151,7 @@ export const _tiktokApiParams = (args: any) => {
ssmix: "a",
as: "a1qwert123",
cp: "cbfhckdckkde1"
}
}).toString()
}
export const _xttParams = (secUid: string, cursor: number, count: number) => {

View File

@ -13,9 +13,11 @@ import { SSSTikResponse } from "./types/downloader/ssstik"
import { TiktokAPIResponse } from "./types/downloader/tiktokApi"
import { TiktokUserSearchResponse } from "./types/search/userSearch"
import { StalkResult } from "./types/search/stalker"
import { SearchLive } from "./utils/search/liveSearch"
import { TiktokLiveSearchResponse } from "./types/search/liveSearch"
type TiktokDownloaderResponse<T extends "v1" | "v2" | "v3"> = T extends "v1" ? TiktokAPIResponse : T extends "v2" ? SSSTikResponse : T extends "v3" ? MusicalDownResponse : TiktokAPIResponse
type TiktokSearchResponse<T extends "user" | "video"> = T extends "user" ? TiktokUserSearchResponse : T extends "video" ? any : TiktokUserSearchResponse
type TiktokSearchResponse<T extends "user" | "live"> = T extends "user" ? TiktokUserSearchResponse : T extends "live" ? any : TiktokLiveSearchResponse
export = {
/**
@ -55,16 +57,16 @@ export = {
* @param {number} options.page - The page of search (optional)
* @returns {Promise<TiktokSearchResponse>}
*/
Search: async <T extends "user" | "video">(query: string, options: { type: T; cookie?: string; page?: number }): Promise<TiktokSearchResponse<T>> => {
Search: async <T extends "user" | "live">(query: string, options: { type: T; cookie?: string; page?: number }): Promise<TiktokSearchResponse<T>> => {
switch (options?.type) {
case "user": {
const response = await SearchUser(query, options?.cookie, options?.page)
return response as TiktokSearchResponse<T>
}
// case "video": {
// const response = await SearchVideo(query)
// return response as TiktokSearchResponse<T>
// }
case "live": {
const response = await SearchLive(query, options?.cookie, options?.page)
return response as TiktokSearchResponse<T>
}
default: {
const response = await SearchUser(query, options?.cookie, options?.page)
return response as TiktokSearchResponse<T>

View File

@ -0,0 +1,52 @@
export type TiktokLiveSearchResponse = {
status: "success" | "error"
message?: string
result?: Result[]
}
export type Result = {
roomInfo: RoomInfo
liveInfo: LiveInfo
}
export type RoomInfo = {
hasCommerceGoods: boolean
isBattle: boolean
}
export type LiveInfo = {
id: string
title: string
cover: string[]
squareCover: string[]
rectangleCover: string[]
liveTypeThirdParty: boolean
hashtag: string
startTime: number
stats: Stats
owner: Owner
}
export type Stats = {
totalUser: number
viewerCount: number
likeCount: number
}
export type Owner = {
id: string
nickname: string
username: string
signature: string
avatarThumb: string[]
avatarMedium: string[]
avatarLarge: string[]
modifyTime: number
stats: OwnerStats
isVerified: boolean
}
export type OwnerStats = {
followingCount: number
followerCount: number
}

View File

@ -11,7 +11,7 @@ import { _musicaldownapi, _musicaldownmusicapi, _musicaldownurl } from "../../co
const TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/
const getRequest = (url: string) =>
new Promise<getRequest>((resolve, reject) => {
new Promise<getRequest>((resolve) => {
if (!TiktokURLregex.test(url)) {
return resolve({
status: "error",
@ -38,7 +38,7 @@ const getRequest = (url: string) =>
})
const getMusic = (cookie: string) =>
new Promise<getMusic>((resolve, reject) => {
new Promise<getMusic>((resolve) => {
Axios.get(_musicaldownmusicapi, {
headers: {
cookie: cookie,
@ -61,7 +61,7 @@ const getMusic = (cookie: string) =>
*/
export const MusicalDown = (url: string) =>
new Promise<MusicalDownResponse>(async (resolve, reject) => {
new Promise<MusicalDownResponse>(async (resolve) => {
const request: getRequest = await getRequest(url)
if (request.status !== "success") return resolve({ status: "error", message: request.message })
Axios(_musicaldownapi, {

View File

@ -12,7 +12,7 @@ import { _ssstikapi, _ssstikurl } from "../../constants/api"
const TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/
const fetchTT = () =>
new Promise<SSSTikFetchTT>(async (resolve, reject) => {
new Promise<SSSTikFetchTT>(async (resolve) => {
Axios.get(_ssstikurl, {
headers: {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0"

View File

@ -13,7 +13,7 @@ const TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(
*/
export const TiktokAPI = (url: string) =>
new Promise<TiktokAPIResponse>((resolve, reject) => {
new Promise<TiktokAPIResponse>((resolve) => {
if (!TiktokURLregex.test(url)) {
return resolve({
status: "error",
@ -99,11 +99,9 @@ const fetchTiktokData = async (ID: string): Promise<responseParser> | null => {
async () => {
const res = await fetch(
_tiktokvFeed(
new URLSearchParams(
_tiktokApiParams({
aweme_id: ID
})
).toString()
_tiktokApiParams({
aweme_id: ID
})
),
{
method: "OPTIONS",

View File

@ -0,0 +1,71 @@
import Axios from "axios"
import { _tiktokSearchLiveFull } from "../../constants/api"
import { _liveSearchParams } from "../../constants/params"
import { LiveInfo, Owner, OwnerStats } from "../../types/search/liveSearch"
export const SearchLive = async (keyword: string, cookie?: any, page: number = 1) =>
new Promise(async (resolve) => {
Axios(_tiktokSearchLiveFull(_liveSearchParams(keyword, page)), {
method: "GET",
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
cookie: typeof cookie === "object" ? cookie.map((v: any) => `${v.name}=${v.value}`).join("; ") : cookie
}
})
.then(({ data }) => {
// Cookie Invalid
if (data.status_code === 2483) return resolve({ status: "error", message: "Invalid cookie!" })
// Another Error
if (data.status_code !== 0) return resolve({ status: "error", message: data.status_msg || "An error occurred! Please report this issue to the developer." })
if (!data.data) return resolve({ status: "error", message: "Live not found!" })
const result = []
data.data.forEach((v: any) => {
const content = JSON.parse(v.live_info.raw_data)
// Live Info
const liveInfo: LiveInfo = {
id: content.id,
title: content.title,
cover: content.cover?.url_list || [],
squareCover: content.square_cover_img?.url_list || [],
rectangleCover: content.rectangle_cover_img?.url_list || [],
liveTypeThirdParty: content.live_type_third_party,
hashtag: content.hashtag?.title || "",
startTime: content.start_time,
stats: {
totalUser: content.stats.total_user,
viewerCount: content.user_count,
likeCount: content.like_count
},
owner: {
id: content.owner.id,
nickname: content.owner.nickname,
username: content.owner.display_id,
signature: content.owner.bio_description,
avatarThumb: content.owner.avatar_thumb?.url_list || [],
avatarMedium: content.owner.avatar_medium?.url_list || [],
avatarLarge: content.owner.avatar_large?.url_list || [],
modifyTime: content.owner.modify_time,
stats: {
followingCount: content.owner.follow_info.following_count,
followerCount: content.owner.follow_info.follower_count
} as OwnerStats,
isVerified: content.owner?.authentication_info?.custom_verify === "verified account" || false
} as Owner
}
// Room Info
const roomInfo = {
hasCommerceGoods: v.live_info.room_info.has_commerce_goods,
isBattle: v.live_info.room_info.is_battle
}
result.push({ roomInfo, liveInfo })
})
resolve({ status: "success", result })
})
.catch((e) => {
resolve({ status: "error", message: e.message })
})
})

View File

@ -14,7 +14,7 @@ import { createCipheriv } from "crypto"
*/
export const StalkUser = (username: string, cookie?: any, postLimit?: number): Promise<StalkResult> =>
new Promise(async (resolve, reject) => {
new Promise(async (resolve) => {
username = username.replace("@", "")
Axios.get(`${_tiktokurl}/@${username}`, {
headers: {

View File

@ -12,7 +12,7 @@ import { _userSearchParams } from "../../constants/params"
*/
export const SearchUser = (username: string, cookie?: any, page?: number): Promise<TiktokUserSearchResponse> =>
new Promise(async (resolve, reject) => {
new Promise(async (resolve) => {
Axios(_tiktokSearchUserFull(_userSearchParams(username, page)), {
method: "GET",
headers: {
@ -21,9 +21,13 @@ export const SearchUser = (username: string, cookie?: any, page?: number): Promi
}
})
.then(({ data }) => {
if (data.status_code !== 0) return resolve({ status: "error", message: "Failed to find user. Make sure the keywords you are looking for are correct..." })
const result = []
// Cookie Invalid
if (data.status_code === 2483) return resolve({ status: "error", message: "Invalid cookie!" })
// Another Error
if (data.status_code !== 0) return resolve({ status: "error", message: data.status_msg || "An error occurred! Please report this issue to the developer." })
if (!data.user_list) return resolve({ status: "error", message: "User not found!" })
const result = []
for (let i = 0; i < data.user_list.length; i++) {
const user = data.user_list[i]
result.push({