KitsuLabs
.
Edit Snippet
Perbarui kode atau detail snippet ini.
Filename / Title
Language
JavaScript
Python
HTML
CSS
JSON
SQL
Bash
TypeScript
PHP
Java
Go
Rust
Description
(Optional)
Tiktok video and image slide downloader with complete metadata.
Tags
Code Content
*
/*** @ Base: https://www.tiktok.com/ @ Author: Shannz @ Note: Tiktok video and image slide downloader with complete metadata. ***/ import axios from 'axios'; import * as cheerio from 'cheerio'; import { CookieJar } from 'tough-cookie'; import { wrapper } from 'axios-cookiejar-support'; import { uploadFile } from 'cloudsky-storage'; async function getBuffer(url, apiInstance) { try { const response = await apiInstance.get(url, { responseType: 'arraybuffer', headers: { 'Referer': 'https://www.tiktok.com/', 'Range': 'bytes=0-' } }); return Buffer.from(response.data); } catch (e) { console.error(`[Download Error]:`, e.message); return null; } } export const tiktok = { video: async (url) => { const jar = new CookieJar(); const api = axios.create({ jar: jar, withCredentials: true, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', } }); wrapper(api); try { console.log(`Downloading Video...`); const htmlResponse = await api.get(url); const $ = cheerio.load(htmlResponse.data); let scriptContent = $('#__UNIVERSAL_DATA_FOR_REHYDRATION__').html() || $('#SIGI_STATE').html(); if (!scriptContent) throw new Error('Script tag data tidak ditemukan (Captcha/IP Blocked).'); const jsonData = JSON.parse(scriptContent); const defaultScope = jsonData?.__DEFAULT_SCOPE__; const itemStruct = defaultScope?.["webapp.video-detail"]?.itemInfo?.itemStruct || Object.values(jsonData.ItemModule || {})[0]; if (!itemStruct) throw new Error('Struct video tidak ditemukan dalam JSON.'); const videoId = itemStruct.id; const videoData = itemStruct.video; const watermarkUrl = videoData.downloadAddr || videoData.playAddr; let hdNoWatermarkUrl = null; let bitrateLabel = 0; let qualityLabel = 'Original'; if (videoData.bitrateInfo && Array.isArray(videoData.bitrateInfo)) { const bestQuality = videoData.bitrateInfo.sort((a, b) => b.Bitrate - a.Bitrate)[0]; if (bestQuality) { bitrateLabel = bestQuality.Bitrate; qualityLabel = bestQuality.QualityType; const urlList = bestQuality.PlayAddr?.UrlList || []; hdNoWatermarkUrl = urlList.find(u => u.includes('aweme/v1/play')) || urlList[urlList.length - 1]; } } if (!hdNoWatermarkUrl) hdNoWatermarkUrl = videoData.playAddr; const finalResult = { metadata: { id: videoId, description: itemStruct.desc, createTime: new Date(itemStruct.createTime * 1000).toLocaleString(), region: itemStruct.locationCreated || 'N/A', hashtags: itemStruct.challenges?.map(tag => ({ id: tag.id, name: tag.title })) || [] }, originalUrl: { watermark: watermarkUrl, hd_nonwatermark: hdNoWatermarkUrl }, cloudUrl: { watermark: null, hd_nonwatermark: null }, videoInfo: { duration: videoData?.duration, resolution: `${videoData?.width}x${videoData?.height}`, format: videoData?.format, codec: videoData?.codecType, bitrate: bitrateLabel, quality: qualityLabel, cover: { static: videoData?.cover, dynamic: videoData?.dynamicCover, origin: videoData?.originCover } }, author: { id: itemStruct.author?.id, uniqueId: itemStruct.author?.uniqueId, nickname: itemStruct.author?.nickname, signature: itemStruct.author?.signature, avatar: itemStruct.author?.avatarLarger || itemStruct.author?.avatarThumb, verified: itemStruct.author?.verified }, music: { id: itemStruct.music?.id, title: itemStruct.music?.title, author: itemStruct.music?.authorName, cover: itemStruct.music?.coverLarge, playUrl: itemStruct.music?.playUrl, isOriginal: itemStruct.music?.original }, stats: { views: itemStruct.statsV2?.playCount || itemStruct.stats?.playCount, likes: itemStruct.statsV2?.diggCount || itemStruct.stats?.diggCount, comments: itemStruct.statsV2?.commentCount || itemStruct.stats?.commentCount, shares: itemStruct.statsV2?.shareCount || itemStruct.stats?.shareCount, saves: itemStruct.statsV2?.collectCount || itemStruct.stats?.collectCount } }; if (watermarkUrl) { const wmBuffer = await getBuffer(watermarkUrl, api); if (wmBuffer) { try { const upload = await uploadFile(wmBuffer, `tiktok_wm_${videoId}.mp4`); if (upload.success) { finalResult.cloudUrl.watermark = upload.data.url; } } catch (e) { console.error(`[Upload WM Error]:`, e.message); } } } if (hdNoWatermarkUrl) { const hdBuffer = await getBuffer(hdNoWatermarkUrl, api); if (hdBuffer) { try { const upload = await uploadFile(hdBuffer, `tiktok_hd_${videoId}.mp4`); if (upload.success) { finalResult.cloudUrl.hd_nonwatermark = upload.data.url; } } catch (e) { console.error(`[Upload HD Error]:`, e.message); } } } return { status: true, result: finalResult }; } catch (error) { console.error('Error Main Process:', error.message); return { status: false, error: error.message }; } }, slide: async (url) => { const jar = new CookieJar(); const api = axios.create({ jar: jar, withCredentials: true, headers: { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', } }); wrapper(api); try { console.log(`Downloading Image Slide...`); const htmlResponse = await api.get(url); const $ = cheerio.load(htmlResponse.data); let scriptContent = $('#__UNIVERSAL_DATA_FOR_REHYDRATION__').html() || $('#SIGI_STATE').html(); if (!scriptContent) throw new Error('Script tag data tidak ditemukan (Captcha/IP Blocked).'); const jsonData = JSON.parse(scriptContent); const defaultScope = jsonData?.__DEFAULT_SCOPE__; const itemStruct = defaultScope?.["webapp.reflow.video.detail"]?.itemInfo?.itemStruct || defaultScope?.["webapp.video-detail"]?.itemInfo?.itemStruct; if (!itemStruct) throw new Error('Struct video tidak ditemukan dalam JSON.'); if (!itemStruct.imagePost) throw new Error('Konten ini bukan berupa image slide.'); const videoId = itemStruct.id; const imagesList = itemStruct.imagePost.images.map(img => img.imageURL.urlList[0]); const audioUrl = itemStruct.music?.playUrl; const finalResult = { metadata: { id: videoId, type: 'image_slide', description: itemStruct.desc, createTime: new Date(itemStruct.createTime * 1000).toLocaleString(), region: itemStruct.locationCreated || 'N/A', hashtags: itemStruct.challenges?.map(tag => ({ id: tag.id, name: tag.title })) || [] }, originalUrl: { images: imagesList, audio: audioUrl }, author: { id: itemStruct.author?.id, uniqueId: itemStruct.author?.uniqueId, nickname: itemStruct.author?.nickname, signature: itemStruct.author?.signature, avatar: itemStruct.author?.avatarLarger || itemStruct.author?.avatarThumb, verified: itemStruct.author?.verified }, music: { id: itemStruct.music?.id, title: itemStruct.music?.title, author: itemStruct.music?.authorName, cover: itemStruct.music?.coverLarge, playUrl: audioUrl, isOriginal: itemStruct.music?.original }, stats: { views: itemStruct.statsV2?.playCount || itemStruct.stats?.playCount, likes: itemStruct.statsV2?.diggCount || itemStruct.stats?.diggCount, comments: itemStruct.statsV2?.commentCount || itemStruct.stats?.commentCount, shares: itemStruct.statsV2?.shareCount || itemStruct.stats?.shareCount, saves: itemStruct.statsV2?.collectCount || itemStruct.stats?.collectCount } }; return { status: true, result: finalResult }; } catch (error) { console.error('Error Slide Process:', error.message); return { status: false, error: error.message }; } } } //tiktok.slide('https://vm.tiktok.com/ZP8bbmHn6/').then(a => console.log(JSON.stringify(a, null, 2))) //tiktok.video('https://vm.tiktok.com/ZP8bbXdyf/').then(a => console.log(JSON.stringify(a, null, 2)))
Editor is always dark mode for better readability.
Admin Access
Update Snippet
Cancel