UNPKG

@prevter/tiktok-scraper

Version:

Library for downloading videos from TikTok (without watermark)

175 lines (174 loc) 7.13 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchVideo = exports.detectVideoId = exports.getVideoId = exports.getFullURL = void 0; const https_1 = __importDefault(require("https")); const API_BASE_URL = 'https://api16-normal-v4.tiktokv.com'; const headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', }; /** * Get data from a URL * @param url URL to get data from * @returns Promise containing the data from the URL */ const get = (url) => { return new Promise((resolve, reject) => { https_1.default .get(url, { headers }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => resolve(data)); }) .on('error', reject); }); }; /** * Get a buffer from a URL * @param url URL to get buffer from * @returns Promise containing the buffer from the URL */ const getBuffer = (url, progress) => { return new Promise((resolve, reject) => { https_1.default .get(url, { headers }, (res) => { let data = Buffer.from([]); res.on('data', (chunk) => { data = Buffer.concat([data, chunk]); if (!progress) return; const downloaded = data.length; const total = parseInt(res.headers['content-length'] || '0'); progress({ downloaded, total, progress: (downloaded / total) * 100, }); }); res.on('end', () => resolve(data)); }) .on('error', reject); }); }; /** * Loads a TikTok video page from short URL to get the full URL * @param url Short TikTok video URL (e.g. https://vm.tiktok.com/...) * @returns Promise containing the full TikTok video URL */ const getFullURL = (url) => __awaiter(void 0, void 0, void 0, function* () { var match = url.match(/(vm|vt)\.tiktok\.com\/(.*)/); if (!match) match = url.match(/(www|vm|vt)\.tiktok\.com\/t\/(.*)/); if (!match) throw new Error(`Unknown TikTok video URL: ${url}`); // follow the redirect to get the full URL return new Promise((resolve, reject) => { https_1.default .get(url, { headers }, (res) => { if (res.headers.location) { resolve(res.headers.location); } else { reject('No redirect found'); } }) .on('error', reject); }); }); exports.getFullURL = getFullURL; /** * Get the video ID from a TikTok video URL (only full URL) * @param url TikTok video URL * @returns Video ID */ const getVideoId = (url) => { const regex = /\/video\/(\d*)/; const match = url.match(regex); if (match) return match[1]; throw new Error(`Invalid TikTok video URL: ${url}`); }; exports.getVideoId = getVideoId; /** * Automatically deduce the video ID from a TikTok video URL * @param url Any TikTok video URL (full or short) * @returns Promise containing the video ID */ const detectVideoId = (url) => __awaiter(void 0, void 0, void 0, function* () { if (url.match(/(vm|vt)\.tiktok\.com\/(.*)/) || url.match(/(vm|vt|www)\.tiktok\.com\/t\/(.*)/)) { url = yield (0, exports.getFullURL)(url); } return (0, exports.getVideoId)(url); }); exports.detectVideoId = detectVideoId; /** * Fetches a TikTok video data from a video ID or URL * @param video TikTok video ID or URL * @returns Promise containing the TikTok video data (see {@link TikTokVideo} interface for more details) */ const fetchVideo = (video) => __awaiter(void 0, void 0, void 0, function* () { video = video.trim(); const video_id = video.match(/^\d*$/) ? video : yield (0, exports.detectVideoId)(video); const url = `${API_BASE_URL}/aweme/v1/feed/?aweme_id=${video_id}`; const data = yield get(url); const json = JSON.parse(data); const video_data = json.aweme_list[0]; const videoWatermark = { uri: video_data.video.download_addr.uri, url: video_data.video.download_addr.url_list[0], width: video_data.video.download_addr.width, height: video_data.video.download_addr.height, dataSize: video_data.video.download_addr.data_size, download: (progress) => __awaiter(void 0, void 0, void 0, function* () { return yield getBuffer(videoWatermark.url, progress); }), }; const videoNoWatermark = { uri: video_data.video.play_addr.uri, url: video_data.video.play_addr.url_list[0], width: video_data.video.play_addr.width, height: video_data.video.play_addr.height, dataSize: video_data.video.play_addr.data_size, download: (progress) => __awaiter(void 0, void 0, void 0, function* () { return yield getBuffer(videoNoWatermark.url, progress); }), }; const music = { id: video_data.music.id, name: video_data.music.title, author: video_data.music.author, url: video_data.music.play_url.url_list[0], download: (progress) => __awaiter(void 0, void 0, void 0, function* () { return yield getBuffer(music.url, progress); }), }; return { id: video_data.aweme_id, url: `https://www.tiktok.com/@${video_data.author.nickname}/video/${video_data.aweme_id}`, description: video_data.desc, author: video_data.author.nickname, videoWatermark, videoNoWatermark, width: video_data.video.width, height: video_data.video.height, likes: video_data.statistics.digg_count, shares: video_data.statistics.share_count, playCount: video_data.statistics.play_count, comments: video_data.statistics.comment_count, music, previewImageUrl: video_data.video.origin_cover.url_list[0], download: (options) => __awaiter(void 0, void 0, void 0, function* () { options = options || {}; const video = options.watermark ? videoWatermark : videoNoWatermark; return yield video.download(options.progress); }), }; }); exports.fetchVideo = fetchVideo;