├── .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 |
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 |
--------------------------------------------------------------------------------