Initial version
						commit
						80411aeb08
					
				@ -0,0 +1,2 @@
 | 
			
		||||
[Desktop Entry]
 | 
			
		||||
Icon=/home/andrew/Pictures/icons/icon.png
 | 
			
		||||
@ -0,0 +1,4 @@
 | 
			
		||||
node_modules
 | 
			
		||||
release
 | 
			
		||||
package-lock.json
 | 
			
		||||
test.js
 | 
			
		||||
@ -0,0 +1,200 @@
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
const ytdl = require("ytdl-core");
 | 
			
		||||
const express = require("express");
 | 
			
		||||
const app = express();
 | 
			
		||||
const bodyParser = require("body-parser");
 | 
			
		||||
const ffmpeg = require("ffmpeg-static");
 | 
			
		||||
const os = require("os");
 | 
			
		||||
const cp = require("child_process");
 | 
			
		||||
 | 
			
		||||
app.use(bodyParser.urlencoded({ extended: true }));
 | 
			
		||||
app.use(express.static(__dirname + "/public"));
 | 
			
		||||
 | 
			
		||||
// To do
 | 
			
		||||
// Preset
 | 
			
		||||
// Download location selection
 | 
			
		||||
// Choosing correct encoding
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
app.get("/", (req, res) => {
 | 
			
		||||
	res.sendFile(__dirname + "/index.html");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function getVideoInfo(url) {
 | 
			
		||||
	const info = await ytdl.getInfo(url);
 | 
			
		||||
	return info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
app.post("/", (req, res) => {
 | 
			
		||||
	const url = req.body.url;
 | 
			
		||||
	getVideoInfo(url)
 | 
			
		||||
		.then((video) =>
 | 
			
		||||
			res.json({
 | 
			
		||||
				status: true,
 | 
			
		||||
				title: video.videoDetails.title,
 | 
			
		||||
				formats: video.formats,
 | 
			
		||||
				url: url,
 | 
			
		||||
			})
 | 
			
		||||
		)
 | 
			
		||||
		.catch((error) =>
 | 
			
		||||
			res.json({ status: false, message: "Use correct URL" })
 | 
			
		||||
		);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Downloading video
 | 
			
		||||
 | 
			
		||||
app.post("/download", (req, res) => {
 | 
			
		||||
	const itag = parseInt(req.body.audioTag || req.body.videoTag);
 | 
			
		||||
	const url = req.body.url;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// Function to find video/audio info
 | 
			
		||||
 | 
			
		||||
	async function findInfo(url, itag) {
 | 
			
		||||
		const data = await ytdl.getInfo(url);
 | 
			
		||||
		const format = ytdl.chooseFormat(data.formats, { quality: itag });
 | 
			
		||||
		const title = data.videoDetails.title;
 | 
			
		||||
		let extension;
 | 
			
		||||
		if (format.hasVideo) {
 | 
			
		||||
			extension = format.mimeType.split("; ")[0].split("/")[1];
 | 
			
		||||
		} else {
 | 
			
		||||
			if (format.audioCodec === "mp4a.40.2") {
 | 
			
		||||
				extension = "m4a";
 | 
			
		||||
			} else {
 | 
			
		||||
				extension = format.audioCodec;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let quality;
 | 
			
		||||
		if (format.hasVideo) {
 | 
			
		||||
			quality = format.qualityLabel;
 | 
			
		||||
		} else {
 | 
			
		||||
			quality = format.audioBitrate + "kbps";
 | 
			
		||||
		}
 | 
			
		||||
		const filename = title + "_" + quality + "." + extension
 | 
			
		||||
		const info = {
 | 
			
		||||
			format: format,
 | 
			
		||||
			title: title,
 | 
			
		||||
			extension: extension,
 | 
			
		||||
			quality:quality,
 | 
			
		||||
			filename:filename
 | 
			
		||||
		};
 | 
			
		||||
		return info;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	findInfo(url, itag).then((info) => {
 | 
			
		||||
		const format = info.format;
 | 
			
		||||
		const filename = info.filename;
 | 
			
		||||
 | 
			
		||||
		// If video
 | 
			
		||||
		if (format.hasVideo) {
 | 
			
		||||
			let video = ytdl(url, { quality: itag });
 | 
			
		||||
			let audio = ytdl(url, {
 | 
			
		||||
				filter: "audioonly",
 | 
			
		||||
				highWaterMark: 1 << 25,
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			const ffmpegProcess = cp.spawn(
 | 
			
		||||
				ffmpeg,
 | 
			
		||||
				[
 | 
			
		||||
					"-i",
 | 
			
		||||
					`pipe:3`,
 | 
			
		||||
					"-i",
 | 
			
		||||
					`pipe:4`,
 | 
			
		||||
					"-map",
 | 
			
		||||
					"0:v",
 | 
			
		||||
					"-map",
 | 
			
		||||
					"1:a",
 | 
			
		||||
					"-c:v",
 | 
			
		||||
					"copy",
 | 
			
		||||
					"-c:a",
 | 
			
		||||
					"libmp3lame",
 | 
			
		||||
					"-crf",
 | 
			
		||||
					"27",
 | 
			
		||||
					"-preset",
 | 
			
		||||
					"fast",
 | 
			
		||||
					"-movflags",
 | 
			
		||||
					"frag_keyframe+empty_moov",
 | 
			
		||||
					'-f', "mp4",
 | 
			
		||||
					"-loglevel",
 | 
			
		||||
					"error",
 | 
			
		||||
					"-",
 | 
			
		||||
				],
 | 
			
		||||
				{
 | 
			
		||||
					stdio: ["pipe", "pipe", "pipe", "pipe", "pipe"],
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			video.pipe(ffmpegProcess.stdio[3]);
 | 
			
		||||
			audio.pipe(ffmpegProcess.stdio[4]);
 | 
			
		||||
 | 
			
		||||
			res.header("Content-Disposition", "attachment; filename='" + filename + "'");
 | 
			
		||||
			ffmpegProcess.stdio[1].pipe(res);
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
		// If audio 
 | 
			
		||||
		 else {
 | 
			
		||||
			res.header("Content-Disposition", "attachment; filename=" + filename);
 | 
			
		||||
 | 
			
		||||
			ytdl(url, { quality: itag })
 | 
			
		||||
				.on("progress", (_, downloaded, size) => {
 | 
			
		||||
					const progress = (downloaded / size) * 100;
 | 
			
		||||
				})
 | 
			
		||||
				// .pipe(fs.createWriteStream(os.homedir() + "/Downloads/" + filename))
 | 
			
		||||
				.pipe(res);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Off for now
 | 
			
		||||
app.post("", (req, res) => {
 | 
			
		||||
	const itag = parseInt(req.body.audioTag || req.body.videoTag);
 | 
			
		||||
	const url = req.body.url;
 | 
			
		||||
 | 
			
		||||
	async function findFormat(url, itag) {
 | 
			
		||||
		const info = await ytdl.getInfo(url);
 | 
			
		||||
		const format = ytdl.chooseFormat(info.formats, { quality: itag });
 | 
			
		||||
		const title = info.videoDetails.title;
 | 
			
		||||
 | 
			
		||||
		// Quality for filename
 | 
			
		||||
		let quality;
 | 
			
		||||
		if (format.hasVideo) {
 | 
			
		||||
			quality = format.qualityLabel;
 | 
			
		||||
		} else {
 | 
			
		||||
			quality = format.audioBitrate + "kbps";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// File extension for filename
 | 
			
		||||
		let extension;
 | 
			
		||||
		if (format.hasVideo) {
 | 
			
		||||
			extension = format.mimeType.split("; ")[0].split("/")[1];
 | 
			
		||||
		} else {
 | 
			
		||||
			if (format.audioCodec === "mp4a.40.2") {
 | 
			
		||||
				extension = "m4a";
 | 
			
		||||
			} else {
 | 
			
		||||
				extension = format.audioCodec;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		const filename = title + "_" + quality + "." + extension;
 | 
			
		||||
		return filename;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	findFormat(url, itag)
 | 
			
		||||
		.then((filename) => {
 | 
			
		||||
			res.header("Content-Disposition", "attachment; filename=.m4a");
 | 
			
		||||
			ytdl(url, { quality: itag })
 | 
			
		||||
				.on("progress", (_, downloaded, size) => {
 | 
			
		||||
					const progress = (downloaded / size) * 100;
 | 
			
		||||
					// console.log("Progress: " + progress + "%" )
 | 
			
		||||
				})
 | 
			
		||||
				// .pipe(fs.createWriteStream(os.homedir() + "/Downloads/" + filename))
 | 
			
		||||
				.pipe(res);
 | 
			
		||||
		})
 | 
			
		||||
		.catch((error) => {
 | 
			
		||||
			console.log(error);
 | 
			
		||||
		});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.listen(3000, () => {
 | 
			
		||||
	console.log("Server: http://localhost:3000");
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Youtube Video Downloader</title>
 | 
			
		||||
    <link rel="stylesheet" href="index.css">
 | 
			
		||||
    <script src="index.js" defer></script>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <!-- Theme toggle -->
 | 
			
		||||
    <div id="themeToggle" onclick="toggle()">
 | 
			
		||||
        <div id="themeToggleInside"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <h1>YouTube Downloader</h1>
 | 
			
		||||
    <input type="text" name="url" placeholder="Paste Video URL or ID here" id="url" autofocus>
 | 
			
		||||
    <!-- Get info button -->
 | 
			
		||||
    <button id="getInfo" onclick="clickAnimation('getInfo')">Get info</button>
 | 
			
		||||
 | 
			
		||||
    <p id="incorrectMsg"></p>
 | 
			
		||||
 | 
			
		||||
    <div id="loadingWrapper">
 | 
			
		||||
        <span>Loading</span>
 | 
			
		||||
        <svg version="1.1" id="L4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
 | 
			
		||||
            y="0px" viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
 | 
			
		||||
            <circle fill="rgb(84, 171, 222)" stroke="none" cx="6" cy="50" r="6">
 | 
			
		||||
                <animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin="0.1" />
 | 
			
		||||
            </circle>
 | 
			
		||||
            <circle fill="rgb(84, 171, 222)" stroke="none" cx="26" cy="50" r="6">
 | 
			
		||||
                <animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin="0.2" />
 | 
			
		||||
            </circle>
 | 
			
		||||
            <circle fill="rgb(84, 171, 222)" stroke="none" cx="46" cy="50" r="6">
 | 
			
		||||
                <animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin="0.3" />
 | 
			
		||||
            </circle>
 | 
			
		||||
        </svg>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <div id="hidden">
 | 
			
		||||
        <div id="btnContainer">
 | 
			
		||||
            <button class="toggleBtn" id="videoToggle">Video</button>
 | 
			
		||||
            <button class="toggleBtn" id="audioToggle">Audio</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <p id="title">Title: </p>
 | 
			
		||||
 | 
			
		||||
        <div id="videoList">
 | 
			
		||||
            <form action="/download" method="post">
 | 
			
		||||
                <label>Select Format - </label>
 | 
			
		||||
                <select name="videoTag" id="videoFormatSelect">
 | 
			
		||||
                </select>
 | 
			
		||||
                <br>
 | 
			
		||||
                <input type="hidden" name="url" class="url">
 | 
			
		||||
                <button type="submit" class="submitBtn">Download</button>
 | 
			
		||||
            </form>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div id="audioList">
 | 
			
		||||
            <form action="/download" method="post">
 | 
			
		||||
                <label>Select Format - </label>
 | 
			
		||||
                <select name="audioTag" id="audioFormatSelect">
 | 
			
		||||
                </select>
 | 
			
		||||
                <br>
 | 
			
		||||
                <input type="hidden" name="url" class="url">
 | 
			
		||||
                <button type="submit" class="submitBtn" id="submitBtn" onclick="clickAnimation('submitBtn')">Download</button>
 | 
			
		||||
            </form>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
const { app, BrowserWindow } = require('electron')
 | 
			
		||||
const path = require('path')
 | 
			
		||||
require("./app.js")
 | 
			
		||||
 | 
			
		||||
function createWindow () {
 | 
			
		||||
  const win = new BrowserWindow({
 | 
			
		||||
    width: 800,
 | 
			
		||||
    height: 600
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  win.loadURL("http://localhost:3000")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
app.whenReady().then(() => {
 | 
			
		||||
  createWindow()
 | 
			
		||||
 | 
			
		||||
  app.on('activate', () => {
 | 
			
		||||
    if (BrowserWindow.getAllWindows().length === 0) {
 | 
			
		||||
      createWindow()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
app.on('window-all-closed', () => {
 | 
			
		||||
  if (process.platform !== 'darwin') {
 | 
			
		||||
    app.quit()
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
@ -0,0 +1,68 @@
 | 
			
		||||
{
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"body-parser": "^1.20.0",
 | 
			
		||||
		"express": "^4.18.1",
 | 
			
		||||
		"ffmpeg-static": "^5.0.2",
 | 
			
		||||
		"ytdl-core": "^4.11.0"
 | 
			
		||||
	},
 | 
			
		||||
	"name": "ytdownloader",
 | 
			
		||||
	"version": "1.0.0",
 | 
			
		||||
	"main": "main.js",
 | 
			
		||||
	"scripts": {
 | 
			
		||||
		"start": "electron .",
 | 
			
		||||
		"windows": "electron-builder -w",
 | 
			
		||||
		"linux": "electron-builder -l",
 | 
			
		||||
		"mac": "electron-builder -m"
 | 
			
		||||
	},
 | 
			
		||||
	"keywords": [],
 | 
			
		||||
	"author": "Andrew",
 | 
			
		||||
	"license": "MIT",
 | 
			
		||||
	"description": "Download videos and audios from YouTube",
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"electron": "^19.0.9",
 | 
			
		||||
		"electron-builder": "^23.1.0"
 | 
			
		||||
	},
 | 
			
		||||
	"build": {
 | 
			
		||||
		"productName": "YTDownloader",
 | 
			
		||||
		"appId": "com.andrew.ytdownloader",
 | 
			
		||||
		"mac": {
 | 
			
		||||
			"category": "Utility",
 | 
			
		||||
			"target": [
 | 
			
		||||
				"dmg"
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
		"dmg": {
 | 
			
		||||
			"contents": [
 | 
			
		||||
				{
 | 
			
		||||
					"x": 130,
 | 
			
		||||
					"y": 220
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"x": 410,
 | 
			
		||||
					"y": 220,
 | 
			
		||||
					"type": "link",
 | 
			
		||||
					"path": "/Applications"
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			"sign": false
 | 
			
		||||
		},
 | 
			
		||||
		"asar": true,
 | 
			
		||||
		"directories": {
 | 
			
		||||
			"buildResources": "resources",
 | 
			
		||||
			"output": "release"
 | 
			
		||||
		},
 | 
			
		||||
		"linux": {
 | 
			
		||||
			"target": [
 | 
			
		||||
				"AppImage"
 | 
			
		||||
			],
 | 
			
		||||
			"category": "Utility"
 | 
			
		||||
		},
 | 
			
		||||
		"win": {
 | 
			
		||||
			"target": "nsis"
 | 
			
		||||
		},
 | 
			
		||||
		"nsis": {
 | 
			
		||||
			"allowToChangeInstallationDirectory": true,
 | 
			
		||||
			"oneClick": false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 4.8 KiB  | 
@ -0,0 +1,149 @@
 | 
			
		||||
body {
 | 
			
		||||
	font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
 | 
			
		||||
		Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
 | 
			
		||||
		sans-serif;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	font-size: xx-large;
 | 
			
		||||
	padding: 10px;
 | 
			
		||||
	background-color: whitesmoke;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#title {
 | 
			
		||||
	font-size: x-large;
 | 
			
		||||
	font-family: sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
	font-size: 50px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[type="text"] {
 | 
			
		||||
	padding: 10px;
 | 
			
		||||
	border-radius: 10px;
 | 
			
		||||
	border: 1px solid gray;
 | 
			
		||||
	outline: none;
 | 
			
		||||
	font-size: large;
 | 
			
		||||
	width: 70%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#getInfo {
 | 
			
		||||
	padding: 10px;
 | 
			
		||||
	background-color: rgb(64, 227, 64);
 | 
			
		||||
	border: none;
 | 
			
		||||
	border-radius: 8px;
 | 
			
		||||
	color: white;
 | 
			
		||||
	font-size: large;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
 | 
			
		||||
	/* animation-name: clickAnimation; */
 | 
			
		||||
	animation-duration: .5s;
 | 
			
		||||
	animation-timing-function: linear;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes clickAnimation {
 | 
			
		||||
	0% {background-color: rgb(64, 227, 64);}
 | 
			
		||||
	50% {background-color: rgb(40, 126, 40);}
 | 
			
		||||
	100% {background-color: rgb(64, 227, 64);}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#hidden {
 | 
			
		||||
	display: none;
 | 
			
		||||
	/* display: inline-block; */
 | 
			
		||||
	background-color: rgb(143, 239, 207);
 | 
			
		||||
	border-radius: 10px;
 | 
			
		||||
	width: 80%;
 | 
			
		||||
	padding: 10px;
 | 
			
		||||
	color:black;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#btnContainer {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	flex-direction: row;
 | 
			
		||||
	justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.toggleBtn {
 | 
			
		||||
	width: 50%;
 | 
			
		||||
	font-size: x-large;
 | 
			
		||||
	border: none;
 | 
			
		||||
	background-color: rgb(108, 231, 190);
 | 
			
		||||
	border-radius: 10px;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	padding: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
select {
 | 
			
		||||
	padding: 15px;
 | 
			
		||||
	background-color: lightgreen;
 | 
			
		||||
	border: none;
 | 
			
		||||
	border-radius: 8px;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	font-size: large;
 | 
			
		||||
	margin: 8px;
 | 
			
		||||
	outline:none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
label {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	top: 3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#videoList,
 | 
			
		||||
#audioList {
 | 
			
		||||
	display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.submitBtn {
 | 
			
		||||
	padding: 15px;
 | 
			
		||||
	margin: 15px;
 | 
			
		||||
	border-radius: 8px;
 | 
			
		||||
	background-color: rgb(64, 227, 64);
 | 
			
		||||
	color: white;
 | 
			
		||||
	border: none;
 | 
			
		||||
	font-size: large;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	
 | 
			
		||||
	animation-duration: .5s;
 | 
			
		||||
	animation-timing-function: linear;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#incorrectMsg {
 | 
			
		||||
	color: rgb(250, 59, 59);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#loadingWrapper{
 | 
			
		||||
	display:none;
 | 
			
		||||
	flex-direction: row;
 | 
			
		||||
	justify-content: center;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
svg {
 | 
			
		||||
	width: 100px;
 | 
			
		||||
	height: 100px;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	margin-left:20px
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#themeToggle {
 | 
			
		||||
	width: 55px;
 | 
			
		||||
	height: 30px;
 | 
			
		||||
	background-color: rgb(147, 174, 185);
 | 
			
		||||
	border-radius: 40px;
 | 
			
		||||
	display: flex;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	transition: linear;
 | 
			
		||||
	transition-duration: 0.4s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#themeToggleInside {
 | 
			
		||||
	background-color: rgb(255, 255, 255);
 | 
			
		||||
	border-radius: 30px;
 | 
			
		||||
	width: 22px;
 | 
			
		||||
	height: 22px;
 | 
			
		||||
	margin: 4px;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	transition: linear;
 | 
			
		||||
	transition-duration: 0.4s;
 | 
			
		||||
	left:0px;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,170 @@
 | 
			
		||||
const videoToggle = document.getElementById("videoToggle");
 | 
			
		||||
const audioToggle = document.getElementById("audioToggle");
 | 
			
		||||
const incorrectMsg = document.getElementById("incorrectMsg");
 | 
			
		||||
const loadingMsg = document.getElementById("loadingWrapper")
 | 
			
		||||
 | 
			
		||||
function getInfo() {
 | 
			
		||||
	incorrectMsg.textContent = "";
 | 
			
		||||
	loadingMsg.style.display = "flex";
 | 
			
		||||
	const url = document.getElementById("url").value;
 | 
			
		||||
	const options = {
 | 
			
		||||
		method: "POST",
 | 
			
		||||
		body: "url=" + url,
 | 
			
		||||
		headers: {
 | 
			
		||||
			"Content-Type": "Application/x-www-form-urlencoded",
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	fetch("/", options)
 | 
			
		||||
		.then((response) => response.json())
 | 
			
		||||
		.then((data) => {
 | 
			
		||||
			console.log(data.formats);
 | 
			
		||||
 | 
			
		||||
			const urlElements = document.querySelectorAll(".url");
 | 
			
		||||
 | 
			
		||||
			urlElements.forEach((element) => {
 | 
			
		||||
				element.value = data.url;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			if (data.status == true) {
 | 
			
		||||
				loadingMsg.style.display = "none";
 | 
			
		||||
				document.getElementById("hidden").style.display =
 | 
			
		||||
					"inline-block";
 | 
			
		||||
				document.getElementById("title").innerHTML =
 | 
			
		||||
					"<b>Title</b>: " + data.title;
 | 
			
		||||
				document.getElementById("videoList").style.display = "block";
 | 
			
		||||
				videoToggle.style.backgroundColor = "rgb(67, 212, 164)";
 | 
			
		||||
 | 
			
		||||
				for (let format of data.formats) {
 | 
			
		||||
					let sizeSplitted = (Number(format.contentLength) / 1000000)
 | 
			
		||||
						.toString().split(".")
 | 
			
		||||
					let size = sizeSplitted[0] + sizeSplitted[1].slice(0,1)
 | 
			
		||||
 | 
			
		||||
					size = size + " MB";
 | 
			
		||||
 | 
			
		||||
					// For videos
 | 
			
		||||
					if (format.hasVideo && format.contentLength && format.container == "mp4") {
 | 
			
		||||
 | 
			
		||||
							const itag = format.itag;
 | 
			
		||||
							const element =
 | 
			
		||||
								"<option value='" +
 | 
			
		||||
								itag +
 | 
			
		||||
								"'>" +
 | 
			
		||||
								format.qualityLabel +
 | 
			
		||||
								" | " +
 | 
			
		||||
								format.container +
 | 
			
		||||
								" | " +
 | 
			
		||||
								size +
 | 
			
		||||
								"</option>";
 | 
			
		||||
							document.getElementById(
 | 
			
		||||
								"videoFormatSelect"
 | 
			
		||||
							).innerHTML += element;
 | 
			
		||||
						
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// For audios
 | 
			
		||||
					else {
 | 
			
		||||
						const pattern = /^mp*4a[0-9.]+$/g;
 | 
			
		||||
						let audioCodec;
 | 
			
		||||
						const itag = format.itag;
 | 
			
		||||
 | 
			
		||||
						if (pattern.test(format.audioCodec)) {
 | 
			
		||||
							audioCodec = "mp4a";
 | 
			
		||||
						} else {
 | 
			
		||||
							audioCodec = format.audioCodec;
 | 
			
		||||
						}
 | 
			
		||||
						const element =
 | 
			
		||||
							"<option value='" +
 | 
			
		||||
							itag +
 | 
			
		||||
							"'>" +
 | 
			
		||||
							format.audioBitrate +
 | 
			
		||||
							" kbps" +
 | 
			
		||||
							" | " +
 | 
			
		||||
							audioCodec +
 | 
			
		||||
							" | " +
 | 
			
		||||
							size +
 | 
			
		||||
							"</option>";
 | 
			
		||||
						document.getElementById(
 | 
			
		||||
							"audioFormatSelect"
 | 
			
		||||
						).innerHTML += element;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				loadingMsg.style.display = "none";
 | 
			
		||||
				incorrectMsg.textContent = "Some error has occured";
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		.catch((error) => {
 | 
			
		||||
			if (error) {
 | 
			
		||||
				loadingMsg.style.display = "none";
 | 
			
		||||
				incorrectMsg.textContent = error;
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.getElementById("getInfo").addEventListener("click", (event) => {
 | 
			
		||||
	getInfo();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.getElementById("url").addEventListener("keypress", (event) => {
 | 
			
		||||
	if (event.key == "Enter") {
 | 
			
		||||
		getInfo();
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
videoToggle.addEventListener("click", (event) => {
 | 
			
		||||
	videoToggle.style.backgroundColor = "rgb(67, 212, 164)";
 | 
			
		||||
	audioToggle.style.backgroundColor = "rgb(108, 231, 190)";
 | 
			
		||||
	document.getElementById("audioList").style.display = "none";
 | 
			
		||||
	document.getElementById("videoList").style.display = "block";
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
audioToggle.addEventListener("click", (event) => {
 | 
			
		||||
	audioToggle.style.backgroundColor = "rgb(67, 212, 164)";
 | 
			
		||||
	videoToggle.style.backgroundColor = "rgb(108, 231, 190)";
 | 
			
		||||
	document.getElementById("videoList").style.display = "none";
 | 
			
		||||
	document.getElementById("audioList").style.display = "block";
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Toggle theme
 | 
			
		||||
let darkTheme = false;
 | 
			
		||||
let button = document.getElementById("themeToggle");
 | 
			
		||||
let circle = document.getElementById("themeToggleInside");
 | 
			
		||||
function toggle() {
 | 
			
		||||
	if (darkTheme == false) {
 | 
			
		||||
		circle.style.left = "25px";
 | 
			
		||||
		button.style.backgroundColor = "rgb(80, 193, 238)";
 | 
			
		||||
		darkTheme = true;
 | 
			
		||||
		document.body.style.backgroundColor = "rgb(50,50,50)";
 | 
			
		||||
		document.getElementById("hidden").style.backgroundColor = "rgb(143, 239, 207)"
 | 
			
		||||
		document.body.style.color = "whitesmoke";
 | 
			
		||||
		localStorage.setItem("theme", "dark");
 | 
			
		||||
	} else {
 | 
			
		||||
		circle.style.left = "0px";
 | 
			
		||||
		darkTheme = false;
 | 
			
		||||
		button.style.backgroundColor = "rgb(147, 174, 185)";
 | 
			
		||||
		document.body.style.backgroundColor = "whitesmoke";
 | 
			
		||||
		document.getElementById("hidden").style.backgroundColor = "rgb(203, 253, 236)"
 | 
			
		||||
		document.body.style.color = "black";
 | 
			
		||||
		localStorage.setItem("theme", "light");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const storageTheme = localStorage.getItem("theme");
 | 
			
		||||
 | 
			
		||||
if (storageTheme == "dark") {
 | 
			
		||||
	darkTheme = false;
 | 
			
		||||
	toggle();
 | 
			
		||||
} else if (storageTheme == "light") {
 | 
			
		||||
	darkTheme = true;
 | 
			
		||||
	toggle();
 | 
			
		||||
}
 | 
			
		||||
////
 | 
			
		||||
 | 
			
		||||
function clickAnimation(id) {
 | 
			
		||||
	document.getElementById(id).style.animationName = "clickAnimation";
 | 
			
		||||
 | 
			
		||||
	setTimeout(() => {
 | 
			
		||||
		document.getElementById("getInfo").style.animationName = "";
 | 
			
		||||
	}, 500);
 | 
			
		||||
}
 | 
			
		||||
											
												Binary file not shown.
											
										
									
								
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 279 KiB  | 
											
												Binary file not shown.
											
										
									
								| 
		 After Width: | Height: | Size: 4.8 KiB  | 
					Loading…
					
					
				
		Reference in New Issue