From 7cef200da2d4802fd99d50fee10a1f5593832b43 Mon Sep 17 00:00:00 2001 From: "Hamza :)" Date: Mon, 15 Jul 2024 03:09:04 +0500 Subject: [PATCH] Update ytdl-core.js --- TalkDrove/dl/ytdl-core.js | 206 +++++++++++++------------------------- 1 file changed, 70 insertions(+), 136 deletions(-) diff --git a/TalkDrove/dl/ytdl-core.js b/TalkDrove/dl/ytdl-core.js index 20f3cc4..72d8281 100644 --- a/TalkDrove/dl/ytdl-core.js +++ b/TalkDrove/dl/ytdl-core.js @@ -1,49 +1,25 @@ -const ytdl = require('youtubedl-core'); const yts = require('youtube-yts'); const readline = require('readline'); -const ffmpeg = require('fluent-ffmpeg') -const NodeID3 = require('node-id3') +const ffmpeg = require('fluent-ffmpeg'); +const NodeID3 = require('node-id3'); const fs = require('fs'); -const { fetchBuffer } = require("./Function") -const { randomBytes } = require('crypto') -const ytIdRegex = /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})/ +const { fetchBuffer } = require("./Function"); +const { randomBytes } = require('crypto'); +const { ytdown } = require("nayan-media-downloader"); +const ytIdRegex = /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})/; class YT { constructor() { } - /** - * Checks if it is yt link - * @param {string|URL} url youtube url - * @returns Returns true if the given YouTube URL. - */ static isYTUrl = (url) => { - return ytIdRegex.test(url) + return ytIdRegex.test(url); } - /** - * VideoID from url - * @param {string|URL} url to get videoID - * @returns - */ static getVideoID = (url) => { - if (!this.isYTUrl(url)) throw new Error('is not YouTube URL') - return ytIdRegex.exec(url)[1] + if (!this.isYTUrl(url)) throw new Error('is not YouTube URL'); + return ytIdRegex.exec(url)[1]; } - /** - * @typedef {Object} IMetadata - * @property {string} Title track title - * @property {string} Artist track Artist - * @property {string} Image track thumbnail url - * @property {string} Album track album - * @property {string} Year track release date - */ - - /** - * Write Track Tag Metadata - * @param {string} filePath - * @param {IMetadata} Metadata - */ static WriteTags = async (filePath, Metadata) => { NodeID3.write( { @@ -52,10 +28,7 @@ class YT { originalArtist: Metadata.Artist, image: { mime: 'jpeg', - type: { - id: 3, - name: 'front cover', - }, + type: { id: 3, name: 'front cover' }, imageBuffer: (await fetchBuffer(Metadata.Image)).buffer, description: `Cover of ${Metadata.Title}`, }, @@ -66,51 +39,20 @@ class YT { ); } - /** - * - * @param {string} query - * @returns - */ static search = async (query, options = {}) => { - const search = await yts.search({ query, hl: 'id', gl: 'ID', ...options }) - return search.videos + const search = await yts.search({ query, hl: 'id', gl: 'ID', ...options }); + return search.videos; } - /** - * @typedef {Object} TrackSearchResult - * @property {boolean} isYtMusic is from YT Music search? - * @property {string} title music title - * @property {string} artist music artist - * @property {string} id YouTube ID - * @property {string} url YouTube URL - * @property {string} album music album - * @property {Object} duration music duration {seconds, label} - * @property {string} image Cover Art - */ - - - /** - * @typedef {Object} MusicResult - * @property {TrackSearchResult} meta music meta - * @property {string} path file path - */ - - /** - * Download music with full tag metadata - * @param {string|TrackSearchResult[]} query title of track want to download - * @returns {Promise} filepath of the result - */ static downloadMusic = async (query) => { try { - const getTrack = Array.isArray(query) ? query : await this.searchTrack(query); - const search = getTrack[0]//await this.searchTrack(query) - const videoInfo = await ytdl.getInfo('https://www.youtube.com/watch?v=' + search.id, { lang: 'id' }); - let stream = ytdl(search.id, { filter: 'audioonly', quality: 140 }); - let songPath = `./dustbin/${randomBytes(3).toString('hex')}.mp3` - stream.on('error', (err) => console.log(err)) + const getTrack = Array.isArray(query) ? query : await this.search(query); + const search = getTrack[0]; + const videoUrl = await ytdown(`https://www.youtube.com/watch?v=${search.id}`); + let songPath = `./dustbin/${randomBytes(3).toString('hex')}.mp3`; const file = await new Promise((resolve) => { - ffmpeg(stream) + ffmpeg(videoUrl) .audioFrequency(44100) .audioChannels(2) .audioBitrate(128) @@ -118,81 +60,66 @@ class YT { .audioQuality(5) .toFormat('mp3') .save(songPath) - .on('end', () => resolve(songPath)) + .on('end', () => resolve(songPath)); + }); + + const videoInfo = await ytdown.getInfo(`https://www.youtube.com/watch?v=${search.id}`); + await this.WriteTags(file, { + Title: search.title, + Artist: search.artist, + Image: search.image, + Album: search.album, + Year: videoInfo.upload_date.split('-')[0] }); - await this.WriteTags(file, { Title: search.title, Artist: search.artist, Image: search.image, Album: search.album, Year: videoInfo.videoDetails.publishDate.split('-')[0] }); + return { meta: search, path: file, size: fs.statSync(songPath).size - } + }; } catch (error) { - throw new Error(error) + throw new Error(error); } } - /** - * get downloadable video urls - * @param {string|URL} query videoID or YouTube URL - * @param {string} quality - * @returns - */ static mp4 = async (query, quality = 134) => { try { - if (!query) throw new Error('Video ID or YouTube Url is required') - const videoId = this.isYTUrl(query) ? this.getVideoID(query) : query - const videoInfo = await ytdl.getInfo('https://www.youtube.com/watch?v=' + videoId, { lang: 'id' }); - const format = ytdl.chooseFormat(videoInfo.formats, { format: quality, filter: 'videoandaudio' }) + if (!query) throw new Error('Video ID or YouTube Url is required'); + const videoId = this.isYTUrl(query) ? this.getVideoID(query) : query; + const videoInfo = await ytdown.getInfo(`https://www.youtube.com/watch?v=${videoId}`); + const format = videoInfo.formats.find(f => f.quality === quality && f.type === 'videoandaudio'); + return { - title: videoInfo.videoDetails.title, - thumb: videoInfo.videoDetails.thumbnails.slice(-1)[0], - date: videoInfo.videoDetails.publishDate, - duration: videoInfo.videoDetails.lengthSeconds, - channel: videoInfo.videoDetails.ownerChannelName, - quality: format.qualityLabel, - contentLength: format.contentLength, - description:videoInfo.videoDetails.description, + title: videoInfo.title, + thumb: videoInfo.thumbnail, + date: videoInfo.upload_date, + duration: videoInfo.duration, + channel: videoInfo.uploader, + quality: format.quality, + contentLength: format.filesize, + description: videoInfo.description, videoUrl: format.url - } + }; } catch (error) { - throw error + throw error; } } - /** - * Download YouTube to mp3 - * @param {string|URL} url YouTube link want to download to mp3 - * @param {IMetadata} metadata track metadata - * @param {boolean} autoWriteTags if set true, it will auto write tags meta following the YouTube info - * @returns - */ static mp3 = async (url, metadata = {}, autoWriteTags = false) => { try { - if (!url) throw new Error('Video ID or YouTube Url is required') - url = this.isYTUrl(url) ? 'https://www.youtube.com/watch?v=' + this.getVideoID(url) : url - const { videoDetails } = await ytdl.getInfo(url, { lang: 'id' }); - let stream = ytdl(url, { filter: 'audioonly', quality: 140 }); - let songPath = `./${randomBytes(3).toString('hex')}.mp3` + if (!url) throw new Error('Video ID or YouTube Url is required'); + const videoUrl = this.isYTUrl(url) ? `https://www.youtube.com/watch?v=${this.getVideoID(url)}` : url; + const videoInfo = await ytdown.getInfo(videoUrl); + let stream = await ytdown(videoUrl); + let songPath = `./${randomBytes(3).toString('hex')}.mp3`; let starttime; stream.once('response', () => { starttime = Date.now(); }); - /* - stream.on('progress', (chunkLength, downloaded, total) => { - const percent = downloaded / total; - const downloadedMinutes = (Date.now() - starttime) / 1000 / 60; - const estimatedDownloadTime = (downloadedMinutes / percent) - downloadedMinutes; - readline.cursorTo(process.stdout, 0); - process.stdout.write(`${(percent * 100).toFixed(2)}% downloaded `); - process.stdout.write(`(${(downloaded / 1024 / 1024).toFixed(2)}MB of ${(total / 1024 / 1024).toFixed(2)}MB)\n`); - process.stdout.write(`running for: ${downloadedMinutes.toFixed(2)}minutes`); - process.stdout.write(`, estimated time left: ${estimatedDownloadTime.toFixed(2)}minutes `); - readline.moveCursor(process.stdout, 0, -1); - //let txt = `${bgColor(color('[FFMPEG]]', 'black'), '#38ef7d')} ${color(moment().format('DD/MM/YY HH:mm:ss'), '#A1FFCE')} ${gradient.summer('[Converting..]')} ${gradient.cristal(p.targetSize)} kb` - });*/ + stream.on('end', () => process.stdout.write('\n\n')); - stream.on('error', (err) => console.log(err)) + stream.on('error', (err) => console.log(err)); const file = await new Promise((resolve) => { ffmpeg(stream) @@ -204,28 +131,35 @@ class YT { .toFormat('mp3') .save(songPath) .on('end', () => { - resolve(songPath) - }) + resolve(songPath); + }); }); + if (Object.keys(metadata).length !== 0) { - await this.WriteTags(file, metadata) + await this.WriteTags(file, metadata); } if (autoWriteTags) { - await this.WriteTags(file, { Title: videoDetails.title, Album: videoDetails.author.name, Year: videoDetails.publishDate.split('-')[0], Image: videoDetails.thumbnails.slice(-1)[0].url }) + await this.WriteTags(file, { + Title: videoInfo.title, + Album: videoInfo.uploader, + Year: videoInfo.upload_date.split('-')[0], + Image: videoInfo.thumbnail + }); } + return { meta: { - title: videoDetails.title, - channel: videoDetails.author.name, - seconds: videoDetails.lengthSeconds, - description:videoDetails.description, - image: videoDetails.thumbnails.slice(-1)[0].url + title: videoInfo.title, + channel: videoInfo.uploader, + seconds: videoInfo.duration, + description: videoInfo.description, + image: videoInfo.thumbnail }, path: file, size: fs.statSync(songPath).size - } + }; } catch (error) { - throw error + throw error; } } }