feat: added tiktok downloader v2 & response posts tiktok stalk
This commit is contained in:
parent
246c6bdb30
commit
f7f6d3776a
@ -1,2 +1,2 @@
|
||||
export const _tiktokurl: string = "https://www.tiktok.com"
|
||||
export const _tiktokapi = (id: string): string => `https://api16-core.tiktokv.com/aweme/v1/feed/?aweme_id=${id}`
|
||||
export const _tiktokapi = (id: string): string => `https://api.tiktokv.com/aweme/v1/feed/?aweme_id=${id}`
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./downloader"
|
||||
export * from "./stalker"
|
||||
@ -4,6 +4,7 @@ export interface StalkResult {
|
||||
result?: {
|
||||
users: Users
|
||||
stats: Stats
|
||||
posts: Posts[]
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +16,7 @@ export interface Users {
|
||||
avatarMedium: string
|
||||
signature: string
|
||||
verified: boolean
|
||||
privateAccount: boolean
|
||||
region: string
|
||||
commerceUser: boolean
|
||||
usernameModifyTime: number
|
||||
@ -28,4 +30,50 @@ export interface Stats {
|
||||
videoCount: number
|
||||
likeCount: number
|
||||
friendCount: number
|
||||
postCount: number
|
||||
}
|
||||
|
||||
export interface Posts {
|
||||
id: string
|
||||
desc: string
|
||||
createTime: number
|
||||
author: string
|
||||
locationCreated: string
|
||||
hashtags: string[]
|
||||
statistics: Statistics
|
||||
video: Video
|
||||
music: Music
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
likeCount: number
|
||||
shareCount: number
|
||||
commentCount: number
|
||||
playCount: number
|
||||
favoriteCount: number
|
||||
}
|
||||
|
||||
export interface Video {
|
||||
id: string
|
||||
duration: string
|
||||
ratio: string
|
||||
cover: string
|
||||
originCover: string
|
||||
dynamicCover: string
|
||||
playAddr: string
|
||||
downloadAddr: string
|
||||
format: string
|
||||
bitrate: number
|
||||
}
|
||||
|
||||
export interface Music {
|
||||
id: string
|
||||
title: string
|
||||
album: string
|
||||
playUrl: string
|
||||
coverLarge: string
|
||||
coverMedium: string
|
||||
coverThumb: string
|
||||
authorName: string
|
||||
duration: string
|
||||
}
|
||||
|
||||
30
src/types/tiktokdownload.ts
Normal file
30
src/types/tiktokdownload.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export interface TiktokFetchTT {
|
||||
status: "success" | "error"
|
||||
message?: string
|
||||
result?: string
|
||||
}
|
||||
|
||||
export interface TiktokDownload {
|
||||
status: "success" | "error"
|
||||
message?: string
|
||||
result?: {
|
||||
type: "image" | "video"
|
||||
desc: string
|
||||
author: Author
|
||||
statistics: Statistics
|
||||
images?: string[]
|
||||
video?: string
|
||||
music: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface Author {
|
||||
avatar: string
|
||||
nickname: string
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
likeCount: string
|
||||
commentCount: string
|
||||
shareCount: string
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
export * from "./downloader"
|
||||
export * from "./switch"
|
||||
export * from "./stalker"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import axios from "axios"
|
||||
import { load } from "cheerio"
|
||||
import { _tiktokurl } from "../api"
|
||||
import { StalkResult, Stats, Users } from "../types"
|
||||
import { Music, Posts, StalkResult, Statistics, Stats, Users, Video } from "../types/stalker"
|
||||
|
||||
const getCookie = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
@ -33,6 +33,9 @@ export const TiktokStalk = (username: string, options: { cookie: string }): Prom
|
||||
})
|
||||
}
|
||||
const user = result.UserModule
|
||||
const itemKeys = Object.keys(result.ItemModule)
|
||||
|
||||
// User Info Result
|
||||
const users: Users = {
|
||||
username: user.users[username].uniqueId,
|
||||
nickname: user.users[username].nickname,
|
||||
@ -41,24 +44,92 @@ export const TiktokStalk = (username: string, options: { cookie: string }): Prom
|
||||
avatarMedium: user.users[username].avatarMedium,
|
||||
signature: user.users[username].signature,
|
||||
verified: user.users[username].verified,
|
||||
privateAccount: user.users[username].privateAccount,
|
||||
region: user.users[username].region,
|
||||
commerceUser: user.users[username].commerceUserInfo.commerceUser,
|
||||
usernameModifyTime: user.users[username].uniqueIdModifyTime,
|
||||
nicknameModifyTime: user.users[username].nickNameModifyTime
|
||||
}
|
||||
|
||||
// Statistics Result
|
||||
const stats: Stats = {
|
||||
followerCount: user.stats[username].followerCount,
|
||||
followingCount: user.stats[username].followingCount,
|
||||
heartCount: user.stats[username].heartCount,
|
||||
videoCount: user.stats[username].videoCount,
|
||||
likeCount: user.stats[username].diggCount,
|
||||
friendCount: user.stats[username].friendCount
|
||||
friendCount: user.stats[username].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
|
||||
stats,
|
||||
posts
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
17
src/utils/switch.ts
Normal file
17
src/utils/switch.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { TiktokDownload } from "./tiktokdownload"
|
||||
import { TiktokAPI } from "./tiktokapi"
|
||||
|
||||
export const TiktokDL = (url: string, options: { version: "v1" | "v2" }) =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
switch (options.version) {
|
||||
case "v1": {
|
||||
await TiktokAPI(url).then(resolve).catch(reject)
|
||||
}
|
||||
case "v2": {
|
||||
await TiktokDownload(url).then(resolve).catch(reject)
|
||||
}
|
||||
default: {
|
||||
await TiktokAPI(url).then(resolve).catch(reject)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,6 +1,6 @@
|
||||
import axios from "axios"
|
||||
import { _tiktokapi, _tiktokurl } from "../api"
|
||||
import { Author, DLResult, Statistics, Music } from "../types"
|
||||
import { Author, DLResult, Statistics, Music } from "../types/tiktokapi"
|
||||
|
||||
const toMinute = (duration) => {
|
||||
const mins = ~~((duration % 3600) / 60)
|
||||
@ -14,7 +14,7 @@ const toMinute = (duration) => {
|
||||
return ret
|
||||
}
|
||||
|
||||
export const TiktokDL = (url: string): Promise<DLResult> =>
|
||||
export const TiktokAPI = (url: string): Promise<DLResult> =>
|
||||
new Promise((resolve, reject) => {
|
||||
url = url.replace("https://vm", "https://vt")
|
||||
axios
|
||||
91
src/utils/tiktokdownload.ts
Normal file
91
src/utils/tiktokdownload.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import Axios from "axios"
|
||||
import { load } from "cheerio"
|
||||
import { Author, Statistics, TiktokFetchTT } from "../types/tiktokdownload"
|
||||
|
||||
const fetchTT = (): Promise<TiktokFetchTT> =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
Axios.get("https://tiktokdownload.online/", {
|
||||
headers: {
|
||||
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0"
|
||||
}
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const regex = /form\.setAttribute\("include-vals",\s*"([^"]+)"\)/
|
||||
const match = data.match(regex)
|
||||
if (match) {
|
||||
const includeValsValue = match[1]
|
||||
resolve({ status: "success", result: includeValsValue })
|
||||
} else {
|
||||
resolve({ status: "error", message: "Not found" })
|
||||
}
|
||||
})
|
||||
.catch((e) => resolve({ status: "error", message: e.message }))
|
||||
})
|
||||
|
||||
export const TiktokDownload = (url: string) =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
const tt: TiktokFetchTT = await fetchTT()
|
||||
if (tt.status !== "success") return resolve(tt)
|
||||
Axios("https://tiktokdownload.online/abc?url=dl", {
|
||||
method: "POST",
|
||||
data: new URLSearchParams(
|
||||
Object.entries({
|
||||
id: url,
|
||||
locale: "en",
|
||||
tt: tt.result
|
||||
})
|
||||
)
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const $ = load(data)
|
||||
|
||||
// Result
|
||||
const desc = $("p.maintext").text().trim()
|
||||
const author: Author = {
|
||||
avatar: $("img.result_author").attr("src"),
|
||||
nickname: $("h2").text().trim()
|
||||
}
|
||||
const statistics: Statistics = {
|
||||
likeCount: $("#trending-actions > .justify-content-start").text().trim(),
|
||||
commentCount: $("#trending-actions > .justify-content-center").text().trim(),
|
||||
shareCount: $("#trending-actions > .justify-content-end").text().trim()
|
||||
}
|
||||
|
||||
// Images / Slide Result
|
||||
const images: string[] = []
|
||||
$("ul.splide__list > li")
|
||||
.get()
|
||||
.map((img) => {
|
||||
images.push($(img).find("img").attr("src"))
|
||||
})
|
||||
|
||||
if (images.length !== 0) {
|
||||
// Images / Slide Result
|
||||
resolve({
|
||||
status: "success",
|
||||
result: {
|
||||
type: "image",
|
||||
desc,
|
||||
author,
|
||||
statistics,
|
||||
images,
|
||||
music: $("a.music").attr("href")
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Video Result
|
||||
resolve({
|
||||
status: "success",
|
||||
result: {
|
||||
type: "video",
|
||||
desc,
|
||||
author,
|
||||
statistics,
|
||||
video: $("a.without_watermark").attr("href"),
|
||||
music: $("a.music").attr("href")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(console.log)
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user