diff --git a/src/index.ts b/src/index.ts index 3257517..e84015c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,18 +57,18 @@ export = { * @param {number} options.page - The page of search (optional) * @returns {Promise} */ - Search: async (query: string, options: { type: T; cookie?: string; page?: number }): Promise> => { + Search: async (query: string, options: { type: T; cookie?: string; page?: number; proxy?: string }): Promise> => { switch (options?.type) { case "user": { - const response = await SearchUser(query, options?.cookie, options?.page) + const response = await SearchUser(query, options?.cookie, options?.page, options?.proxy) return response as TiktokSearchResponse } case "live": { - const response = await SearchLive(query, options?.cookie, options?.page) + const response = await SearchLive(query, options?.cookie, options?.page, options?.proxy) return response as TiktokSearchResponse } default: { - const response = await SearchUser(query, options?.cookie, options?.page) + const response = await SearchUser(query, options?.cookie, options?.page, options?.proxy) return response as TiktokSearchResponse } } @@ -80,8 +80,8 @@ export = { * @param {string} options.cookie - Your Tiktok Cookie (optional) * @returns {Promise} */ - StalkUser: async (username: string, options?: { cookie?: string; postLimit?: number }): Promise => { - const response = await StalkUser(username, options?.cookie, options?.postLimit) + StalkUser: async (username: string, options?: { cookie?: string; postLimit?: number; proxy?: string }): Promise => { + const response = await StalkUser(username, options?.cookie, options?.postLimit, options?.proxy) return response } } diff --git a/src/utils/downloader/musicalDown.ts b/src/utils/downloader/musicalDown.ts index 1a55a75..2009e58 100644 --- a/src/utils/downloader/musicalDown.ts +++ b/src/utils/downloader/musicalDown.ts @@ -2,6 +2,8 @@ import Axios from "axios" import { load } from "cheerio" import { MusicalDownResponse, getMusic, getRequest } from "../../types/downloader/musicaldown" import { _musicaldownapi, _musicaldownmusicapi, _musicaldownurl } from "../../constants/api" +import { HttpsProxyAgent } from "https-proxy-agent" +import { SocksProxyAgent } from "socks-proxy-agent" /** * Using API from Website: @@ -10,7 +12,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) => +const getRequest = (url: string, proxy?: string) => new Promise((resolve) => { if (!TiktokURLregex.test(url)) { return resolve({ @@ -18,10 +20,12 @@ const getRequest = (url: string) => message: "Invalid Tiktok URL. Make sure your url is correct!" }) } - Axios.get(_musicaldownurl, { + Axios(_musicaldownurl, { + method: "GET", headers: { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0" - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then((data) => { const cookie = data.headers["set-cookie"][0].split(";")[0] + "; " + "lang=en" @@ -37,14 +41,16 @@ const getRequest = (url: string) => .catch((e) => resolve({ status: "error", message: "Failed to get the request form!" })) }) -const getMusic = (cookie: string) => +const getMusic = (cookie: string, proxy?: string) => new Promise((resolve) => { - Axios.get(_musicaldownmusicapi, { + Axios(_musicaldownmusicapi, { + method: "GET", headers: { cookie: cookie, "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0" - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then(({ data }) => { const $ = load(data) @@ -57,10 +63,11 @@ const getMusic = (cookie: string) => /** * Tiktok MusicalDown Downloader * @param {string} url - Tiktok URL + * @param {string} proxy - Proxy * @returns {Promise} */ -export const MusicalDown = (url: string) => +export const MusicalDown = (url: string, proxy?: string) => new Promise(async (resolve) => { const request: getRequest = await getRequest(url) if (request.status !== "success") return resolve({ status: "error", message: request.message }) @@ -72,7 +79,8 @@ export const MusicalDown = (url: string) => "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0" }, - data: new URLSearchParams(Object.entries(request.request)) + data: new URLSearchParams(Object.entries(request.request)), + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then(async ({ data }) => { const $ = load(data) diff --git a/src/utils/downloader/ssstik.ts b/src/utils/downloader/ssstik.ts index bc1904f..8405b89 100644 --- a/src/utils/downloader/ssstik.ts +++ b/src/utils/downloader/ssstik.ts @@ -3,6 +3,8 @@ import asyncRetry from "async-retry" import { load } from "cheerio" import { Author, Statistics, SSSTikFetchTT, SSSTikResponse } from "../../types/downloader/ssstik" import { _ssstikapi, _ssstikurl } from "../../constants/api" +import { HttpsProxyAgent } from "https-proxy-agent" +import { SocksProxyAgent } from "socks-proxy-agent" /** * Using API from Website: @@ -11,12 +13,14 @@ 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 = () => +const fetchTT = (proxy?: string) => new Promise(async (resolve) => { - Axios.get(_ssstikurl, { + Axios(_ssstikurl, { + method: "GET", headers: { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0" - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then(({ data }) => { const regex = /s_tt\s*=\s*["']([^"']+)["']/ @@ -34,10 +38,11 @@ const fetchTT = () => /** * Tiktok SSSTik Downloader * @param {string} url - Tiktok URL + * @param {string} proxy - Your Proxy (optional) * @returns {Promise} */ -export const SSSTik = (url: string) => +export const SSSTik = (url: string, proxy?: string) => new Promise(async (resolve) => { try { if (!TiktokURLregex.test(url)) { @@ -46,7 +51,7 @@ export const SSSTik = (url: string) => message: "Invalid Tiktok URL. Make sure your url is correct!" }) } - const tt: SSSTikFetchTT = await fetchTT() + const tt: SSSTikFetchTT = await fetchTT(proxy) if (tt.status !== "success") return resolve({ status: "error", message: tt.message }) const response = asyncRetry( @@ -65,7 +70,8 @@ export const SSSTik = (url: string) => locale: "en", tt: tt.result }) - ) + ), + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) if (res.status === 200 && res.data !== "") return res.data diff --git a/src/utils/downloader/tiktokApi.ts b/src/utils/downloader/tiktokApi.ts index 30ce13b..dcdbfe3 100644 --- a/src/utils/downloader/tiktokApi.ts +++ b/src/utils/downloader/tiktokApi.ts @@ -3,16 +3,19 @@ import asyncRetry from "async-retry" import { _tiktokvFeed, _tiktokurl } from "../../constants/api" import { _tiktokApiParams } from "../../constants/params" import { Author, TiktokAPIResponse, Statistics, Music, responseParser, Video } from "../../types/downloader/tiktokApi" +import { HttpsProxyAgent } from "https-proxy-agent" +import { SocksProxyAgent } from "socks-proxy-agent" const TiktokURLregex = /https:\/\/(?:m|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/ /** * Tiktok API Downloader * @param {string} url - Tiktok URL + * @param {string} proxy - Your Proxy (optional) * @returns {Promise} */ -export const TiktokAPI = (url: string) => +export const TiktokAPI = (url: string, proxy?: string) => new Promise((resolve) => { if (!TiktokURLregex.test(url)) { return resolve({ @@ -21,7 +24,10 @@ export const TiktokAPI = (url: string) => }) } url = url.replace("https://vm", "https://vt") - Axios.head(url) + Axios(url, { + method: "HEAD", + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined + }) .then(async ({ request }) => { const { responseUrl } = request.res let ID = responseUrl.match(/\d{17,21}/g) @@ -32,7 +38,7 @@ export const TiktokAPI = (url: string) => }) ID = ID[0] - let data2 = await fetchTiktokData(ID) + let data2 = await fetchTiktokData(ID, proxy) if (!data2?.content) { return resolve({ @@ -57,7 +63,7 @@ export const TiktokAPI = (url: string) => isADS: content.is_ads, author, 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]) || [], music } }) @@ -66,11 +72,11 @@ export const TiktokAPI = (url: string) => 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 + playAddr: content.video?.play_addr?.url_list || [], // No Watermark Video + downloadAddr: content.video?.download_addr?.url_list || [], // Watermark Video + cover: content.video?.cover?.url_list || [], + dynamicCover: content.video?.dynamic_cover?.url_list || [], + originCover: content.video?.origin_cover?.url_list || [] } resolve({ @@ -93,11 +99,11 @@ export const TiktokAPI = (url: string) => .catch((e) => resolve({ status: "error", message: e.message })) }) -const fetchTiktokData = async (ID: string): Promise | null => { +const fetchTiktokData = async (ID: string, proxy?: string): Promise | null => { try { const response = asyncRetry( async () => { - const res = await fetch( + const res = await Axios( _tiktokvFeed( _tiktokApiParams({ aweme_id: ID @@ -107,13 +113,13 @@ const fetchTiktokData = async (ID: string): Promise | null => { method: "OPTIONS", headers: { "User-Agent": "com.zhiliaoapp.musically/300904 (2018111632; U; Android 10; en_US; Pixel 4; Build/QQ3A.200805.001; Cronet/58.0.2991.0)" - } + }, + httpsAgent: proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined) } ) - if (res.headers.get("content-length") !== "0") { - const data = await res.json() - return data + if (res.data !== "" && res.data.status_code === 0) { + return res.data } throw new Error("Failed to fetch tiktok data") @@ -163,8 +169,8 @@ const parseTiktokData = (ID: string, data: any): responseParser => { nickname: content.author.nickname, signature: content.author.signature, region: content.author.region, - avatarThumb: content.author.avatar_thumb.url_list, - avatarMedium: content.author.avatar_medium.url_list, + avatarThumb: content.author?.avatar_thumb?.url_list || [], + avatarMedium: content.author?.avatar_medium?.url_list || [], url: `${_tiktokurl}/@${content.author.unique_id}` } @@ -174,10 +180,10 @@ const parseTiktokData = (ID: string, data: any): responseParser => { title: content.music.title, author: content.music.author, album: content.music.album, - playUrl: content.music.play_url.url_list, - coverLarge: content.music.cover_large.url_list, - coverMedium: content.music.cover_medium.url_list, - coverThumb: content.music.cover_thumb.url_list, + playUrl: content.music?.play_url?.url_list || [], + coverLarge: content.music?.cover_large?.url_list || [], + coverMedium: content.music?.cover_medium?.url_list || [], + coverThumb: content.music?.cover_thumb?.url_list || [], duration: content.music.duration, isCommerceMusic: content.music.is_commerce_music, isOriginalSound: content.music.is_original_sound, diff --git a/src/utils/search/liveSearch.ts b/src/utils/search/liveSearch.ts index 72327cc..f59e996 100644 --- a/src/utils/search/liveSearch.ts +++ b/src/utils/search/liveSearch.ts @@ -2,15 +2,27 @@ import Axios from "axios" import { _tiktokSearchLiveFull } from "../../constants/api" import { _liveSearchParams } from "../../constants/params" import { LiveInfo, Owner, OwnerStats } from "../../types/search/liveSearch" +import { SocksProxyAgent } from "socks-proxy-agent" +import { HttpsProxyAgent } from "https-proxy-agent" -export const SearchLive = async (keyword: string, cookie?: any, page: number = 1) => +/** + * Tiktok Search Live + * @param {string} keyword - The keyword you want to search + * @param {object|string} cookie - Your Tiktok cookie (optional) + * @param {number} page - The page you want to search (optional) + * @param {string} proxy - Your Proxy (optional) + * @returns {Promise} + */ + +export const SearchLive = async (keyword: string, cookie?: any, page: number = 1, proxy?: string) => 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 - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then(({ data }) => { // Cookie Invalid diff --git a/src/utils/search/stalker.ts b/src/utils/search/stalker.ts index aaebfc1..1bd311e 100644 --- a/src/utils/search/stalker.ts +++ b/src/utils/search/stalker.ts @@ -5,22 +5,27 @@ import { _tiktokGetPosts, _tiktokurl } from "../../constants/api" import { AuthorPost, Posts, StalkResult, Stats, Users } from "../../types/search/stalker" import { _userPostsParams, _xttParams } from "../../constants/params" import { createCipheriv } from "crypto" +import { HttpsProxyAgent } from "https-proxy-agent" +import { SocksProxyAgent } from "socks-proxy-agent" /** * Tiktok Stalk User * @param {string} username - The username you want to stalk * @param {object|string} cookie - Your Tiktok Cookie (optional) + * @param {number} postLimit - The limit of post you want to get (optional) + * @param {string} proxy - Your Proxy (optional) * @returns {Promise} */ -export const StalkUser = (username: string, cookie?: any, postLimit?: number): Promise => +export const StalkUser = (username: string, cookie?: any, postLimit?: number, proxy?: string): Promise => new Promise(async (resolve) => { 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 - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then(async ({ data }) => { const $ = load(data) @@ -33,7 +38,7 @@ export const StalkUser = (username: string, cookie?: any, postLimit?: number): P } const dataUser = result["__DEFAULT_SCOPE__"]["webapp.user-detail"]["userInfo"] - const posts: Posts[] = await parsePosts(dataUser, postLimit) + const posts: Posts[] = await parsePosts(dataUser, postLimit, proxy) const { users, stats } = parseDataUser(dataUser, posts) resolve({ @@ -53,12 +58,13 @@ export const StalkUser = (username: string, cookie?: any, postLimit?: number): P * 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 request = async (secUid: string, cursor = 0, count = 30, proxy?: string) => { const { data } = await Axios.get(`${_tiktokGetPosts(_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(_xttParams(secUid, cursor, count)) - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) return data @@ -96,7 +102,7 @@ const parseDataUser = (dataUser: any, posts: Posts[]) => { return { users, stats } } -const parsePosts = async (dataUser: any, postLimit?: number): Promise => { +const parsePosts = async (dataUser: any, postLimit?: number, proxy?: string): Promise => { // Posts Result let hasMore = true let cursor: number | null = null @@ -107,7 +113,7 @@ const parsePosts = async (dataUser: any, postLimit?: number): Promise = // Prevent missing response posts for (let i = 0; i < 30; i++) { - result2 = await request(dataUser.user.secUid, cursor, 30) + result2 = await request(dataUser.user.secUid, cursor, 30, proxy) if (result2 !== "") break } diff --git a/src/utils/search/userSearch.ts b/src/utils/search/userSearch.ts index f24dcd3..394824d 100644 --- a/src/utils/search/userSearch.ts +++ b/src/utils/search/userSearch.ts @@ -2,23 +2,27 @@ import Axios from "axios" import { _tiktokSearchUserFull, _tiktokurl } from "../../constants/api" import { TiktokUserSearchResponse } from "../../types/search/userSearch" import { _userSearchParams } from "../../constants/params" +import { HttpsProxyAgent } from "https-proxy-agent" +import { SocksProxyAgent } from "socks-proxy-agent" /** * Tiktok Search User * @param {string} username - The username you want to search * @param {object|string} cookie - Your Tiktok cookie (optional) * @param {number} page - The page you want to search (optional) + * @param {string} proxy - Your Proxy (optional) * @returns {Promise} */ -export const SearchUser = (username: string, cookie?: any, page?: number): Promise => +export const SearchUser = (username: string, cookie?: any, page: number = 1, proxy?: string): Promise => new Promise(async (resolve) => { Axios(_tiktokSearchUserFull(_userSearchParams(username, 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 - } + }, + httpsAgent: (proxy && (proxy.startsWith("http") || proxy.startsWith("https") ? new HttpsProxyAgent(proxy) : proxy.startsWith("socks") ? new SocksProxyAgent(proxy) : undefined)) || undefined }) .then(({ data }) => { // Cookie Invalid