diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ff03ee1 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,18 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "parserOptions": { + "ecmaVersion": 2020, + "project": ["tsconfig.json"] + }, + "rules": { + "no-empty": [2, { "allowEmptyCatch": true }], + "no-inner-declarations": [0, "both"], + "@typescript-eslint/no-unused-vars": 2, + "@typescript-eslint/consistent-type-definitions": [2, "type"] + }, + "env": { + "node": true + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index f4947dc..bac55b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules yarn-lock.json -package-lock.json \ No newline at end of file +package-lock.json +yarn.lock +lib \ No newline at end of file diff --git a/.npmignore b/.npmignore index aec30ce..6ae12fd 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,7 @@ node_modules yarn-lock.json package-lock.json -.gitignore \ No newline at end of file +.gitignore +tsconfig.json +.eslintrc +.prettierrc \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..772a35a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,16 @@ +{ + "arrowParens": "always", + "endOfLine": "auto", + "printWidth": 333, + "semi": false, + "tabWidth": 2, + "trailingComma": "none", + "overrides": [ + { + "files": ["./src/types/*.ts"], + "options": { + "printWidth": 150 + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 680eb61..7fe52a7 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ yarn add @tobyg74/tiktok-api-dl ### Tiktok Downloader -``` +```js const { TiktokDL } = require("@tobyg74/tiktok-api-dl"); const tiktok_url = "https://vt.tiktok.com/ZS84BnrU9" @@ -36,7 +36,7 @@ TiktokDL(tiktok_url) ### Tiktok Profile -``` +```js const { TiktokStalk } = require("@tobyg74/tiktok-api-dl"); const username = "tobz2k19" @@ -51,50 +51,58 @@ TiktokStalk(username) ### Tiktok Downloader -``` +```ts { - status: "success", - result: { - type: "video" // "image", - id: ..., - create_time: ..., - description: ..., + status: "success" | "error" + message?: string + result?: { + type: "video" | "image" + id: string + create_time: number + description: string author: { - ... - }, + username: string + nickname: string + signature: string + birthday: string + region: string + } statistics: { - ... - }, - video // images: [ - ... - ], - music: [ - ... - ] + play_count: number + download_count: number + share_count: number + comment_count: number + like_count: number + favourite_count: number + } + video?: string[] + images?: string[] + music: string[] } } ``` ### Tiktok Profile -``` +```ts { - status: "success", - result: { + status: "success" | "error" + message?: string + result?: { users: { - username: ..., - nickname: ..., - avatar: ..., - signature: ..., - verified: ..., - region: ... - }, + username: string + nickname: string + avatar: string + signature: string + verified: boolean + region: string + } stats: { - followerCount: ..., - followingCount: ..., - heartCount: ..., - videoCount: ..., - likeCount: ... + followerCount: number + followingCount: number + heartCount: number + videoCount: number + likeCount: number } } } diff --git a/index.js b/index.js deleted file mode 100644 index 1c84e49..0000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const { TiktokDL, TiktokStalk } = require("./utils"); - -module.exports = { TiktokDL, TiktokStalk }; diff --git a/package.json b/package.json index c863faf..6f3ee1d 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,12 @@ { "name": "@tobyg74/tiktok-api-dl", - "version": "1.0.2-fix", + "version": "1.0.3", "description": "Scrapper for download Video, Image, Music from Tiktok", - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc" }, "repository": { "type": "git", @@ -18,6 +20,7 @@ "tiktok" ], "author": "Tobz", + "contributors": ["aqulzz"], "license": "ISC", "bugs": { "url": "https://github.com/TobyG74/tiktok-api-dl/issues" @@ -26,5 +29,10 @@ "dependencies": { "axios": "^1.3.4", "cheerio": "^1.0.0-rc.12" + }, + "devDependencies": { + "@types/cheerio": "^0.22.31", + "@types/node": "^18.15.11", + "typescript": "^5.0.3" } } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3eeaeaa --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * from "./utils" diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..5742c0f --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,50 @@ +export type DLResult = { + status: "success" | "error" + message?: string + result?: { + type: "video" | "image" + id: string + create_time: number + description: string + author: { + username: string + nickname: string + signature: string + birthday: string + region: string + } + statistics: { + play_count: number + download_count: number + share_count: number + comment_count: number + like_count: number + favourite_count: number + } + video?: string[] + images?: string[] + music: string[] + } +} + +export type StalkResult = { + status: "success" | "error" + message?: string + result?: { + users: { + username: string + nickname: string + avatar: string + signature: string + verified: boolean + region: string + } + stats: { + followerCount: number + followingCount: number + heartCount: number + videoCount: number + likeCount: number + } + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..66d4457 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,130 @@ +import axios from "axios" +import { load } from "cheerio" +import { DLResult, StalkResult } from "../types" + +const _tiktokurl: string = "https://www.tiktok.com" +const _tiktokapi = (id: string): string => + `https://api16-va.tiktokv.com/aweme/v1/feed/?aweme_id=${id}&version_name=1.1.9&version_code=119&build_number=1.1.9&manifest_version_code=119&update_version_code=119&openudid=dlcrw3zg28ajm4ml&uuid=3703699664470627&_rticket=1677813932976&ts=1677813932&device_brand=Realme&device_type=RMX1821&device_platform=android&resolution=720*1370&dpi=320&os_version=11&os_api=30&carrier_region=US&sys_region=US%C2%AEion=US&app_name=TK%20Downloader&app_language=en&language=en&timezone_name=Western%20Indonesia%20Time&timezone_offset=25200&channel=googleplay&ac=wifi&mcc_mnc=&is_my_cn=0&aid=1180&ssmix=a&as=a1qwert123` + +export const TiktokDL = (url: string): Promise => + new Promise((resolve, reject) => { + url = url.replace("https://vm", "https://vt") + axios + .head(url) + .then(({ request }) => { + const { responseUrl } = request.res + let ID = responseUrl.match(/\d{17,21}/g) + if (ID === null) + return reject({ + status: "error", + message: "Failed to fetch tiktok url. Make sure your tiktok url is correct!" + }) + ID = ID[0] + axios + .get(_tiktokapi(ID)) + .then(({ data }) => { + const content = data.aweme_list.filter((v) => v.aweme_id === ID)[0] + if (!content) + return resolve({ + status: "error", + message: "Failed to find tiktok data. Make sure your tiktok url is correct!" + }) + const statistics = { + play_count: content.statistics.play_count, + download_count: content.statistics.download_count, + share_count: content.statistics.share_count, + comment_count: content.statistics.comment_count, + like_count: content.statistics.digg_count, + favourite_count: content.statistics.collect_count + } + const author = { + username: content.author.unique_id, + nickname: content.author.nickname, + signature: content.author.signature, + birthday: content.author.birthday, + region: content.author.region + } + if (content.image_post_info) { + resolve({ + status: "success", + result: { + type: "image", + id: content.aweme_id, + create_time: content.create_time, + description: content.desc, + author, + statistics, + images: content.image_post_info.images.map((v) => v.display_image.url_list[0]), + music: content.music.play_url.url_list + } + }) + } else { + resolve({ + status: "success", + result: { + type: "video", + id: content.aweme_id, + create_time: content.create_time, + description: content.desc, + author, + statistics, + video: content.video.play_addr.url_list, + music: content.music.play_url.url_list + } + }) + } + }) + .catch(reject) + }) + .catch(reject) + }) + +export const TiktokStalk = (username: string): Promise => + new Promise((resolve, reject) => { + axios + .get("https://pastebin.com/raw/ELJjcbZT") + .then(({ data: cookie }) => { + 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: cookie + } + }) + .then(({ data }) => { + const $ = load(data) + const result = JSON.parse($("script#SIGI_STATE").text()) + if (!result.UserModule) { + return resolve({ + status: "error", + message: "User not found!" + }) + } + const user = result.UserModule + const users = { + username: user.users[username].uniqueId, + nickname: user.users[username].nickname, + avatar: user.users[username].avatarLarger, + signature: user.users[username].signature, + verified: user.users[username].verified, + region: user.users[username].region + } + const 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 + } + resolve({ + status: "success", + result: { + users, + stats + } + }) + }) + }) + .catch((e) => resolve({ status: "error", message: e.message })) + }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b199dd2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "allowJs": false, + "checkJs": false, + "esModuleInterop": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2020", "ESNext.Array", "DOM"], + "module": "CommonJS", + "moduleResolution": "node", + "noImplicitThis": true, + "outDir": "lib", + "removeComments": true, + "resolveJsonModule": true, + "rootDir": "src", + "skipLibCheck": true, + "declaration": true, + "target": "ES2020" + }, + "exclude": ["node_modules"], + "include": ["./src/**/*.ts"] +} \ No newline at end of file diff --git a/utils/index.js b/utils/index.js deleted file mode 100644 index aea6518..0000000 --- a/utils/index.js +++ /dev/null @@ -1,139 +0,0 @@ -const Axios = require("axios"); -const cheerio = require("cheerio"); - -const _tiktokurl = "https://www.tiktok.com"; -const _tiktokapi = (id) => - `https://api16-va.tiktokv.com/aweme/v1/feed/?aweme_id=${id}&version_name=1.1.9&version_code=119&build_number=1.1.9&manifest_version_code=119&update_version_code=119&openudid=dlcrw3zg28ajm4ml&uuid=3703699664470627&_rticket=1677813932976&ts=1677813932&device_brand=Realme&device_type=RMX1821&device_platform=android&resolution=720*1370&dpi=320&os_version=11&os_api=30&carrier_region=US&sys_region=US%C2%AEion=US&app_name=TK%20Downloader&app_language=en&language=en&timezone_name=Western%20Indonesia%20Time&timezone_offset=25200&channel=googleplay&ac=wifi&mcc_mnc=&is_my_cn=0&aid=1180&ssmix=a&as=a1qwert123`; - -const TiktokDL = (url) => { - return new Promise((resolve, reject) => { - url = url.replace("https://vm", "https://vt"); - Axios(url, { - method: "HEAD", - }) - .then(({ request }) => { - const { responseUrl } = request.res; - let ID = responseUrl.match(/\d{17,21}/g); - if (ID === null) - return reject({ - status: "error", - message: - "Failed to fetch tiktok url. Make sure your tiktok url is correct!", - }); - ID = ID[0]; - Axios(_tiktokapi(ID), { - method: "GET", - }) - .then(({ data }) => { - const content = data.aweme_list.filter((v) => v.aweme_id === ID)[0]; - if (!content) - return resolve({ - status: "error", - message: - "Failed to find tiktok data. Make sure your tiktok url is correct!", - }); - const statistics = { - play_count: content.statistics.play_count, - download_count: content.statistics.download_count, - share_count: content.statistics.share_count, - comment_count: content.statistics.comment_count, - like_count: content.statistics.digg_count, - favourite_count: content.statistics.collect_count, - }; - const author = { - username: content.author.unique_id, - nickname: content.author.nickname, - signature: content.author.signature, - birthday: content.author.birthday, - region: content.author.region, - }; - if (content.image_post_info) { - resolve({ - status: "success", - result: { - type: "image", - id: content.aweme_id, - create_time: content.create_time, - description: content.desc, - author, - statistics, - images: content.image_post_info.images.map( - (v) => v.display_image.url_list[0] - ), - music: content.music.play_url.url_list, - }, - }); - } else { - resolve({ - status: "success", - result: { - type: "video", - id: content.aweme_id, - create_time: content.create_time, - description: content.desc, - author, - statistics, - video: content.video.play_addr.url_list, - music: content.music.play_url.url_list, - }, - }); - } - }) - .catch(reject); - }) - .catch(reject); - }); -}; - -const TiktokStalk = (username) => { - return new Promise(async (resolve, reject) => { - const { data: cookie } = await Axios.get("https://pastebin.com/raw/ELJjcbZT"); - username = username.replace("@", ""); - Axios(`${_tiktokurl}/@${username}`, { - 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", - cookie: cookie, - }, - }) - .then(({ data }) => { - const $ = cheerio.load(data); - const result = JSON.parse($("script#SIGI_STATE").text()); - if (!result.UserModule) { - return resolve({ - status: "error", - message: "User not found!", - }); - } - const user = result.UserModule; - const users = { - username: user.users[username].uniqueId, - nickname: user.users[username].nickname, - avatar: user.users[username].avatarLarger, - signature: user.users[username].signature, - verified: user.users[username].verified, - region: user.users[username].region, - }; - const 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, - }; - resolve({ - status: "success", - result: { - users, - stats, - }, - }); - }) - .catch((e) => { - resolve({ status: "error", message: e.message }); - }); - }); -}; - -module.exports = { TiktokDL, TiktokStalk };