Added progress for video and audio, fixed some bugs

pull/9/head
aandrew-me 4 years ago
parent fd48878c10
commit c546960186

139
app.js

@ -1,19 +1,32 @@
//! Todo
//! Filename needs to be filtered
//! Total progress
//! Colours
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
const fs = require("fs");
const ytdl = require("ytdl-core");
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser")
const ffmpeg = require("ffmpeg-static");
const os = require("os");
const cp = require("child_process");
const os = require('os')
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + "/public"));
app.use(cookieParser())
// To do
// Preset
// Download location selection
// Choosing correct encoding
io.on("connection", (socket)=>{
socket.emit("id", socket.id)
})
app.get("/", (req, res) => {
@ -43,11 +56,12 @@ app.post("/", (req, res) => {
// Downloading video
app.post("/download", (req, res) => {
const socketId = req.cookies.id
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) {
@ -71,13 +85,13 @@ app.post("/download", (req, res) => {
} else {
quality = format.audioBitrate + "kbps";
}
const filename = title + "_" + quality + "." + extension
const filename = title + "_" + quality + "." + extension;
const info = {
format: format,
title: title,
extension: extension,
quality:quality,
filename:filename
quality: quality,
filename: filename,
};
return info;
}
@ -85,15 +99,28 @@ app.post("/download", (req, res) => {
findInfo(url, itag).then((info) => {
const format = info.format;
const filename = info.filename;
const extension = info.extension;
// If video
if (format.hasVideo) {
let video = ytdl(url, { quality: itag });
let video = ytdl(url, { quality: itag }).on(
"progress",
(_, downloaded, size) => {
const videoProgress = (downloaded / size) * 100;
io.to(socketId).emit("videoProgress", videoProgress)
}
);
let audio = ytdl(url, {
filter: "audioonly",
highWaterMark: 1 << 25,
}).on("progress", (_, downloaded, size) => {
const audioProgress = (downloaded / size) * 100;
io.to(socketId).emit("audioProgress", audioProgress)
});
res.header("Content-Disposition", `attachment; filename=.mp4`);
const ffmpegProcess = cp.spawn(
ffmpeg,
[
@ -101,21 +128,17 @@ app.post("/download", (req, res) => {
`pipe:3`,
"-i",
`pipe:4`,
"-map",
"0:v",
"-map",
"1:a",
"-c:v",
"-c",
"copy",
"-c:a",
"libmp3lame",
"-crf",
"27",
"-preset",
"fast",
// '-map','0:v',
// '-map','1:a',
// '-c:v', 'copy',
// '-c:a', 'copy',
// '-crf','27',
"-movflags",
"frag_keyframe+empty_moov",
'-f', "mp4",
"-f",
extension,
"-loglevel",
"error",
"-",
@ -127,74 +150,34 @@ app.post("/download", (req, res) => {
video.pipe(ffmpegProcess.stdio[3]);
audio.pipe(ffmpegProcess.stdio[4]);
ffmpegProcess.stdio[1].pipe(fs.createWriteStream(os.homedir() + "/Downloads/" + filename));
res.header("Content-Disposition", "attachment; filename='" + filename + "'");
ffmpegProcess.stdio[1].pipe(res);
}
// If audio
else {
res.header("Content-Disposition", "attachment; filename=" + filename);
// If audio
else {
res.header(
"Content-Disposition",
"attachment; filename=audio." + extension
);
ytdl(url, { quality: itag })
.on("progress", (_, downloaded, size) => {
const progress = (downloaded / size) * 100;
io.sockets.to(req.cookies.id).emit("audioProgress", progress)
})
// .pipe(fs.createWriteStream(os.homedir() + "/Downloads/" + filename))
.pipe(res);
.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;
app.post("/test", (req, res)=>{
console.log(req.body)
})
// 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, () => {
server.listen(3000, () => {
console.log("Server: http://localhost:3000");
});

@ -8,6 +8,7 @@
<title>Youtube Video Downloader</title>
<link rel="stylesheet" href="index.css">
<script src="index.js" defer></script>
</head>
<body>
@ -48,28 +49,49 @@
<p id="title">Title: </p>
<div id="videoList">
<form action="/download" method="post">
<label>Select Format - </label>
<select name="videoTag" id="videoFormatSelect">
<select id="videoFormatSelect">
</select>
<br>
<input type="hidden" name="url" class="url">
<button type="submit" class="submitBtn">Download</button>
</form>
<input type="hidden" name="url" class="url" id="url">
<button class="submitBtn" id="videoDownload">Download</button>
</div>
<div id="audioList">
<form action="/download" method="post">
<label>Select Format - </label>
<select name="audioTag" id="audioFormatSelect">
<select id="audioFormatSelect">
</select>
<br>
<input type="hidden" name="url" class="url">
<button type="submit" class="submitBtn" id="submitBtn" onclick="clickAnimation('submitBtn')">Download</button>
</form>
<button class="submitBtn" id="audioDownload">Download</button>
</div>
<label>Video Progress: <progress max="100" value="0" id="videoProgress"></progress></label>
<br>
<label>Audio Progress: <progress max="100" value="0" id="audioProgress"></progress></label>
<br>
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
let totalProgress = 0
const socket = io();
socket.on("id", (id) => {
document.cookie = "id=" + id + "; SameSite=Strict"
})
socket.on("videoProgress", (progress) => {
document.getElementById("videoProgress").value = progress
})
socket.on("audioProgress", (progress) => {
document.getElementById("audioProgress").value = progress
})
socket.on("message", (message) => console.log(message))
</script>
</body>
</html>

@ -1,12 +1,14 @@
{
"dependencies": {
"body-parser": "^1.20.0",
"cookie-parser": "^1.4.6",
"express": "^4.18.1",
"ffmpeg-static": "^5.0.2",
"socket.io": "^4.5.1",
"ytdl-core": "^4.11.0"
},
"name": "ytdownloader",
"version": "1.0.0",
"version": "1.1.0",
"main": "main.js",
"scripts": {
"start": "electron .",

@ -1,11 +1,13 @@
const videoToggle = document.getElementById("videoToggle");
const audioToggle = document.getElementById("audioToggle");
const incorrectMsg = document.getElementById("incorrectMsg");
const loadingMsg = document.getElementById("loadingWrapper")
const loadingMsg = document.getElementById("loadingWrapper");
function getInfo() {
incorrectMsg.textContent = "";
loadingMsg.style.display = "flex";
document.getElementById("videoFormatSelect").innerHTML = "";
document.getElementById("audioFormatSelect").innerHTML = "";
const url = document.getElementById("url").value;
const options = {
method: "POST",
@ -35,33 +37,56 @@ function getInfo() {
document.getElementById("videoList").style.display = "block";
videoToggle.style.backgroundColor = "rgb(67, 212, 164)";
let highestQualityLength = 0;
for (let format of data.formats) {
let size = (Number(format.contentLength) / 1000000).toFixed(2)
let size = (Number(format.contentLength) / 1000000).toFixed(
2
);
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;
if (
format.hasVideo &&
format.contentLength &&
!format.hasAudio
) {
const itag = format.itag;
const avcPattern = /^avc1[0-9a-zA-Z.]+$/g;
const av1Pattern = /^av01[0-9a-zA-Z.]+$/g;
let codec;
if (av1Pattern.test(format.codecs)) {
codec = "AV1 Codec";
} else if (avcPattern.test(format.codecs)) {
codec = "AVC Codec";
} else {
codec = format.codecs.toUpperCase() + " Codec";
}
const element =
"<option value='" +
itag +
"'>" +
format.qualityLabel +
" | " +
format.container +
" | " +
size +
" | " +
codec;
("</option>");
document.getElementById(
"videoFormatSelect"
).innerHTML += element;
}
// For audios
else if(format.hasAudio && !format.hasVideo && format.audioBitrate)
{
else if (
format.hasAudio &&
!format.hasVideo &&
format.audioBitrate
) {
const pattern = /^mp*4a[0-9.]+$/g;
let audioCodec;
const itag = format.itag;
@ -89,7 +114,8 @@ function getInfo() {
}
} else {
loadingMsg.style.display = "none";
incorrectMsg.textContent = "Some error has occured";
incorrectMsg.textContent =
"Some error has occured. Check your connection or the URL";
}
})
.catch((error) => {
@ -100,6 +126,55 @@ function getInfo() {
});
}
function download(type) {
const url = document.getElementById("url").value;
let itag;
let options;
if (type === "video") {
itag = document.getElementById("videoFormatSelect").value;
options = {
method: "POST",
body: new URLSearchParams({
url: url,
videoTag: itag,
}),
headers: {
"Content-Type": "Application/x-www-form-urlencoded",
},
};
} else {
itag = document.getElementById("audioFormatSelect").value;
options = {
method: "POST",
body: new URLSearchParams({
url: url,
audioTag: itag,
}),
headers: {
"Content-Type": "Application/x-www-form-urlencoded",
},
};
}
fetch("/download", options)
.then((response) => response.json)
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
});
}
document.getElementById("videoDownload").addEventListener("click", (event) => {
clickAnimation("videoDownload");
download("video");
});
document.getElementById("audioDownload").addEventListener("click", (event) => {
clickAnimation("audioDownload");
download("audio");
});
document.getElementById("getInfo").addEventListener("click", (event) => {
getInfo();
});
@ -134,7 +209,8 @@ function toggle() {
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.getElementById("hidden").style.backgroundColor =
"rgb(143, 239, 207)";
document.body.style.color = "whitesmoke";
localStorage.setItem("theme", "dark");
} else {
@ -142,7 +218,8 @@ function toggle() {
darkTheme = false;
button.style.backgroundColor = "rgb(147, 174, 185)";
document.body.style.backgroundColor = "whitesmoke";
document.getElementById("hidden").style.backgroundColor = "rgb(203, 253, 236)"
document.getElementById("hidden").style.backgroundColor =
"rgb(203, 253, 236)";
document.body.style.color = "black";
localStorage.setItem("theme", "light");
}
@ -163,6 +240,6 @@ function clickAnimation(id) {
document.getElementById(id).style.animationName = "clickAnimation";
setTimeout(() => {
document.getElementById("getInfo").style.animationName = "";
document.getElementById(id).style.animationName = "";
}, 500);
}

Loading…
Cancel
Save