feat: add tiktok search live
This commit is contained in:
parent
a79fa9017b
commit
f7d9b1be48
@ -2,7 +2,8 @@
|
|||||||
export const _tiktokurl: string = "https://www.tiktok.com"
|
export const _tiktokurl: string = "https://www.tiktok.com"
|
||||||
export const _tiktokSearchUserFull = (params: any): string => `${_tiktokurl}/api/search/user/full/?${params}`
|
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 _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 */
|
/** Tiktokv */
|
||||||
export const _tiktokvApi: string = `https://api.tiktokv.com`
|
export const _tiktokvApi: string = `https://api.tiktokv.com`
|
||||||
|
|||||||
@ -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
|
let cursor = 0
|
||||||
for (let i = 1; i < page; i++) {
|
for (let i = 1; i < page; i++) {
|
||||||
cursor += 10
|
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) => {
|
export const _tiktokApiParams = (args: any) => {
|
||||||
return {
|
return new URLSearchParams({
|
||||||
...args,
|
...args,
|
||||||
version_name: "1.1.9",
|
version_name: "1.1.9",
|
||||||
version_code: "2018111632",
|
version_code: "2018111632",
|
||||||
@ -108,7 +151,7 @@ export const _tiktokApiParams = (args: any) => {
|
|||||||
ssmix: "a",
|
ssmix: "a",
|
||||||
as: "a1qwert123",
|
as: "a1qwert123",
|
||||||
cp: "cbfhckdckkde1"
|
cp: "cbfhckdckkde1"
|
||||||
}
|
}).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const _xttParams = (secUid: string, cursor: number, count: number) => {
|
export const _xttParams = (secUid: string, cursor: number, count: number) => {
|
||||||
|
|||||||
14
src/index.ts
14
src/index.ts
@ -13,9 +13,11 @@ import { SSSTikResponse } from "./types/downloader/ssstik"
|
|||||||
import { TiktokAPIResponse } from "./types/downloader/tiktokApi"
|
import { TiktokAPIResponse } from "./types/downloader/tiktokApi"
|
||||||
import { TiktokUserSearchResponse } from "./types/search/userSearch"
|
import { TiktokUserSearchResponse } from "./types/search/userSearch"
|
||||||
import { StalkResult } from "./types/search/stalker"
|
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 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 = {
|
export = {
|
||||||
/**
|
/**
|
||||||
@ -55,16 +57,16 @@ export = {
|
|||||||
* @param {number} options.page - The page of search (optional)
|
* @param {number} options.page - The page of search (optional)
|
||||||
* @returns {Promise<TiktokSearchResponse>}
|
* @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) {
|
switch (options?.type) {
|
||||||
case "user": {
|
case "user": {
|
||||||
const response = await SearchUser(query, options?.cookie, options?.page)
|
const response = await SearchUser(query, options?.cookie, options?.page)
|
||||||
return response as TiktokSearchResponse<T>
|
return response as TiktokSearchResponse<T>
|
||||||
}
|
}
|
||||||
// case "video": {
|
case "live": {
|
||||||
// const response = await SearchVideo(query)
|
const response = await SearchLive(query, options?.cookie, options?.page)
|
||||||
// return response as TiktokSearchResponse<T>
|
return response as TiktokSearchResponse<T>
|
||||||
// }
|
}
|
||||||
default: {
|
default: {
|
||||||
const response = await SearchUser(query, options?.cookie, options?.page)
|
const response = await SearchUser(query, options?.cookie, options?.page)
|
||||||
return response as TiktokSearchResponse<T>
|
return response as TiktokSearchResponse<T>
|
||||||
|
|||||||
52
src/types/search/liveSearch.ts
Normal file
52
src/types/search/liveSearch.ts
Normal 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
|
||||||
|
}
|
||||||
@ -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 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) =>
|
const getRequest = (url: string) =>
|
||||||
new Promise<getRequest>((resolve, reject) => {
|
new Promise<getRequest>((resolve) => {
|
||||||
if (!TiktokURLregex.test(url)) {
|
if (!TiktokURLregex.test(url)) {
|
||||||
return resolve({
|
return resolve({
|
||||||
status: "error",
|
status: "error",
|
||||||
@ -38,7 +38,7 @@ const getRequest = (url: string) =>
|
|||||||
})
|
})
|
||||||
|
|
||||||
const getMusic = (cookie: string) =>
|
const getMusic = (cookie: string) =>
|
||||||
new Promise<getMusic>((resolve, reject) => {
|
new Promise<getMusic>((resolve) => {
|
||||||
Axios.get(_musicaldownmusicapi, {
|
Axios.get(_musicaldownmusicapi, {
|
||||||
headers: {
|
headers: {
|
||||||
cookie: cookie,
|
cookie: cookie,
|
||||||
@ -61,7 +61,7 @@ const getMusic = (cookie: string) =>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const MusicalDown = (url: string) =>
|
export const MusicalDown = (url: string) =>
|
||||||
new Promise<MusicalDownResponse>(async (resolve, reject) => {
|
new Promise<MusicalDownResponse>(async (resolve) => {
|
||||||
const request: getRequest = await getRequest(url)
|
const request: getRequest = await getRequest(url)
|
||||||
if (request.status !== "success") return resolve({ status: "error", message: request.message })
|
if (request.status !== "success") return resolve({ status: "error", message: request.message })
|
||||||
Axios(_musicaldownapi, {
|
Axios(_musicaldownapi, {
|
||||||
|
|||||||
@ -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 TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/
|
||||||
|
|
||||||
const fetchTT = () =>
|
const fetchTT = () =>
|
||||||
new Promise<SSSTikFetchTT>(async (resolve, reject) => {
|
new Promise<SSSTikFetchTT>(async (resolve) => {
|
||||||
Axios.get(_ssstikurl, {
|
Axios.get(_ssstikurl, {
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0"
|
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0"
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const TiktokAPI = (url: string) =>
|
export const TiktokAPI = (url: string) =>
|
||||||
new Promise<TiktokAPIResponse>((resolve, reject) => {
|
new Promise<TiktokAPIResponse>((resolve) => {
|
||||||
if (!TiktokURLregex.test(url)) {
|
if (!TiktokURLregex.test(url)) {
|
||||||
return resolve({
|
return resolve({
|
||||||
status: "error",
|
status: "error",
|
||||||
@ -99,11 +99,9 @@ const fetchTiktokData = async (ID: string): Promise<responseParser> | null => {
|
|||||||
async () => {
|
async () => {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
_tiktokvFeed(
|
_tiktokvFeed(
|
||||||
new URLSearchParams(
|
_tiktokApiParams({
|
||||||
_tiktokApiParams({
|
aweme_id: ID
|
||||||
aweme_id: ID
|
})
|
||||||
})
|
|
||||||
).toString()
|
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
method: "OPTIONS",
|
method: "OPTIONS",
|
||||||
|
|||||||
71
src/utils/search/liveSearch.ts
Normal file
71
src/utils/search/liveSearch.ts
Normal 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 })
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -14,7 +14,7 @@ import { createCipheriv } from "crypto"
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const StalkUser = (username: string, cookie?: any, postLimit?: number): Promise<StalkResult> =>
|
export const StalkUser = (username: string, cookie?: any, postLimit?: number): Promise<StalkResult> =>
|
||||||
new Promise(async (resolve, reject) => {
|
new Promise(async (resolve) => {
|
||||||
username = username.replace("@", "")
|
username = username.replace("@", "")
|
||||||
Axios.get(`${_tiktokurl}/@${username}`, {
|
Axios.get(`${_tiktokurl}/@${username}`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { _userSearchParams } from "../../constants/params"
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const SearchUser = (username: string, cookie?: any, page?: number): Promise<TiktokUserSearchResponse> =>
|
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)), {
|
Axios(_tiktokSearchUserFull(_userSearchParams(username, page)), {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@ -21,9 +21,13 @@ export const SearchUser = (username: string, cookie?: any, page?: number): Promi
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.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..." })
|
// Cookie Invalid
|
||||||
const result = []
|
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++) {
|
for (let i = 0; i < data.user_list.length; i++) {
|
||||||
const user = data.user_list[i]
|
const user = data.user_list[i]
|
||||||
result.push({
|
result.push({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user