fix: response
This commit is contained in:
parent
7b988d7f12
commit
8fd77ea2b2
@ -1,15 +1,23 @@
|
|||||||
import Axios from "axios"
|
import Axios from "axios"
|
||||||
import { load } from "cheerio"
|
import { load } from "cheerio"
|
||||||
import { MusicalDownResponse, getMusic, getRequest } from "../../types/musicaldown"
|
import { MusicalDownResponse, getMusic, getRequest } from "../../types/downloader/musicaldown"
|
||||||
import { _musicaldownapi, _musicaldownmusicapi, _musicaldownurl } from "../../api"
|
import { _musicaldownapi, _musicaldownmusicapi, _musicaldownurl } from "../../constants/api"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using API from Website:
|
* Using API from Website:
|
||||||
* BASE URL : https://ssstik.io
|
* BASE URL : https://ssstik.io
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const TiktokURLregex = /(?:http[s]?:\/\/)?(?:www\.|m\.)?(?:tiktok\.com\/(?:@[\w.-]+\/video\/|@[\w.-]+\/video\/))?(\d+)/
|
||||||
|
|
||||||
const getRequest = (url: string) =>
|
const getRequest = (url: string) =>
|
||||||
new Promise<getRequest>((resolve, reject) => {
|
new Promise<getRequest>((resolve, reject) => {
|
||||||
|
if (!TiktokURLregex.test(url)) {
|
||||||
|
return resolve({
|
||||||
|
status: "error",
|
||||||
|
message: "Invalid Tiktok URL. Make sure your url is correct!"
|
||||||
|
})
|
||||||
|
}
|
||||||
Axios.get(_musicaldownurl, {
|
Axios.get(_musicaldownurl, {
|
||||||
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"
|
||||||
@ -46,6 +54,12 @@ const getMusic = (cookie: string) =>
|
|||||||
.catch((e) => resolve({ status: "error" }))
|
.catch((e) => resolve({ status: "error" }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiktok MusicalDown Downloader
|
||||||
|
* @param {string} url - Tiktok URL
|
||||||
|
* @returns {Promise<MusicalDownResponse>}
|
||||||
|
*/
|
||||||
|
|
||||||
export const MusicalDown = (url: string) =>
|
export const MusicalDown = (url: string) =>
|
||||||
new Promise<MusicalDownResponse>(async (resolve, reject) => {
|
new Promise<MusicalDownResponse>(async (resolve, reject) => {
|
||||||
const request: getRequest = await getRequest(url)
|
const request: getRequest = await getRequest(url)
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import Axios from "axios"
|
import Axios from "axios"
|
||||||
import { load } from "cheerio"
|
import { load } from "cheerio"
|
||||||
import { Author, Statistics, SSSTikFetchTT, SSSTikResponse } from "../../types/ssstik"
|
import { Author, Statistics, SSSTikFetchTT, SSSTikResponse } from "../../types/downloader/ssstik"
|
||||||
import { _ssstikapi, _ssstikurl } from "../../api"
|
import { _ssstikapi, _ssstikurl } from "../../constants/api"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using API from Website:
|
* Using API from Website:
|
||||||
* BASE URL : https://ssstik.io
|
* BASE URL : https://ssstik.io
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const TiktokURLregex = /(?:http[s]?:\/\/)?(?:www\.|m\.)?(?:tiktok\.com\/(?:@[\w.-]+\/video\/|@[\w.-]+\/video\/))?(\d+)/
|
||||||
|
|
||||||
const fetchTT = () =>
|
const fetchTT = () =>
|
||||||
new Promise<SSSTikFetchTT>(async (resolve, reject) => {
|
new Promise<SSSTikFetchTT>(async (resolve, reject) => {
|
||||||
Axios.get(_ssstikurl, {
|
Axios.get(_ssstikurl, {
|
||||||
@ -28,8 +30,20 @@ const fetchTT = () =>
|
|||||||
.catch((e) => resolve({ status: "error", message: e.message }))
|
.catch((e) => resolve({ status: "error", message: e.message }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiktok SSSTik Downloader
|
||||||
|
* @param {string} url - Tiktok URL
|
||||||
|
* @returns {Promise<SSSTikResponse>}
|
||||||
|
*/
|
||||||
|
|
||||||
export const SSSTik = (url: string) =>
|
export const SSSTik = (url: string) =>
|
||||||
new Promise<SSSTikResponse>(async (resolve, reject) => {
|
new Promise<SSSTikResponse>(async (resolve, reject) => {
|
||||||
|
if (!TiktokURLregex.test(url)) {
|
||||||
|
return resolve({
|
||||||
|
status: "error",
|
||||||
|
message: "Invalid Tiktok URL. Make sure your url is correct!"
|
||||||
|
})
|
||||||
|
}
|
||||||
const tt: SSSTikFetchTT = await fetchTT()
|
const tt: SSSTikFetchTT = await fetchTT()
|
||||||
if (tt.status !== "success") return resolve({ status: "error", message: tt.message })
|
if (tt.status !== "success") return resolve({ status: "error", message: tt.message })
|
||||||
Axios(_ssstikapi, {
|
Axios(_ssstikapi, {
|
||||||
|
|||||||
@ -1,13 +1,26 @@
|
|||||||
import axios from "axios"
|
import Axios from "axios"
|
||||||
import asyncRetry from "async-retry"
|
import { _tiktokapi, _tiktokurl } from "../../constants/api"
|
||||||
import { _tiktokapi, _tiktokurl } from "../../api"
|
import { _tiktokApiParams } from "../../constants/params"
|
||||||
import { Author, TiktokAPIResponse, Statistics, Music, responseParser } from "../../types/tiktokApi"
|
import { Author, TiktokAPIResponse, Statistics, Music, responseParser, Video } from "../../types/downloader/tiktokApi"
|
||||||
|
|
||||||
|
const TiktokURLregex = /(?:http[s]?:\/\/)?(?:www\.|m\.)?(?:tiktok\.com\/(?:@[\w.-]+\/video\/|@[\w.-]+\/video\/))?(\d+)/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiktok API Downloader
|
||||||
|
* @param {string} url - Tiktok URL
|
||||||
|
* @returns {Promise<TiktokAPIResponse>}
|
||||||
|
*/
|
||||||
|
|
||||||
export const TiktokAPI = (url: string) =>
|
export const TiktokAPI = (url: string) =>
|
||||||
new Promise<TiktokAPIResponse>((resolve, reject) => {
|
new Promise<TiktokAPIResponse>((resolve, reject) => {
|
||||||
|
if (!TiktokURLregex.test(url)) {
|
||||||
|
return resolve({
|
||||||
|
status: "error",
|
||||||
|
message: "Invalid Tiktok URL. Make sure your url is correct!"
|
||||||
|
})
|
||||||
|
}
|
||||||
url = url.replace("https://vm", "https://vt")
|
url = url.replace("https://vm", "https://vt")
|
||||||
axios
|
Axios.head(url)
|
||||||
.head(url)
|
|
||||||
.then(async ({ request }) => {
|
.then(async ({ request }) => {
|
||||||
const { responseUrl } = request.res
|
const { responseUrl } = request.res
|
||||||
let ID = responseUrl.match(/\d{17,21}/g)
|
let ID = responseUrl.match(/\d{17,21}/g)
|
||||||
@ -20,7 +33,7 @@ export const TiktokAPI = (url: string) =>
|
|||||||
|
|
||||||
let data2 = await fetchTiktokData(ID)
|
let data2 = await fetchTiktokData(ID)
|
||||||
|
|
||||||
if (!data2.content) {
|
if (!data2?.content) {
|
||||||
return resolve({
|
return resolve({
|
||||||
status: "error",
|
status: "error",
|
||||||
message: "Failed to fetch tiktok data. Make sure your tiktok url is correct!"
|
message: "Failed to fetch tiktok data. Make sure your tiktok url is correct!"
|
||||||
@ -40,6 +53,7 @@ export const TiktokAPI = (url: string) =>
|
|||||||
createTime: content.create_time,
|
createTime: content.create_time,
|
||||||
description: content.desc,
|
description: content.desc,
|
||||||
hashtag: content.text_extra.filter((x) => x.hashtag_name !== undefined).map((v) => v.hashtag_name),
|
hashtag: content.text_extra.filter((x) => x.hashtag_name !== undefined).map((v) => v.hashtag_name),
|
||||||
|
isADS: content.is_ads,
|
||||||
author,
|
author,
|
||||||
statistics,
|
statistics,
|
||||||
images: content.image_post_info.images.map((v) => v.display_image.url_list[0]),
|
images: content.image_post_info.images.map((v) => v.display_image.url_list[0]),
|
||||||
@ -48,6 +62,16 @@ export const TiktokAPI = (url: string) =>
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Video Result
|
// Video Result
|
||||||
|
const video: Video = {
|
||||||
|
ratio: content.video.ratio,
|
||||||
|
duration: content.video.duration,
|
||||||
|
playAddr: content.video.play_addr.url_list,
|
||||||
|
downloadAddr: content.video.download_addr.url_list,
|
||||||
|
cover: content.video.cover.url_list,
|
||||||
|
dynamicCover: content.video.dynamic_cover.url_list,
|
||||||
|
originCover: content.video.origin_cover.url_list
|
||||||
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
status: "success",
|
status: "success",
|
||||||
result: {
|
result: {
|
||||||
@ -56,13 +80,10 @@ export const TiktokAPI = (url: string) =>
|
|||||||
createTime: content.create_time,
|
createTime: content.create_time,
|
||||||
description: content.desc,
|
description: content.desc,
|
||||||
hashtag: content.text_extra.filter((x) => x.hashtag_name !== undefined).map((v) => v.hashtag_name),
|
hashtag: content.text_extra.filter((x) => x.hashtag_name !== undefined).map((v) => v.hashtag_name),
|
||||||
duration: toMinute(content.duration),
|
isADS: content.is_ads,
|
||||||
author,
|
author,
|
||||||
statistics,
|
statistics,
|
||||||
video: content.video.play_addr.url_list,
|
video,
|
||||||
cover: content.video.cover.url_list,
|
|
||||||
dynamicCover: content.video.dynamic_cover.url_list,
|
|
||||||
originCover: content.video.origin_cover.url_list,
|
|
||||||
music
|
music
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -71,62 +92,54 @@ export const TiktokAPI = (url: string) =>
|
|||||||
.catch((e) => resolve({ status: "error", message: e.message }))
|
.catch((e) => resolve({ status: "error", message: e.message }))
|
||||||
})
|
})
|
||||||
|
|
||||||
const fetchTiktokData = async (ID: string) => {
|
const fetchTiktokData = async (ID: string): Promise<responseParser> | null => {
|
||||||
let data2: responseParser
|
const res = await fetch(
|
||||||
await asyncRetry(
|
_tiktokapi(
|
||||||
async () => {
|
new URLSearchParams(
|
||||||
const res = await fetch(
|
_tiktokApiParams({
|
||||||
_tiktokapi(
|
aweme_id: ID
|
||||||
new URLSearchParams(
|
})
|
||||||
withParams({
|
).toString()
|
||||||
aweme_id: ID
|
),
|
||||||
})
|
{
|
||||||
).toString()
|
method: "GET",
|
||||||
),
|
headers: {
|
||||||
{
|
"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"
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"User-Agent": "com.ss.android.ugc.trill/494+Mozilla/5.0+(Linux;+Android+12;+2112123G+Build/SKQ1.211006.001;+wv)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Version/4.0+Chrome/107.0.5304.105+Mobile+Safari/537.36"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.headers.get("content-length") !== "0") {
|
|
||||||
const data = await res.json()
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
data2 = parseTiktokData(data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
throw new Error("Data is empty!")
|
|
||||||
},
|
|
||||||
{ forever: true, minTimeout: 0, maxTimeout: 0 }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return data2
|
if (res.headers.get("content-length") !== "0") {
|
||||||
|
const data = await res.json()
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
return parseTiktokData(ID, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseTiktokData = (data: any): responseParser => {
|
const parseTiktokData = (ID: string, data: any): responseParser => {
|
||||||
let content = data?.aweme_list
|
let content = data?.aweme_list
|
||||||
|
|
||||||
if (!content) return { content: null }
|
if (!content) return { content: null }
|
||||||
|
|
||||||
content = content[0]
|
content = content.find((v: any) => v.aweme_id === ID)
|
||||||
|
|
||||||
// Statistics Result
|
// Statistics Result
|
||||||
const statistics: Statistics = {
|
const statistics: Statistics = {
|
||||||
playCount: content.statistics.play_count,
|
|
||||||
downloadCount: content.statistics.download_count,
|
|
||||||
shareCount: content.statistics.share_count,
|
|
||||||
commentCount: content.statistics.comment_count,
|
commentCount: content.statistics.comment_count,
|
||||||
likeCount: content.statistics.digg_count,
|
diggCount: content.statistics.digg_count,
|
||||||
favoriteCount: content.statistics.collect_count,
|
downloadCount: content.statistics.download_count,
|
||||||
|
playCount: content.statistics.play_count,
|
||||||
|
shareCount: content.statistics.share_count,
|
||||||
forwardCount: content.statistics.forward_count,
|
forwardCount: content.statistics.forward_count,
|
||||||
whatsappShareCount: content.statistics.whatsapp_share_count,
|
|
||||||
loseCount: content.statistics.lose_count,
|
loseCount: content.statistics.lose_count,
|
||||||
loseCommentCount: content.statistics.lose_comment_count
|
loseCommentCount: content.statistics.lose_comment_count,
|
||||||
|
whatsappShareCount: content.statistics.whatsapp_share_count,
|
||||||
|
collectCount: content.statistics.collect_count,
|
||||||
|
repostCount: content.statistics.repost_count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Author Result
|
// Author Result
|
||||||
@ -151,68 +164,11 @@ const parseTiktokData = (data: any): responseParser => {
|
|||||||
coverLarge: content.music.cover_large.url_list,
|
coverLarge: content.music.cover_large.url_list,
|
||||||
coverMedium: content.music.cover_medium.url_list,
|
coverMedium: content.music.cover_medium.url_list,
|
||||||
coverThumb: content.music.cover_thumb.url_list,
|
coverThumb: content.music.cover_thumb.url_list,
|
||||||
duration: content.music.duration
|
duration: content.music.duration,
|
||||||
|
isCommerceMusic: content.music.is_commerce_music,
|
||||||
|
isOriginalSound: content.music.is_original_sound,
|
||||||
|
isAuthorArtist: content.music.is_author_artist
|
||||||
}
|
}
|
||||||
|
|
||||||
return { content, statistics, author, music }
|
return { content, statistics, author, music }
|
||||||
}
|
}
|
||||||
|
|
||||||
const withParams = (args) => {
|
|
||||||
return {
|
|
||||||
...args,
|
|
||||||
version_name: "1.1.9",
|
|
||||||
version_code: "2018111632",
|
|
||||||
build_number: "1.1.9",
|
|
||||||
manifest_version_code: "2018111632",
|
|
||||||
update_version_code: "2018111632",
|
|
||||||
openudid: randomChar("0123456789abcdef", 16),
|
|
||||||
uuid: randomChar("1234567890", 16),
|
|
||||||
_rticket: Date.now() * 1000,
|
|
||||||
ts: Date.now(),
|
|
||||||
device_brand: "Google",
|
|
||||||
device_type: "Pixel 4",
|
|
||||||
device_platform: "android",
|
|
||||||
resolution: "1080*1920",
|
|
||||||
dpi: 420,
|
|
||||||
os_version: "10",
|
|
||||||
os_api: "29",
|
|
||||||
carrier_region: "US",
|
|
||||||
sys_region: "US",
|
|
||||||
region: "US",
|
|
||||||
app_name: "trill",
|
|
||||||
app_language: "en",
|
|
||||||
language: "en",
|
|
||||||
timezone_name: "America/New_York",
|
|
||||||
timezone_offset: "-14400",
|
|
||||||
channel: "googleplay",
|
|
||||||
ac: "wifi",
|
|
||||||
mcc_mnc: "310260",
|
|
||||||
is_my_cn: 0,
|
|
||||||
aid: 1180,
|
|
||||||
ssmix: "a",
|
|
||||||
as: "a1qwert123",
|
|
||||||
cp: "cbfhckdckkde1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const toMinute = (duration: number) => {
|
|
||||||
const mins = ~~((duration % 3600) / 60)
|
|
||||||
const secs = ~~duration % 60
|
|
||||||
|
|
||||||
let ret = ""
|
|
||||||
|
|
||||||
ret += "" + mins + ":" + (secs < 10 ? "0" : "")
|
|
||||||
ret += "" + secs
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
const randomChar = (char: string, range: number) => {
|
|
||||||
let chars = ""
|
|
||||||
|
|
||||||
for (let i = 0; i < range; i++) {
|
|
||||||
chars += char[Math.floor(Math.random() * char.length)]
|
|
||||||
}
|
|
||||||
|
|
||||||
return chars
|
|
||||||
}
|
|
||||||
|
|||||||
196
src/utils/search/tiktok_stalker.ts
Normal file
196
src/utils/search/tiktok_stalker.ts
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import Axios from "axios"
|
||||||
|
import qs from "qs"
|
||||||
|
import { load } from "cheerio"
|
||||||
|
import { _tiktokurl } from "../../constants/api"
|
||||||
|
import { AuthorPost, Posts, StalkResult, Stats, Users } from "../../types/search/stalker"
|
||||||
|
import { _userPostsParams } from "../../constants/params"
|
||||||
|
import { createCipheriv } from "crypto"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tiktok Stalk User
|
||||||
|
* @param {string} username - The username you want to stalk
|
||||||
|
* @param {object|string} cookie - Your Tiktok Cookie (optional)
|
||||||
|
* @returns {Promise<StalkResult>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const StalkUser = (username: string, cookie?: any): Promise<StalkResult> =>
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
username = username.replace("@", "")
|
||||||
|
Axios.get(`${_tiktokurl}/@${username}`, {
|
||||||
|
headers: {
|
||||||
|
"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",
|
||||||
|
cookie: typeof cookie === "object" ? cookie.map((v) => `${v.name}=${v.value}`).join("; ") : cookie
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(async ({ data }) => {
|
||||||
|
const $ = load(data)
|
||||||
|
const result = JSON.parse($("script#__UNIVERSAL_DATA_FOR_REHYDRATION__").text())
|
||||||
|
if (!result["__DEFAULT_SCOPE__"] && !result["__DEFAULT_SCOPE__"]["webapp.user-detail"]) {
|
||||||
|
return resolve({
|
||||||
|
status: "error",
|
||||||
|
message: "User not found!"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const dataUser = result["__DEFAULT_SCOPE__"]["webapp.user-detail"]["userInfo"]
|
||||||
|
|
||||||
|
// Posts Result
|
||||||
|
let hasMore = true
|
||||||
|
let cursor
|
||||||
|
const posts: Posts[] = []
|
||||||
|
|
||||||
|
while (hasMore) {
|
||||||
|
const result2 = await request(dataUser.user.secUid, cursor, 30)
|
||||||
|
|
||||||
|
if (result2 === "") hasMore = false
|
||||||
|
|
||||||
|
result2?.itemList?.forEach((v) => {
|
||||||
|
const author: AuthorPost = {
|
||||||
|
id: v.author.id,
|
||||||
|
username: v.author.uniqueId,
|
||||||
|
nickname: v.author.nickname,
|
||||||
|
avatarLarger: v.author.avatarLarger,
|
||||||
|
avatarThumb: v.author.avatarThumb,
|
||||||
|
avatarMedium: v.author.avatarMedium,
|
||||||
|
signature: v.author.signature,
|
||||||
|
verified: v.author.verified,
|
||||||
|
openFavorite: v.author.openFavorite,
|
||||||
|
privateAccount: v.author.privateAccount,
|
||||||
|
isADVirtual: v.author.isADVirtual,
|
||||||
|
isEmbedBanned: v.author.isEmbedBanned
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.imagePost) {
|
||||||
|
const images: string[] = v.imagePost.images.map((img) => img.imageURL.urlList[0])
|
||||||
|
|
||||||
|
posts.push({
|
||||||
|
id: v.id,
|
||||||
|
desc: v.desc,
|
||||||
|
createTime: v.createTime,
|
||||||
|
digged: v.digged,
|
||||||
|
duetEnabled: v.duetEnabled,
|
||||||
|
forFriend: v.forFriend,
|
||||||
|
officalItem: v.officalItem,
|
||||||
|
originalItem: v.originalItem,
|
||||||
|
privateItem: v.privateItem,
|
||||||
|
shareEnabled: v.shareEnabled,
|
||||||
|
stitchEnabled: v.stitchEnabled,
|
||||||
|
stats: v.stats,
|
||||||
|
music: v.music,
|
||||||
|
author,
|
||||||
|
images
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const video = {
|
||||||
|
id: v.video.id,
|
||||||
|
duration: v.video.duration,
|
||||||
|
format: v.video.format,
|
||||||
|
bitrate: v.video.bitrate,
|
||||||
|
ratio: v.video.ratio,
|
||||||
|
playAddr: v.video.playAddr,
|
||||||
|
cover: v.video.cover,
|
||||||
|
originCover: v.video.originCover,
|
||||||
|
dynamicCover: v.video.dynamicCover,
|
||||||
|
downloadAddr: v.video.downloadAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
posts.push({
|
||||||
|
id: v.id,
|
||||||
|
desc: v.desc,
|
||||||
|
createTime: v.createTime,
|
||||||
|
digged: v.digged,
|
||||||
|
duetEnabled: v.duetEnabled,
|
||||||
|
forFriend: v.forFriend,
|
||||||
|
officalItem: v.officalItem,
|
||||||
|
originalItem: v.originalItem,
|
||||||
|
privateItem: v.privateItem,
|
||||||
|
shareEnabled: v.shareEnabled,
|
||||||
|
stitchEnabled: v.stitchEnabled,
|
||||||
|
stats: v.stats,
|
||||||
|
music: v.music,
|
||||||
|
author,
|
||||||
|
video
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
hasMore = result2.hasMore
|
||||||
|
cursor = hasMore ? result2.cursor : null
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Info Result
|
||||||
|
const users: Users = {
|
||||||
|
id: dataUser.user.id,
|
||||||
|
username: dataUser.user.uniqueId,
|
||||||
|
nickname: dataUser.user.nickname,
|
||||||
|
avatarLarger: dataUser.user.avatarLarger,
|
||||||
|
avatarThumb: dataUser.user.avatarThumb,
|
||||||
|
avatarMedium: dataUser.user.avatarMedium,
|
||||||
|
signature: dataUser.user.signature,
|
||||||
|
verified: dataUser.user.verified,
|
||||||
|
privateAccount: dataUser.user.privateAccount,
|
||||||
|
region: dataUser.user.region,
|
||||||
|
commerceUser: dataUser.user.commerceUserInfo.commerceUser,
|
||||||
|
usernameModifyTime: dataUser.user.uniqueIdModifyTime,
|
||||||
|
nicknameModifyTime: dataUser.user.nickNameModifyTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics Result
|
||||||
|
const stats: Stats = {
|
||||||
|
followerCount: dataUser.stats.followerCount,
|
||||||
|
followingCount: dataUser.stats.followingCount,
|
||||||
|
heartCount: dataUser.stats.heartCount,
|
||||||
|
videoCount: dataUser.stats.videoCount,
|
||||||
|
likeCount: dataUser.stats.diggCount,
|
||||||
|
friendCount: dataUser.stats.friendCount,
|
||||||
|
postCount: posts.length
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
status: "success",
|
||||||
|
result: {
|
||||||
|
users,
|
||||||
|
stats,
|
||||||
|
posts
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((e) => resolve({ status: "error", message: e.message }))
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thanks to:
|
||||||
|
* https://github.com/atharahmed/tiktok-private-api/blob/020ede2eaa6021bcd363282d8cef1aacaff2f88c/src/repositories/user.repository.ts#L148
|
||||||
|
*/
|
||||||
|
|
||||||
|
const request = async (secUid: string, cursor = 0, count = 30) => {
|
||||||
|
const { data } = await Axios.get(`https://www.tiktok.com/api/post/item_list/?${_userPostsParams()}`, {
|
||||||
|
headers: {
|
||||||
|
"user-agent": "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",
|
||||||
|
"X-tt-params": xttparams(
|
||||||
|
qs.stringify({
|
||||||
|
aid: "1988",
|
||||||
|
cookie_enabled: true,
|
||||||
|
screen_width: 0,
|
||||||
|
screen_height: 0,
|
||||||
|
browser_language: "",
|
||||||
|
browser_platform: "",
|
||||||
|
browser_name: "",
|
||||||
|
browser_version: "",
|
||||||
|
browser_online: "",
|
||||||
|
timezone_name: "Europe/London",
|
||||||
|
secUid,
|
||||||
|
cursor,
|
||||||
|
count,
|
||||||
|
is_encryption: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
const xttparams = (params) => {
|
||||||
|
const cipher = createCipheriv("aes-128-cbc", "webapp1.0+202106", "webapp1.0+202106")
|
||||||
|
return Buffer.concat([cipher.update(params), cipher.final()]).toString("base64")
|
||||||
|
}
|
||||||
@ -1,138 +0,0 @@
|
|||||||
import axios from "axios"
|
|
||||||
import { load } from "cheerio"
|
|
||||||
import { _tiktokurl } from "../../api"
|
|
||||||
import { StalkResult, Stats, Users } from "../../types/stalker"
|
|
||||||
|
|
||||||
const getCookie = () =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.get("https://pastebin.com/raw/ELJjcbZT")
|
|
||||||
.then(({ data: cookie }) => {
|
|
||||||
resolve(cookie)
|
|
||||||
})
|
|
||||||
.catch((e) => resolve({ status: "error", message: "Failed to fetch cookie." }))
|
|
||||||
})
|
|
||||||
|
|
||||||
export const TiktokStalk = (username: string, options?: { cookie: string }): Promise<StalkResult> =>
|
|
||||||
new Promise(async (resolve, reject) => {
|
|
||||||
username = username.replace("@", "")
|
|
||||||
axios
|
|
||||||
.get(`${_tiktokurl}/@${username}`, {
|
|
||||||
headers: {
|
|
||||||
"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",
|
|
||||||
cookie: (options?.cookie ? options.cookie : await getCookie()) as string
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(({ data }) => {
|
|
||||||
const $ = load(data)
|
|
||||||
const result = JSON.parse($("script#__UNIVERSAL_DATA_FOR_REHYDRATION__").text())
|
|
||||||
if (!result?.__DEFAULT_SCOPE__?.["webapp.user-detail"]) {
|
|
||||||
return resolve({
|
|
||||||
status: "error",
|
|
||||||
message: "User not found!"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const dataUser = result.__DEFAULT_SCOPE__["webapp.user-detail"].userInfo
|
|
||||||
|
|
||||||
// User Info Result
|
|
||||||
const users: Users = {
|
|
||||||
username: dataUser.user.uniqueId,
|
|
||||||
nickname: dataUser.user.nickname,
|
|
||||||
avatarLarger: dataUser.user.avatarLarger,
|
|
||||||
avatarThumb: dataUser.user.avatarThumb,
|
|
||||||
avatarMedium: dataUser.user.avatarMedium,
|
|
||||||
signature: dataUser.user.signature,
|
|
||||||
verified: dataUser.user.verified,
|
|
||||||
privateAccount: dataUser.user.privateAccount,
|
|
||||||
region: dataUser.user.region,
|
|
||||||
commerceUser: dataUser.user.commerceUserInfo.commerceUser,
|
|
||||||
usernameModifyTime: dataUser.user.uniqueIdModifyTime,
|
|
||||||
nicknameModifyTime: dataUser.user.nickNameModifyTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statistics Result
|
|
||||||
const stats: Stats = {
|
|
||||||
followerCount: dataUser.stats.followerCount,
|
|
||||||
followingCount: dataUser.stats.followingCount,
|
|
||||||
heartCount: dataUser.stats.heartCount,
|
|
||||||
videoCount: dataUser.stats.videoCount,
|
|
||||||
likeCount: dataUser.stats.diggCount,
|
|
||||||
friendCount: dataUser.stats.friendCount
|
|
||||||
// postCount: itemKeys.length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Posts Result
|
|
||||||
/**
|
|
||||||
const posts: Posts[] = []
|
|
||||||
itemKeys.forEach((key) => {
|
|
||||||
const post = result.ItemModule[key]
|
|
||||||
let media
|
|
||||||
if (post.imagePost) {
|
|
||||||
// Images or Slide Posts Result
|
|
||||||
media = {
|
|
||||||
images: post.imagePost.images.map((v: any) => v.imageURL.urlList[0])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Video Posts Result
|
|
||||||
media = {
|
|
||||||
video: {
|
|
||||||
id: post.video.id,
|
|
||||||
duration: post.video.duration,
|
|
||||||
ratio: post.video.ratio,
|
|
||||||
cover: post.video.cover,
|
|
||||||
originCover: post.video.originCover,
|
|
||||||
dynamicCover: post.video.dynamicCover,
|
|
||||||
playAddr: post.video.playAddr,
|
|
||||||
downloadAddr: post.video.downloadAddr,
|
|
||||||
format: post.video.format,
|
|
||||||
bitrate: post.video.bitrate
|
|
||||||
} as Video
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Music Posts Result
|
|
||||||
const music: Music = {
|
|
||||||
id: post.music.id,
|
|
||||||
title: post.music.title,
|
|
||||||
authorName: post.music.authorName,
|
|
||||||
album: post.music.album,
|
|
||||||
coverLarge: post.music.coverLarge,
|
|
||||||
coverMedium: post.music.coverMedium,
|
|
||||||
coverThumb: post.music.coverThumb,
|
|
||||||
playUrl: post.music.playUrl,
|
|
||||||
duration: post.music.duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statistics Posts Result
|
|
||||||
const statistics: Statistics = {
|
|
||||||
likeCount: post.stats.diggCount,
|
|
||||||
shareCount: post.stats.shareCount,
|
|
||||||
commentCount: post.stats.commentCount,
|
|
||||||
playCount: post.stats.playCount,
|
|
||||||
favoriteCount: post.stats.collectCount
|
|
||||||
}
|
|
||||||
|
|
||||||
posts.push({
|
|
||||||
id: post.id,
|
|
||||||
desc: post.desc,
|
|
||||||
createTime: post.createTime,
|
|
||||||
author: post.author,
|
|
||||||
locationCreated: post.locationCreated,
|
|
||||||
hashtags: post.challenges.map((v: any) => v.title),
|
|
||||||
statistics,
|
|
||||||
music,
|
|
||||||
...media
|
|
||||||
})
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
resolve({
|
|
||||||
status: "success",
|
|
||||||
result: {
|
|
||||||
users,
|
|
||||||
stats
|
|
||||||
// posts
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch((e) => resolve({ status: "error", message: e.message }))
|
|
||||||
})
|
|
||||||
Loading…
x
Reference in New Issue
Block a user