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}`); | |
}); |