Spaces:
Runtime error
Runtime error
| const { Telegraf } = require('telegraf'); | |
| const fetch = require('node-fetch'); | |
| const fs = require('fs'); | |
| const axios = require('axios'); | |
| const instagramDl = require('@sasmeee/igdl'); | |
| const { Headers } = fetch; | |
| const express = require('express'); | |
| const app = express(); | |
| const port = 7860; | |
| const botToken = '6773241108:AAHz1TCLqpjR880ZLYHdaqJsL9Vxoqcr7jo'; // Replace with your Telegram bot token | |
| const bot = new Telegraf(botToken); | |
| const headers = new Headers(); | |
| const getRedirectUrl = async (url) => { | |
| if (url.includes("vm.tiktok.com") || url.includes("vt.tiktok.com")) { | |
| url = await fetch(url, { | |
| redirect: "follow", | |
| follow: 10, | |
| }); | |
| url = url.url; | |
| console.log("[*] Redirecting to: " + url); | |
| } | |
| return url; | |
| }; | |
| const getIdVideo = async (url) => { | |
| if (url.includes("/t/")) { | |
| url = await new Promise((resolve) => { | |
| require("follow-redirects").https.get(url, function (res) { | |
| return resolve(res.responseUrl); | |
| }); | |
| }); | |
| } | |
| const matching = url.includes("/video/"); | |
| const matchingPhoto = url.includes("/photo/"); | |
| let idVideo = url.substring( | |
| url.indexOf("/video/") + 7, | |
| url.indexOf("/video/") + 26 | |
| ); | |
| if (matchingPhoto) | |
| idVideo = url.substring( | |
| url.indexOf("/photo/") + 7, | |
| url.indexOf("/photo/") + 26 | |
| ); | |
| else if (!matching) { | |
| throw new Error("URL not found"); | |
| } | |
| return idVideo.length > 19 | |
| ? idVideo.substring(0, idVideo.indexOf("?")) | |
| : idVideo; | |
| }; | |
| const getVideo = async (url, watermark) => { | |
| const idVideo = await getIdVideo(url); | |
| const API_URL = `https://api22-normal-c-alisg.tiktokv.com/aweme/v1/feed/?aweme_id=${idVideo}&iid=7318518857994389254&device_id=7318517321748022790&channel=googleplay&app_name=musical_ly&version_code=300904&device_platform=android&device_type=ASUS_Z01QD&version=9`; | |
| const request = await fetch(API_URL, { | |
| method: "OPTIONS", | |
| headers: headers, | |
| }); | |
| const body = await request.text(); | |
| try { | |
| var res = JSON.parse(body); | |
| } catch (err) { | |
| console.error("Error:", err); | |
| console.error("Response body:", body); | |
| throw err; | |
| } | |
| if (res.aweme_list[0].aweme_id != idVideo) { | |
| return null; | |
| } | |
| let urlMedia = ""; | |
| let image_urls = []; | |
| if (!!res.aweme_list[0].image_post_info) { | |
| console.log("[*] Video is slideshow"); | |
| res.aweme_list[0].image_post_info.images.forEach((element) => { | |
| image_urls.push(element.display_image.url_list[1]); | |
| }); | |
| } else { | |
| urlMedia = watermark | |
| ? res.aweme_list[0].video.download_addr.url_list[0] | |
| : res.aweme_list[0].video.play_addr.url_list[0]; | |
| } | |
| return { | |
| url: urlMedia, | |
| images: image_urls, | |
| id: idVideo, | |
| }; | |
| }; | |
| const downloadMedia = async (item) => { | |
| const folder = "downloads/"; | |
| if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true }); | |
| if (item.images.length != 0) { | |
| console.log("[*] Downloading Slideshow"); | |
| let index = 0; | |
| for (const image_url of item.images) { | |
| const fileName = `${item.id}_${index}.jpeg`; | |
| if (fs.existsSync(folder + fileName)) { | |
| console.log(`[!] File '${fileName}' already exists. Skipping`); | |
| continue; | |
| } | |
| index++; | |
| const response = await fetch(image_url); | |
| const buffer = await response.buffer(); | |
| fs.writeFileSync(folder + fileName, buffer); | |
| } | |
| } else { | |
| const fileName = `${item.id}.mp4`; | |
| if (fs.existsSync(folder + fileName)) { | |
| console.log(`[!] File '${fileName}' already exists. Skipping`); | |
| return; | |
| } | |
| const response = await fetch(item.url); | |
| const buffer = await response.buffer(); | |
| fs.writeFileSync(folder + fileName, buffer); | |
| } | |
| }; | |
| bot.start((ctx) => ctx.reply('My love! send me a TikTok or Instagram video link baby 😘')); | |
| bot.on('text', async (ctx) => { | |
| const url = ctx.message.text; | |
| if (!url.includes('instagram.com') && !url.includes('tiktok.com')) { | |
| return ctx.reply('Baby, the url is not correct...'); | |
| } | |
| try { | |
| const processingMessage = await ctx.reply('🤗Honey, wait a bit my love. Processing the URL...'); | |
| let data; | |
| if (url.includes('instagram.com')) { | |
| const dataList = await instagramDl(url); | |
| const downloadLink = dataList[0].download_link; | |
| const response = await axios.get(downloadLink, { responseType: 'stream' }); | |
| await ctx.editMessageText('Uploading the video...my baby ><', { message_id: processingMessage.message_id }); | |
| const filePath = `video_${Date.now()}.mp4`; | |
| const writer = fs.createWriteStream(filePath); | |
| response.data.pipe(writer); | |
| writer.on('finish', () => { | |
| ctx.replyWithVideo({ source: filePath }) | |
| .then(() => { | |
| fs.unlinkSync(filePath); // Delete the file after sending | |
| }) | |
| .catch((error) => { | |
| console.error('Error sending video:', error); | |
| ctx.reply('Failed to send the video.'); | |
| }); | |
| }); | |
| writer.on('error', (error) => { | |
| console.error('Error downloading video:', error); | |
| ctx.reply('Failed to download the video.'); | |
| }); | |
| } else if (url.includes('tiktok.com')) { | |
| const processingMessage = await ctx.reply('🤗Honey, wait a bit my love. Processing the URL...'); | |
| const resolvedUrl = await getRedirectUrl(url); | |
| data = await getVideo(resolvedUrl, false); | |
| if (data == null) { | |
| return ctx.reply('Video not found or has been deleted.'); | |
| } | |
| await downloadMedia(data); | |
| if (data.images.length > 0) { | |
| data.images.forEach((image, index) => { | |
| ctx.replyWithPhoto({ url: image }); | |
| }); | |
| } else { | |
| const filePath = `downloads/${data.id}.mp4`; | |
| await ctx.editMessageText('Uploading the video...my baby ><', { message_id: processingMessage.message_id }); | |
| ctx.replyWithVideo({ source: filePath }, { caption: 'Downloaded TikTok Video' }) | |
| .then(() => { | |
| fs.unlinkSync(filePath); // Delete the file after sending | |
| }) | |
| .catch((error) => { | |
| console.error('Error sending video:', error); | |
| ctx.reply('Failed to send the video.'); | |
| }); | |
| } | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| ctx.reply('Failed to process the URL.'); | |
| } | |
| }); | |
| bot.launch(); | |
| console.log('Bot is running...'); | |
| app.get('/download', async (req, res) => { | |
| const url = req.query.url; | |
| const watermark = req.query.watermark === 'true'; | |
| if (!url) { | |
| return res.status(400).json({ error: 'URL parameter is required' }); | |
| } | |
| try { | |
| const resolvedUrl = await getRedirectUrl(url); | |
| const data = await getVideo(resolvedUrl, watermark); | |
| if (data == null) { | |
| return res.status(404).json({ error: 'Video not found or has been deleted' }); | |
| } | |
| await downloadMedia(data); | |
| if (data.images.length > 0) { | |
| res.json({ | |
| message: 'Slideshow downloaded successfully', | |
| files: data.images.map((_, index) => `${data.id}_${index}.jpeg`) | |
| }); | |
| } else { | |
| const filePath = `downloads/${data.id}.mp4`; | |
| res.download(filePath, (err) => { | |
| if (err) { | |
| console.error('Error sending file:', err); | |
| res.status(500).json({ error: 'Error sending file' }); | |
| } | |
| // Optionally delete the file after sending | |
| // fs.unlinkSync(filePath); | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| res.status(500).json({ error: 'Internal server error' }); | |
| } | |
| }); | |
| app.listen(port, () => { | |
| console.log(`TikTok Downloader API listening at http://localhost:${port}`); | |
| }); |