├── .gitignore ├── Procfile ├── .replit ├── public ├── logos │ ├── sc.png │ ├── sp.png │ ├── yt.png │ ├── audio.png │ └── video.png ├── index.html ├── style.css └── script.js ├── .github └── dependabot.yml ├── lib ├── ytVideoInfo.js ├── soundcloud.js ├── ytPlaylist.js ├── ytSearch.js ├── ytVideo.js └── ytAudio.js ├── index.js ├── README.md ├── package.json └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js 2 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "nodejs" 2 | run = "node ." -------------------------------------------------------------------------------- /public/logos/sc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deniscerri/yt-downloader/HEAD/public/logos/sc.png -------------------------------------------------------------------------------- /public/logos/sp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deniscerri/yt-downloader/HEAD/public/logos/sp.png -------------------------------------------------------------------------------- /public/logos/yt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deniscerri/yt-downloader/HEAD/public/logos/yt.png -------------------------------------------------------------------------------- /public/logos/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deniscerri/yt-downloader/HEAD/public/logos/audio.png -------------------------------------------------------------------------------- /public/logos/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deniscerri/yt-downloader/HEAD/public/logos/video.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /lib/ytVideoInfo.js: -------------------------------------------------------------------------------- 1 | const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); 2 | let settings = { method: "Get" }; 3 | let YoutubeAPIKey = 'AIzaSyB41R-kjCc6AezNxEp-t9xYUP_-3RfN92E'; 4 | 5 | var express = require('express'); 6 | var app = module.exports = express(); 7 | 8 | app.get('/video', (req,res)=>{ 9 | 10 | var VideoID = req.query.id; 11 | let url = `https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id=${VideoID}&key=${YoutubeAPIKey}` 12 | let list 13 | fetch(url, settings) 14 | .then(res => res.json()) 15 | .then((json) => { 16 | list = json; 17 | res.json(list); 18 | }); 19 | 20 | }) 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const cors = require('cors'); 3 | 4 | const app = express(); 5 | var root = __dirname 6 | 7 | app.listen(process.env.PORT, () =>{ 8 | console.log('Server is working'); 9 | }); 10 | 11 | app.use(cors()); 12 | app.use(express.static(root + "/public")); 13 | 14 | const ytmp3 = require('./lib/ytAudio') 15 | const ytmp4 = require('./lib/ytVideo') 16 | const ytSearch = require('./lib/ytSearch') 17 | const ytPlaylist = require('./lib/ytPlaylist') 18 | const soundcloud = require('./lib/soundcloud') 19 | const videoInfo = require('./lib/ytVideoInfo') 20 | 21 | app.use(ytmp3) 22 | app.use(ytmp4) 23 | app.use(ytSearch) 24 | app.use(ytPlaylist) 25 | app.use(soundcloud) 26 | app.use(videoInfo) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

5 | 6 | This repo uses ytdl-core and other dependencies to function. 7 | 8 | # Purpose 9 | 10 | **Convert any video, playlist on youtube to audio or video format in their highest quality possible. Also Download SoundCloud songs too :)** 11 | 12 | ## Usage: 13 | -Drop youtube video/playlist link. As long as its not private, it will download. 14 | -Drop soundloud link. 15 | -Search anything on youtube. 16 | -Click Audio/Video icon to download. 17 | -Profit 18 | 19 | ### My Host: 20 | Hosts like Heroku and Repl are fine for testing but performance is not the best when downloading. 21 | [Click Here!](https://ytdl.deniscerri.repl.co/) 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "converter", 3 | "version": "1.0.4", 4 | "description": "Simple youtube/soundcloud converter to mp3/mp4", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/deniscerri/converter.git" 15 | }, 16 | "author": "Denis Çerri", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/deniscerri/converter/issues" 20 | }, 21 | "homepage": "https://github.com/deniscerri/converter#readme", 22 | "dependencies": { 23 | "content-disposition": "^0.5.4", 24 | "cors": "^2.8.5", 25 | "express": "^4.18.1", 26 | "ffmpeg-static": "^5.0.0", 27 | "node-fetch": "^3.2.6", 28 | "soundcloud-downloader": "^1.0.0", 29 | "ytdl-core": "^4.11.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 deniscerri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/soundcloud.js: -------------------------------------------------------------------------------- 1 | const scdl = require('soundcloud-downloader').default; 2 | const contentDisposition = require('content-disposition'); 3 | 4 | var express = require('express'); 5 | var app = module.exports = express(); 6 | 7 | scID = process.env.soundcloudID; 8 | 9 | app.get('/download/sc', async (req, res) =>{ 10 | var URL = req.query.URL; 11 | 12 | try { 13 | 14 | let info = await scdl.getInfo(URL, scID); 15 | let artist = ''; 16 | try{ 17 | artist = info.publiser_metadata.artist; 18 | }catch(err){ 19 | artist = ''; 20 | } 21 | 22 | let filename = ''; 23 | if(artist != null){ 24 | filename = info.title+".mp3"; 25 | }else{ 26 | filename = artist + ' - '+ info.title+".mp3"; 27 | } 28 | 29 | 30 | res.writeHead(200, { 31 | 'Content-Type': 'audio/mpeg', 32 | 'Content-disposition': contentDisposition(filename)}); 33 | 34 | // Get audio stream going 35 | const audio = scdl.download(URL, scID).then(stream => stream.pipe(res)); 36 | 37 | 38 | } catch (err) { 39 | console.log(err); 40 | } 41 | 42 | 43 | }) 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/ytPlaylist.js: -------------------------------------------------------------------------------- 1 | const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); 2 | let settings = { method: "Get" }; 3 | let YoutubeAPIKey = process.env.YoutubeAPIKey; 4 | 5 | var express = require('express'); 6 | var app = module.exports = express(); 7 | 8 | app.get('/ytPlaylist', (req,res)=>{ 9 | 10 | var PlaylistID = req.query.id; 11 | let url = `https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=49&playlistId=${PlaylistID}&key=${YoutubeAPIKey}` 12 | let list 13 | fetch(url, settings) 14 | .then(res => res.json()) 15 | .then((json) => { 16 | //after getting the video results, we call fetch again to get their durations 17 | let url2 = `https://www.googleapis.com/youtube/v3/videos?id=` 18 | for(var i in json.items){ 19 | url2 = url2 + json.items[i].snippet.resourceId.videoId+','; 20 | } 21 | 22 | url2 = url2 + `&part=contentDetails,statistics&key=${YoutubeAPIKey}`; 23 | fetch(url2, settings) 24 | .then(res => res.json()) 25 | .then((json2) => { 26 | for(var i in json.items){ 27 | try{ 28 | json.items[i].snippet.length = json2.items[i].contentDetails.duration; 29 | json.items[i].snippet.views = json2.items[i].statistics.viewCount; 30 | }catch(err){ 31 | json.items[i].snippet.length = ''; 32 | json.items[i].snippet.views = ''; 33 | } 34 | } 35 | list = json 36 | }) 37 | .then(()=>{ 38 | res.json(list); 39 | }) 40 | }); 41 | 42 | }) 43 | -------------------------------------------------------------------------------- /lib/ytSearch.js: -------------------------------------------------------------------------------- 1 | const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); 2 | let settings = { method: "Get" }; 3 | let YoutubeAPIKey = process.env.YoutubeAPIKey; 4 | 5 | var express = require('express'); 6 | var app = module.exports = express(); 7 | 8 | app.get('/search', (req,res)=>{ 9 | 10 | var Query = req.query.Query; 11 | let url = `https://www.googleapis.com/youtube/v3/search?part=snippet&q=${Query}&maxResults=25®ionCode=US&key=${YoutubeAPIKey}` 12 | let list 13 | fetch(url, settings) 14 | .then(res => res.json()) 15 | .then((json) => { 16 | //after getting the video results, we call fetch again to more info on videos like durations 17 | let url2 = `https://www.googleapis.com/youtube/v3/videos?id=` 18 | for(var i in json.items){ 19 | url2 = url2 + json.items[i].id.videoId+','; 20 | } 21 | 22 | url2 = url2 + `&part=contentDetails,statistics&key=${YoutubeAPIKey}`; 23 | 24 | 25 | let j = 0; 26 | fetch(url2, settings) 27 | .then(res => res.json()) 28 | .then((json2) => { 29 | for(var i in json.items){ 30 | if(json.items[i].id.kind == 'youtube#video' || json.items[i].kind == 'youtube#playlistItem'){ 31 | try{ 32 | json.items[i].snippet.length = json2.items[j].contentDetails.duration; 33 | json.items[i].snippet.views = json2.items[j].statistics.viewCount; 34 | }catch(err){ 35 | json.items[i].snippet.length = ''; 36 | } 37 | j++; 38 | } 39 | } 40 | list = json 41 | }) 42 | .then(()=>{ 43 | res.json(list); 44 | }) 45 | }); 46 | 47 | }) 48 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Denis' Youtube Downloader 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 |
24 |
25 | 26 |

27 |
28 |
29 |

Title Sample

30 |
31 |

Channel Name

32 |

Video Views

33 |

Release Date

34 |
35 | 36 | 37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lib/ytVideo.js: -------------------------------------------------------------------------------- 1 | const ytdl = require('ytdl-core') 2 | const ffmpeg = require('ffmpeg-static') 3 | const spawn = require('child_process').spawn 4 | const contentDisposition = require('content-disposition'); 5 | 6 | var express = require('express'); 7 | var app = module.exports = express(); 8 | 9 | app.get('/download/MP4', async (req, res) =>{ 10 | var URL = req.query.URL; 11 | // //If something went wrong and files didnt get deleted, we clear them beforehand 12 | URL = fixURL(URL); 13 | 14 | try { 15 | let info = await ytdl.getInfo(URL) 16 | var title = info.videoDetails.title; 17 | //remove escape characters that cause errors 18 | title = title.replace(/[\'\\\/]/g,'') 19 | let filename = `${title}.mkv`; 20 | 21 | res.writeHead(200, { 22 | 'Content-Type': 'video/x-matroska', 23 | 'Content-disposition': contentDisposition(filename)}); 24 | 25 | 26 | // Get audio and video streams 27 | const audio = ytdl(URL, { quality: 'highestaudio' }) 28 | const video = ytdl(URL, { quality: 'highestvideo' }) 29 | 30 | // Start the ffmpeg child process 31 | const ffmpegProcess = spawn(ffmpeg, [ 32 | // Remove ffmpeg's console spamming 33 | '-loglevel', '8', '-hide_banner', 34 | // Set inputs 35 | '-i', 'pipe:3', 36 | '-i', 'pipe:4', 37 | // Map audio & video from streams 38 | '-map', '0:a', 39 | '-map', '1:v', 40 | // Keep encoding 41 | '-c:v', 'copy', 42 | // Define output file 43 | '-f','matroska','pipe:5', 44 | ], { 45 | windowsHide: true, 46 | stdio: [ 47 | /* Standard: stdin, stdout, stderr */ 48 | 'inherit', 'inherit', 'inherit', 49 | /* Custom: pipe:3, pipe:4, pipe:5 */ 50 | 'pipe', 'pipe', 'pipe', 51 | ], 52 | }); 53 | 54 | audio.pipe(ffmpegProcess.stdio[3]); 55 | video.pipe(ffmpegProcess.stdio[4]); 56 | ffmpegProcess.stdio[5].pipe(res); 57 | 58 | } catch (err) { 59 | console.log(err); 60 | } 61 | 62 | }) 63 | 64 | function fixURL(url){ 65 | let fixed = 'https://youtube.com/watch?v='; 66 | if(url.startsWith('https://youtu.be/')){ 67 | for(var i = 17;i{ 10 | var URL = req.query.URL; 11 | URL = fixURL(URL); 12 | 13 | try { 14 | try{ 15 | let info = await ytdl.getInfo(URL) 16 | var title = info.videoDetails.title; 17 | //remove characters that cause errors 18 | title = title.replace(/[\'\\\/\|\"]/g,'') 19 | 20 | var tags = { 21 | title: title, 22 | artist: info.videoDetails.ownerChannelName, 23 | } 24 | }catch(err){ 25 | var title = 'file' 26 | var tags = { 27 | title: '', 28 | artist: '', 29 | } 30 | } 31 | const audio = ytdl(URL, { filter: 'audioonly', quality: 'highestaudio' }) 32 | 33 | res.writeHead(200, { 34 | 'Content-Type': 'audio/mpeg', 35 | 'Content-disposition': contentDisposition(`${title}.mp3`)}); 36 | 37 | // Start the ffmpeg child process 38 | const ffmpegProcess = spawn(ffmpeg, [ 39 | // Remove ffmpeg's console spamming 40 | '-loglevel', '8', '-hide_banner', 41 | // Set inputs 42 | '-i', 'pipe:3', 43 | '-c:a','libmp3lame', 44 | '-b:a','128k', 45 | // add metadata 46 | '-metadata', `title="${tags.title}"`,'-metadata',`artist="${tags.artist}"`, 47 | // Define output file 48 | '-f','mp3','pipe:4', 49 | ], { 50 | windowsHide: true, 51 | stdio: [ 52 | /* Standard: stdin, stdout, stderr */ 53 | 'inherit', 'inherit', 'inherit', 54 | /* Custom: pipe:3, pipe:4, pipe:5 */ 55 | 'pipe', 'pipe', 'pipe', 56 | ], 57 | }); 58 | 59 | audio.pipe(ffmpegProcess.stdio[3]); 60 | ffmpegProcess.stdio[4].pipe(res); 61 | 62 | }catch (err) { 63 | console.log(err); 64 | } 65 | 66 | 67 | }) 68 | 69 | 70 | function fixURL(url){ 71 | let fixed = 'https://youtube.com/watch?v='; 72 | if(url.startsWith('https://youtu.be/')){ 73 | for(var i = 17;i img'); 14 | 15 | 16 | 17 | 18 | var baseURL = window.location.href; 19 | 20 | 21 | 22 | 23 | window.addEventListener("pageshow", () => { 24 | URLinput.value = ""; 25 | URLinput.focus(); 26 | }); 27 | 28 | var source = 'yt'; 29 | 30 | URLinput.addEventListener('input', inputQuery) 31 | mp3Btn.addEventListener("click", function(){downloadMp3(this.id)}) 32 | mp4Btn.addEventListener("click", function(){downloadMp4(this.id)}) 33 | searchBtn.addEventListener("click", function(){ 34 | if(URLinput.value == ''){ 35 | alert('Please write something on the search bar first. :)'); 36 | return; 37 | } 38 | 39 | let request = new XMLHttpRequest(); 40 | let url = `${baseURL}/search?Query=${URLinput.value}`; 41 | 42 | request.open('GET', url); 43 | request.responseType = 'text'; 44 | request.onload = function() { 45 | let info = request.response; 46 | let json = JSON.parse(info); 47 | removeResults(); 48 | addResults(json); 49 | headerImg.click(); 50 | }; 51 | request.send(); 52 | }) 53 | URLinput.addEventListener("keypress", function(e){ 54 | if(e.keyCode === 13){ 55 | if(playlistBtn.style.display == 'none'){ 56 | searchBtn.click(); 57 | }else if(searchBtn.style.display == 'none'){ 58 | playlistBtn.click(); 59 | } 60 | } 61 | }) 62 | playlistBtn.addEventListener("click", function(){ 63 | if(URLinput.value == ''){ 64 | alert('Please write something on the search bar first. :)'); 65 | return; 66 | } 67 | 68 | let playlistID = getPLaylistID(URLinput.value); 69 | 70 | let request = new XMLHttpRequest(); 71 | let url = `${baseURL}/ytPlaylist/?id=${playlistID}`; 72 | 73 | request.open('GET', url); 74 | request.responseType = 'text'; 75 | request.onload = function() { 76 | let info = request.response; 77 | let json = JSON.parse(info); 78 | removeResults(); 79 | addResults(json); 80 | headerImg.click(); 81 | }; 82 | request.send(); 83 | }) 84 | 85 | //downloadMp3 and downloadMp4 functions work for both link and list results. 86 | function downloadMp3(id){ 87 | if(!id.startsWith('http')){ 88 | id = URLinput.value; 89 | } 90 | console.log('Downloading: '+id); 91 | window.location.href = `${baseURL}/download/${source}/?URL=${id}`; 92 | 93 | 94 | // let request = new XMLHttpRequest(); 95 | // let url = `${baseURL}/download/${source}/?URL=${id}`; 96 | 97 | // request.open('GET', url); 98 | // request.responseType = 'text'; 99 | 100 | // request.onprogress = function(e){ 101 | // console.log(e.loaded + " "+ e.total); 102 | // } 103 | // request.send(); 104 | } 105 | 106 | function downloadMp4(id){ 107 | if(!id.startsWith('http')){ 108 | id = URLinput.value; 109 | } 110 | console.log('Downloading: '+id); 111 | window.location.href = `${baseURL}/download/MP4?URL=${id}` 112 | } 113 | 114 | //Show correct style and buttons for the input given 115 | function inputQuery(e){ 116 | let input = e.target; 117 | 118 | //if we recieve input we remove the red border 119 | input.style.border = "1px solid #0485ff"; 120 | 121 | //while we are writing we show the search button 122 | searchBtn.style.display = 'inline-flex'; 123 | 124 | //buttons are kept hidden unless you write out a link 125 | mp3Btn.style.display = 'none'; 126 | mp4Btn.style.display = 'none'; 127 | playlistBtn.style.display = 'none'; 128 | 129 | //header images are only shown if you insert a proper link 130 | headerImg.src = ''; 131 | headerImg.style.display = 'none'; 132 | 133 | //by default we use the youtube downloader 134 | source = 'yt'; 135 | 136 | //if we delete and input is empty we show red around the bar 137 | if (input.value == '') { 138 | input.style.border = "2px solid #FF0000"; 139 | } 140 | //if its a soundcloud link 141 | if((input.value).startsWith('https://soundcloud.com/')){ 142 | //we give a soundcloud themed color around the search bar 143 | //we hide the search button 144 | input.style.border = "1px solid #FF8C00"; 145 | searchBtn.style.display = 'none'; 146 | mp3Btn.style.display = 'inline-flex'; 147 | 148 | //Hide the mp4 button because we dont need it 149 | mp4Btn.style.display = "none"; 150 | 151 | //we show the soundcloud logo 152 | headerImg.src='../logos/sc.png' 153 | headerImg.style.display = 'block'; 154 | source = 'sc'; 155 | } 156 | //if its a spotify link 157 | if((input.value).startsWith('https://open.spotify.com/')){ 158 | //we give a spotify themed color around the search bar 159 | //we hide the search button 160 | input.style.border = "1px solid #1DB954"; 161 | searchBtn.style.display = 'none'; 162 | mp3Btn.style.display = 'inline-flex'; 163 | 164 | //Hide the mp4 button because we dont need it 165 | mp4Btn.style.display = "none"; 166 | 167 | //we show the spotify logo 168 | headerImg.src='../logos/sp.png' 169 | headerImg.style.display = 'block'; 170 | source = 'sp' 171 | } 172 | //if its a youtube link 173 | if((input.value).startsWith('https://youtu') || (input.value).startsWith('https://www.youtu')){ 174 | //if its a youtube playlist link we show the playlist button that lists the videos 175 | if((input.value).includes('&list=') || (input.value).includes('?list=')){ 176 | playlistBtn.style.display = 'inline-flex'; 177 | }else{ 178 | //if its a normal video link we just show the mp3 and mp4 button 179 | playlistBtn.style.display = 'none'; 180 | mp3Btn.style.display = 'inline-flex'; 181 | mp4Btn.style.display = 'inline-flex'; 182 | } 183 | //either way we hide the search button 184 | searchBtn.style.display = 'none'; 185 | 186 | //we show the youtube logo 187 | headerImg.src='../logos/yt.png' 188 | headerImg.style.display = 'block'; 189 | } 190 | 191 | } 192 | 193 | function addResults(json){ 194 | 195 | json = fixElements(json); 196 | 197 | if(json.error != undefined){ 198 | alert(json.error.message); 199 | return; 200 | } 201 | 202 | var resultsDiv = document.querySelector('.resultsArea'); 203 | var item = document.querySelector('.results-item'); 204 | for(var i = 0;i'+minutes+'>'+seconds); 272 | 273 | if(hours != ''){ 274 | timestamp += hours + ':'; 275 | } 276 | if(minutes != ''){ 277 | timestamp += minutes + ':'; 278 | //if minutes are 0 but the video is >= 1 hr, then we have to still show the minutes 279 | //if the video is shorter than a minute, we still have to show the minutes as zeroes 280 | }else{ 281 | if(hours != ''){ 282 | timestamp += '00:'; 283 | } 284 | if(seconds != ''){ 285 | timestamp += '00:'; 286 | } 287 | } 288 | if(seconds == '' && minutes != ''){ 289 | seconds = '00'; 290 | } 291 | timestamp += seconds; 292 | 293 | json.items[i].snippet.length = timestamp; 294 | } 295 | } 296 | 297 | //fix view count===================================================== 298 | for(i in json.items){ 299 | if(json.items[i].id.kind == 'youtube#video' || json.items[i].kind == 'youtube#playlistItem'){ 300 | let views = json.items[i].snippet.views; 301 | if(views >= 1000000000){ 302 | views = Math.floor(views/1000000000) + 'B views'; 303 | } 304 | if(views >= 1000000){ 305 | views = Math.floor(views/1000000) + 'M views'; 306 | } 307 | if(views >= 1000){ 308 | views = Math.floor(views/1000) + 'K views'; 309 | } 310 | if(views < 1000){ 311 | views = views + ' views'; 312 | } 313 | json.items[i].snippet.views = views; 314 | } 315 | } 316 | 317 | //fix release date=================================================== 318 | for(i in json.items){ 319 | if(json.items[i].id.kind == 'youtube#video' || json.items[i].kind == 'youtube#playlistItem'){ 320 | console.log(json.items[i]); 321 | let date = (json.items[i].snippet.publishedAt).split('-'); 322 | let day = date[2].split('T'); 323 | date[2] = day[0]; 324 | console.log(date); 325 | let videoDateInSeconds = new Date(`${date[0]}, ${date[1]}, ${date[2]}, 00:00`); 326 | let currentDate = new Date(); 327 | 328 | var interval = Math.abs(currentDate - videoDateInSeconds) / 1000; 329 | let tmpInterval = interval; 330 | interval = interval / 31536000; 331 | console.log(interval) 332 | 333 | if (interval > 1) { 334 | json.items[i].snippet.publishTime = Math.floor(interval) + " years ago"; 335 | continue; 336 | } 337 | interval = tmpInterval; 338 | interval = interval / 2592000; 339 | if (interval > 1) { 340 | json.items[i].snippet.publishTime = Math.floor(interval) + " months ago"; 341 | continue; 342 | } 343 | interval = tmpInterval; 344 | interval = interval / 86400; 345 | if (interval > 1) { 346 | json.items[i].snippet.publishTime = Math.floor(interval) + " days ago"; 347 | continue; 348 | } 349 | interval = tmpInterval; 350 | interval = interval / 3600; 351 | if (interval > 1) { 352 | json.items[i].snippet.publishTime = Math.floor(interval) + " hours ago"; 353 | continue; 354 | } 355 | interval = tmpInterval; 356 | interval = interval / 60; 357 | if (interval > 1) { 358 | json.items[i].snippet.publishTime = Math.floor(interval) + " minutes ago"; 359 | continue; 360 | } 361 | interval = tmpInterval; 362 | json.items[i].snippet.publishTime = Math.floor(interval) + " seconds ago"; 363 | } 364 | } 365 | return json; 366 | } 367 | 368 | function removeResults(){ 369 | var results = document.querySelectorAll('.results-item'); 370 | if(results.length == 1){ 371 | return; 372 | }else{ 373 | for (let i = 1; i < results.length; i++) { 374 | results[i].remove(); 375 | } 376 | } 377 | 378 | console.log('Finished deleting results') 379 | } 380 | 381 | function getPLaylistID(link){ 382 | let IDPart = link.split('list='); 383 | return IDPart[1]; 384 | } 385 | --------------------------------------------------------------------------------