ytdl / index.js
ilhamdev's picture
Update index.js
a53d90d verified
const yts = require('yt-search');
const morgan = require('morgan');
const express = require('express');
const ytdl = require('ytdl-core');
const { Writable, pipeline } = require('stream');
const ffmpeg = require("fluent-ffmpeg")
const util = require('util');
const axios = require('axios');
// Regex untuk mengidentifikasi URL YouTube dan Mega
const ytIdRegex = /(?:http(?:s|):\/\/|)(?:(?:www\.|)?youtube(?:\-nocookie|)\.com\/(?:shorts\/)?(?:watch\?.*(?:|\&)v=|embed\/|v\/)?|youtu\.be\/)([-_0-9A-Za-z]{11})/;
// Fungsi untuk melakukan HTTP POST request
const post = async (url, form, headers = {}) => {
const response = await fetch(url, {
method: 'post',
body: new URLSearchParams(form),
headers
});
return response;
};
//YTDL-CORE
async function uploadBuffer(buffer) {
return new Promise(async (resolve, reject) => {
let res = await axios.post('https://ilhamdev-up.hf.space/upload', {
file: buffer.toString('base64'),
headers: {
'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0"
}
}).catch(e => reject(e))
if (res.status !== 200) {
resolve(res?.statusText)
} else {
resolve(res?.data?.url)
}
})
}
async function streamToBuffer(stream) {
const chunks = [];
const captureChunks = new Writable({
write(chunk, encoding, callback) {
chunks.push(chunk);
callback();
}
});
await util.promisify(pipeline)(stream, captureChunks);
return Buffer.concat(chunks);
}
function formatViews(viewCount) {
if (viewCount >= 1000000000) {
return (viewCount / 1000000000).toFixed(1) + 'B';
} else if (viewCount >= 1000000) {
return (viewCount / 1000000).toFixed(1) + 'M';
} else {
return viewCount >= 1000
? (viewCount / 1000).toFixed(1) + 'K'
: viewCount.toString();
}
}
function formatDuration(durationInSeconds) {
const hours = Math.floor(durationInSeconds / 3600);
const minutes = Math.floor((durationInSeconds % 3600) / 60);
const seconds = durationInSeconds % 60;
return hours > 0
? hours +
':' +
minutes.toString().padStart(2, '0') +
':' +
seconds.toString().padStart(2, '0')
: minutes + ':' + seconds.toString().padStart(2, '0');
}
function formatSize(bytes, si = false, dp = 2) {
const thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return `${bytes} B`;
}
const units = si
? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
let u = -1;
const r = 10 ** dp;
do {
bytes /= thresh;
++u;
} while (
Math.round(Math.abs(bytes) * r) / r >= thresh &&
u < units.length - 1
);
return `${bytes.toFixed(dp)} ${units[u]}`;
}
async function ytmp4(url, quality = 'highestvideo') {
try {
const ID = ytdl.getVideoID(url),
data = await ytdl.getInfo('https://www.youtube.com/watch?v=' + ID)
const videoStream = await ytdl(ID, { filter: 'videoandaudio', quality: 'highestvideo' });
let buffer = await streamToBuffer(videoStream)
let upload = await uploadBuffer(buffer)
let format = ytdl.chooseFormat(data.formats, { filter: 'videoandaudio', quality: quality });
if (format) {
return {
title: data.videoDetails.title,
description: data.videoDetails.description,
channel: data.videoDetails.ownerChannelName,
views: formatViews(data.videoDetails.viewCount),
publish: data.videoDetails.publishDate,
duration: formatDuration(data.videoDetails.lengthSeconds),
size: format.contentLength ? formatSize(format.contentLength) : 0,
quality: format.qualityLabel,
thumb: data.videoDetails.thumbnails[0].url,
dl_url: format.url,
url_v2: upload,
}
} else {
throw new Error('No suitable format found');
}
} catch (error) {
console.error('Error occurred:', error);
return null;
}
}
async function ytmp3(url, bitrate = 'lowestaudio') {
try {
const ID = ytdl.getVideoID(url),
data = await ytdl.getInfo('https://www.youtube.com/watch?v=' + ID)
let audioStream = await ytdl(ID, { filter: 'audioonly', quality: 'lowestaudio', });
let buffer = await streamToBuffer(audioStream)
let upload = await uploadBuffer(buffer)
let format = ytdl.chooseFormat(data.formats, { filter: 'audioonly', quality: bitrate });
if (format) {
return {
title: data.videoDetails.title,
description: data.videoDetails.description,
channel: data.videoDetails.ownerChannelName,
views: formatViews(data.videoDetails.viewCount),
publish: data.videoDetails.publishDate,
duration: formatDuration(data.videoDetails.lengthSeconds),
size: format.contentLength ? formatSize(format.contentLength) : 0,
quality: format.audioQuality,
thumb: data.videoDetails.thumbnails[0].url,
dl_url: format.url,
url_v2: upload
}
} else {
throw new Error('No suitable format found');
}
} catch (error) {
console.error('Error occurred:', error);
return null;
}
}
// Fungsi untuk mengkonversi video dari YouTube
const convert = async (url, v_id, ftype, fquality, fname, token, timeExpire) => {
let params = {
v_id,
ftype,
fquality,
fname,
token,
timeExpire,
client: 'yt5s.com'
};
// Mengirim permintaan konversi
let resServer = await (await post(url, params, { 'x-requested-key': 'de0cfuirtgf67a' })).json();
let server = resServer.c_server;
// Jika tidak ada server dan tipe file adalah mp3, kembalikan null
if (!server && ftype === 'mp3') return server || resServer.d_url || '';
// Mengambil data hasil konversi
let data = await (await post(`${server}/api/json/convert`, params)).json();
let result;
// Memeriksa status kode hasil konversi
if (data.statusCode === 200) result = data.result;
while (!result) {
let json = await (await post(`${server}/api/json/convert`, params)).json();
if (json.statusCode === 200) {
result = json.result;
break;
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
return result;
};
// Fungsi untuk mendownload video dari YouTube
const youtubedl = async (url) => {
let html = await (await fetch('https://yt5s.com/en32')).text();
let urlAjax = (html.match(/k_url_search="(.*?)"/) || [])[1];
let urlConvert = (html.match(/k_url_convert="(.*?)"/) || [])[1];
let json = await (await post(urlAjax, { q: url, vt: 'home' })).json();
let video = {}, audio = {};
if (!json?.links) throw json.mess;
Object.values(json.links.mp4).map(({ k, size }) => video[k] = {
quality: k,
fileSizeH: size,
fileSize: parseFloat(size) * (/MB$/.test(size) ? 1000 : 1),
download: convert.bind(null, urlConvert, json.vid, 'mp4', k, json.fn, json.token, parseInt(json.timeExpires))
});
Object.values(json.links.mp3).map(({ key, size }) => audio[key] = {
quality: key,
fileSizeH: size,
fileSize: parseFloat(size) * (/MB$/.test(size) ? 1000 : 1),
download: convert.bind(null, urlConvert, json.vid, 'mp3', key.replace(/kbps/i, ''), json.fn, json.token, parseInt(json.timeExpires))
});
return {
id: json.vid,
title: json.title,
thumbnail: `https://i.ytimg.com/vi/${json.vid}/0.jpg`,
video,
audio
};
};
async function streamToBuffer(stream) {
const chunks = [];
const captureChunks = new Writable({
write(chunk, encoding, callback) {
chunks.push(chunk);
callback();
}
});
await util.promisify(pipeline)(stream, captureChunks);
return Buffer.concat(chunks);
}
async function fileDitch(media){
return new Promise(async (resolve, reject) => {
let {fileTypeFromBuffer} = await (await import('file-type'))
let mime = await fileTypeFromBuffer(media)
let form = new FormData()
form.append("files[]", media, `file-${new Date().getTime()}.${mime.ext}`)
axios.post("https://up1.fileditch.com/temp/upload.php", form, {
headers: {
"User-Agent": generateRandomUserAgent(),
"X-Forwarded-For": generateRandomIP(),
...form.getHeaders()
}
}).then(({ data }) => resolve(data?.files[0]?.url)).catch(reject)
})
//https://up1.fileditch.com/upload.php
}
async function convertMp4ToAudio(inputBuffer) {
return new Promise((resolve, reject) => {
const inputStream = new Readable();
inputStream.push(inputBuffer);
inputStream.push(null);
const outputBuffer = [];
const outputStream = new Writable({
write(chunk, encoding, callback) {
outputBuffer.push(chunk);
callback();
}
});
ffmpeg(inputStream)
.toFormat('mp3')
.on('end', () => {
console.log('Conversion finished!');
resolve(Buffer.concat(outputBuffer));
})
.on('error', (err) => {
console.error('Error during conversion:', err);
reject(err);
})
.pipe(outputStream);
});
}
async function ytAPI(url) {
try {
const ID = ytdl.getVideoID(url)
//let videoStream = await ytdl(ID, { filter: 'audioandvideo', quality: 'highestvideo' });
let data = await ytdl.getInfo('https://www.youtube.com/watch?v=' + ID)
let format = ytdl.chooseFormat(data.formats, { filter: 'videoandaudio', quality: 'highestvideo' });
let audioStream = await ytdl(ID, {filter: "audioandvideo", quality:"lowestvideo"})
//let buffermp4 = await streamToBuffer(videoStream)
let buffermp3 = await streamToBuffer(audioStream)
buffermp3 = await convertMp4ToAudio(buffermp3)
//buffermp4 = await fileDitch(buffermp4)
buffermp3 = await fileDitch(buffermp3)
return {
mp4_url: format.url,
mp3_url: buffermp3,
}
} catch (err) {
console.error('Error occurred:', err);
return null;
}
}
const app = express()
.set('json spaces', 4)
.use(morgan('dev'))
.use(express.json())
.all('/', (_, res) => res.send('Hello World'))
.get('/yt', async (req, res) => {
const host = 'https://' + req.get('host');
try {
let { url, type, quality, json } = req.query;
if (!ytIdRegex.test(url)) return res.json({ message: 'Invalid URL' });
if (!!json) {
let ytId = ytIdRegex.exec(url)?.[1];
if (!ytId) return res.json({ message: 'No video id found' });
let data = await yts({ videoId: ytId });
const downloadUrls = {
audio: `${host}/yt?url=${url}&type=audio&quality=128kbps`,
video: `${host}/yt?url=${url}&type=video&quality=`,
};
return res.json({
...data,
download: downloadUrls
});
}
if (!type || !/audio|video/i.test(type)) type = 'video';
let data = await youtubedl(url).catch(e => console.log(e));
if (!data) return res.json({ message: 'Error: link download not found' });
type = type.toLowerCase();
let result = quality ? Object.values(data[type]).find(x => x.quality == quality) : Object.values(data[type])[0];
if (quality && !result) return res.json({ message: `Invalid quality: ${quality}, available quality (${Object.keys(data[type]).join('/')})` });
if (!result) return res.json({ message: 'Error: can\'t download' });
res.redirect(await result.download());
} catch (e) {
console.log(e);
res.json({ message: e });
}
})
.get('/search', async (req, res) => {
try {
let q = req.query.q || req.query.query;
if (!q) return res.json({ message: 'Input parameter q' });
let data = await yts(q);
if (!data.all[0]) return res.json({ message: 'Not found' });
res.json(data.all);
} catch (e) {
console.log(e);
res.json({ message: e });
}
})
.get('/ytdl', async (req, res) => {
try {
let { url } = req.query;
//if (!ytIdRegex.test(url)) return res.json({ message: 'Invalid URL' });
if (!ytdl.validateURL(url)) return res.json({ message: 'Invalid URL' });
let data = await ytAPI(url) || {};
let videoID = ytdl.getVideoID(url);
let dataInfo = await yts({ videoId: videoID }) || {};
//let otherAPI = await (await axios.get("https://line.1010diy.com/web/free-mp3-finder/detail?url="+url))?.data?.data ? await (await axios.get("https://line.1010diy.com/web/free-mp3-finder/detail?url="+url))?.data?.data : null
let response = {
...dataInfo,
mp4: data.mp4_url,
mp3: data.mp3_url,
//other: otherAPI || null
};
return res.json(response);
} catch (e) {
console.log(e);
return res.status(500).json({ message: e.message });
}
})
.listen(7860, () => console.log('App running on port 7860'));