| import express from 'express'; | |
| import { chromium } from 'playwright'; | |
| import cors from 'cors'; | |
| import axios from 'axios'; | |
| import fetch from 'node-fetch'; | |
| import * as cheerio from 'cheerio'; | |
| import fakeUa from 'fake-useragent'; | |
| import { FormData } from 'formdata-node'; | |
| import dotenv from 'dotenv'; | |
| import os from 'os'; | |
| import { io } from "socket.io-client"; | |
| import ytSearch from 'yt-search'; | |
| import pkg from 'fast-levenshtein'; | |
| const { get: levenshtein } = pkg; | |
| dotenv.config(); | |
| const app = express(); | |
| app.use(express.json()); | |
| app.use(cors()); | |
| app.get('/', async (req, res) => { | |
| return res.status(200).json({ success: true, message: 'DOWNLOADER' }); | |
| }); | |
| app.get('/mediafire', async (req, res) => { | |
| const { url } = req.query; | |
| if (!url) { | |
| return res.status(400).json({ success: false, message: 'URL is required' }); | |
| } | |
| try { | |
| const downloadInfo = await mediafire(url); | |
| return res.json(downloadInfo); | |
| } catch (error) { | |
| console.error('Error:', error); | |
| return res.status(500).json({ success: false, message: error.message }); | |
| } | |
| }); | |
| async function mediafire(url) { | |
| const browser = await chromium.launch({ headless: true }); | |
| const context = await browser.newContext({ | |
| userAgent: 'Mozilla/5.0 (Linux; Android 6.0; iris50) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36', | |
| }); | |
| const page = await context.newPage(); | |
| try { | |
| await page.goto(url); | |
| let downloadInfo = await page.evaluate(() => { | |
| const fileNameElement = document.querySelector('.dl-btn-label'); | |
| const fileName = fileNameElement ? fileNameElement.textContent.trim() : ''; | |
| const downloadLinkElement = document.querySelector('#downloadButton'); | |
| const downloadLink = downloadLinkElement ? downloadLinkElement.href : ''; | |
| const fileSizeText = downloadLinkElement ? downloadLinkElement.textContent : ''; | |
| const sizeMatch = fileSizeText.match(/\(([^)]+)\)/); | |
| const fileSize = sizeMatch ? sizeMatch[1] : ''; | |
| const metaTags = Array.from(document.querySelectorAll('meta')).reduce((acc, meta) => { | |
| const name = meta.getAttribute('name') || meta.getAttribute('property'); | |
| const content = meta.getAttribute('content'); | |
| if (name && content) acc[name.split(':')[1]] = content; | |
| return acc; | |
| }, {}); | |
| return { | |
| fileName, | |
| downloadLink, | |
| fileSize, | |
| meta: metaTags, | |
| }; | |
| }); | |
| if (!downloadInfo.downloadLink.startsWith('https://down')) { | |
| await browser.close(); | |
| const newBrowser = await chromium.launch({ headless: true }); | |
| const newContext = await newBrowser.newContext({ | |
| userAgent: 'Mozilla/5.0 (Linux; Android 6.0; iris50) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36', | |
| }); | |
| const newPage = await newContext.newPage(); | |
| await newPage.goto(downloadInfo.downloadLink); | |
| const updatedInfo = await newPage.evaluate(() => { | |
| const downloadLink = document.querySelector('#downloadButton')?.href || ''; | |
| return { downloadLink }; | |
| }); | |
| downloadInfo.downloadLink = updatedInfo.downloadLink; | |
| await newBrowser.close(); | |
| } | |
| return downloadInfo; | |
| } catch (error) { | |
| console.error('Error:', error.message); | |
| return { success: false, message: error.message }; | |
| } finally { | |
| if (browser) { | |
| await browser.close(); | |
| } | |
| } | |
| } | |
| app.get('/ytdl', async (req, res) => { | |
| const id = req.query.id; | |
| if (!id) { | |
| return res.status(400).send('Parameter "id" is required.'); | |
| } | |
| try { | |
| const response = await fetch(`https://api.allorigins.win/raw?url=https://ytdlp.online/stream?command=https://www.youtube.com/watch?v=${id} --get-url`, { | |
| timeout: 1000, | |
| cache: 'no-store' | |
| }); | |
| if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); | |
| const responseText = await response.text(); | |
| const urls = responseText.split('\n') | |
| .filter(line => line.trim().startsWith('data:')) | |
| .map(line => line.substring(5).trim()) | |
| .filter(url => url.startsWith('http')); | |
| res.json({ data: urls }); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).send('Something went wrong while processing the request.'); | |
| } | |
| }); | |
| app.get('/ytdl/v1', async (req, res) => { | |
| const id = req.query.id; | |
| if (!id) { | |
| return res.status(400).json({ error: 'Parameter "id" is required.' }); | |
| } | |
| try { | |
| const response = await fetch( | |
| `https://api.allorigins.win/raw?url=https://ytdlp.online/stream?command=https://www.youtube.com/watch?v=${id} -j`, | |
| { | |
| timeout: 1000, | |
| cache: 'no-store' | |
| } | |
| ); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! Status: ${response.status}`); | |
| } | |
| const responseText = await response.text(); | |
| const parsedData = responseText | |
| .split('\n') | |
| .map(line => line.trim().substring(5).trim()) | |
| .filter(line => { | |
| try { | |
| JSON.parse(line); | |
| return true; | |
| } catch { | |
| return false; | |
| } | |
| }) | |
| .map(line => JSON.parse(line)); | |
| if (parsedData.length === 0) { | |
| return res.status(404).json({ error: 'No valid data found.' }); | |
| } | |
| res.json({ data: parsedData }); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).json({ error: 'Something went wrong while processing the request.' }); | |
| } | |
| }); | |
| app.get('/ytdl/v2', async (req, res) => { | |
| const id = req.query.id; | |
| if (!id) { | |
| return res.status(400).json({ error: 'Parameter "id" is required.' }); | |
| } | |
| try { | |
| const response = await fetch( | |
| `https://api.allorigins.win/raw?url=https://ytdlp.online/stream?command=https://www.youtube.com/watch?v=${id}`, | |
| { | |
| timeout: 1000, | |
| cache: 'no-store' | |
| } | |
| ); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! Status: ${response.status}`); | |
| } | |
| const responseText = await response.text(); | |
| const lastLine = responseText | |
| .split('\n') | |
| .map(line => line.trim().substring(5).trim()) | |
| .filter(Boolean); | |
| const extractDownloadLinks = (data) => { | |
| return [...new Set(data.flatMap(item => { | |
| const $ = cheerio.load(item); | |
| return $("a[href^='/download/']").map((_, el) => "https://ytdlp.online" + $(el).attr("href")).get(); | |
| }))]; | |
| }; | |
| if (!lastLine) return res.status(404).json({ error: 'No valid data found.' }); | |
| res.json({ data: extractDownloadLinks(lastLine)[0] }); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).json({ error: 'Something went wrong while processing the request.' }); | |
| } | |
| }); | |
| app.get('/ytdl/search', async (req, res) => { | |
| const { query, limit = 5 } = req.query; | |
| if (!query) return res.status(400).json({ error: 'Parameter "query" is required.' }); | |
| try { | |
| const result = await ytSearch(query); | |
| if (!result.videos || result.videos.length === 0) { | |
| return res.status(404).json({ error: 'No results found.' }); | |
| } | |
| const cos = levenshtein; | |
| const videosWithSimilarity = result.videos.map(video => { | |
| const videoId = video.url.split('v=')[1]; | |
| return { | |
| id: videoId, | |
| title: video.title, | |
| url: video.url, | |
| duration: video.timestamp, | |
| views: video.views, | |
| uploaded: video.ago, | |
| author: video.author.name, | |
| similarity: 1 - (cos(query, video.title) / Math.max(query.length, video.title.length)) | |
| }; | |
| }); | |
| const sortedVideos = videosWithSimilarity.sort((a, b) => b.similarity - a.similarity).sort((a, b) => b.views - a.views); | |
| const topVideos = sortedVideos.slice(0, parseInt(limit)); | |
| res.json({ query, limit: parseInt(limit), videos: topVideos }); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).json({ error: 'Failed to fetch YouTube search results.' }); | |
| } | |
| }); | |
| app.get('/ace', async (req, res) => { | |
| const id = req.query.id; | |
| if (!id) { | |
| return res.status(400).send('Parameter "id" is required.'); | |
| } | |
| try { | |
| const apiUrl = `https://www.acethinker.com/downloader/api/video_info.php?url=https://www.youtube.com/watch?v=${id}&israpid=1&ismp3=0`; | |
| const response = await fetch(apiUrl); | |
| if (!response.ok) throw new Error('AceThinker API request failed'); | |
| const processedData = await response.json(); | |
| res.json(processedData); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).send('Something went wrong while processing the request.'); | |
| } | |
| }); | |
| const genSpinner = () => Math.random().toString(36).substring(2, 10); | |
| app.get("/y232", async (req, res) => { | |
| const id = req.query.id; | |
| if (!id) { | |
| return res.status(400).send('Parameter "id" is required.'); | |
| } | |
| try { | |
| const spinnerid = genSpinner(); | |
| const socket = io("https://api.y232.live"); | |
| const data = { url: `https://www.youtube.com/watch?v=${id}`, spinnerid, method: "streams" }; | |
| socket.emit("getInfoEvent", data); | |
| socket.on("done", (response) => { | |
| res.status(200).send(response); | |
| socket.close(); | |
| }); | |
| socket.on("error", (err) => { | |
| res.status(500).send({ success: false, error: err.message }); | |
| socket.close(); | |
| }); | |
| } catch (error) { | |
| console.error(error); | |
| res.status(500).send('Something went wrong while processing the request.'); | |
| } | |
| }); | |
| class Luluvdo { | |
| async download(url, output = 'json') { | |
| try { | |
| console.log(`[LOG] Memulai proses untuk URL: ${url}`); | |
| const idMatch = url.match(/(?:\/[de])\/([a-zA-Z0-9_-]+)/); | |
| const id = idMatch?.[1]; | |
| if (!id) throw new Error('Invalid URL: ID not found'); | |
| const client = axios.create({ | |
| headers: { 'User-Agent': fakeUa() }, | |
| withCredentials: true, | |
| }); | |
| console.log(`[LOG] Mengambil form dari halaman: https://luluvdo.com/d/${id}_h`); | |
| let formResult; | |
| do { | |
| const { data: pageData } = await client.get(`https://luluvdo.com/d/${id}_h`); | |
| const $ = cheerio.load(pageData); | |
| formResult = new FormData(); | |
| $('form#F1 input').each((_, el) => { | |
| const name = $(el).attr('name'); | |
| const value = $(el).val(); | |
| if (name && value) formResult.append(name, value); | |
| }); | |
| console.log(`[LOG] Form yang diambil: ${JSON.stringify(formResult, null, 2)}`); | |
| if (!formResult.has('hash')) { | |
| console.log('[LOG] Form tidak valid, mencoba lagi...'); | |
| await new Promise(resolve => setTimeout(resolve, 2000)); | |
| } | |
| } while (!formResult.has('hash')); | |
| console.log('[LOG] Form berhasil diambil, mengirimkan permintaan untuk mendapatkan link unduhan'); | |
| let result; | |
| do { | |
| const { data: postData } = await client.post(`https://luluvdo.com/d/${id}_h`, formResult); | |
| const $$ = cheerio.load(postData); | |
| result = { | |
| size: $$('table tr:nth-child(1) td:nth-child(2)').text().trim() || 'N/A', | |
| bytes: $$('table tr:nth-child(2) td:nth-child(2)').text().trim() || 'N/A', | |
| ip: $$('table tr:nth-child(3) td:nth-child(2)').text().trim() || 'N/A', | |
| link: $$('a.btn.btn-gradient.submit-btn').attr('href') || 'N/A', | |
| expired: $$('div.text-center.text-danger').text().trim() || 'N/A', | |
| }; | |
| console.log(`[LOG] Hasil: ${JSON.stringify(result, null, 2)}`); | |
| if (result.link === 'N/A') { | |
| console.log('[LOG] Link unduhan belum tersedia, mencoba lagi...'); | |
| await new Promise(resolve => setTimeout(resolve, 2000)); | |
| } | |
| } while (result.link === 'N/A'); | |
| console.log(`[LOG] Link unduhan berhasil ditemukan: ${result.link}`); | |
| let media = null; | |
| if (output === 'file') { | |
| console.log('[LOG] Mengunduh file...'); | |
| const { data: buffer, headers } = await client.get(result.link, { | |
| headers: { | |
| Referer: `https://luluvdo.com/d/${id}_h`, | |
| 'X-Forwarded-For': result.ip, | |
| }, | |
| responseType: 'arraybuffer', | |
| }); | |
| media = { | |
| buffer: Buffer.from(buffer), | |
| contentType: headers['content-type'] || 'application/octet-stream', | |
| fileName: result.link.split('/').pop() || 'downloaded_file', | |
| }; | |
| console.log('[LOG] File berhasil diunduh'); | |
| } | |
| return media ? { ...result, ...media } : result; | |
| } catch (error) { | |
| console.error(`[ERROR] Proses gagal: ${error.message}`); | |
| throw new Error(`Download failed: ${error.message}`); | |
| } | |
| } | |
| } | |
| app.get('/luluvdo', async (req, res) => { | |
| const { url, output } = req.query; | |
| const luluvdo = new Luluvdo(); | |
| if (!url) { | |
| return res.status(400).json({ error: 'URL is required' }); | |
| } | |
| try { | |
| const result = await luluvdo.download(url, output || 'json'); | |
| res.status(200).json(result); | |
| } catch (error) { | |
| console.error(`[ERROR] Download failed: ${error.message}`); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| const PORT = process.env.PORT || 7860; | |
| app.listen(PORT, async () => { | |
| console.log(`Server running on port ${PORT}`); | |
| }); | |
| process.on('SIGINT', async () => { | |
| process.exit(0); | |
| }); |