mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-07-29 18:25:47 +02:00
Feat: Enhance download experience with progress indicators and toast notifications
This commit is contained in:
@@ -5,6 +5,7 @@ const { notifyDownloadFinished } = require("../helpers/notify.helpers");
|
||||
|
||||
const listeners = [];
|
||||
const speedListeners = [];
|
||||
const stageListeners = [];
|
||||
|
||||
async function downloadController(req, res) {
|
||||
try {
|
||||
@@ -15,16 +16,16 @@ async function downloadController(req, res) {
|
||||
outputFolder: req.body.savePath || undefined,
|
||||
};
|
||||
|
||||
if (!options.url || !isValidUrl(options.url)) return res.status(400).send("❌ Invalid URL !");
|
||||
if (!options.url || !isValidUrl(options.url)) return res.status(400).send("Invalid URL !");
|
||||
if (options.outputFolder && !isSafePath(options.outputFolder)) {
|
||||
logger.warn(`Unsafe download path rejected: ${options.outputFolder}`);
|
||||
return res.status(400).send("❌ Save Path Not Allowed.");
|
||||
}
|
||||
|
||||
// Get output folder when the download is finished
|
||||
const outputFolder = await fetchDownload(options, listeners, speedListeners);
|
||||
const outputFolder = await fetchDownload(options, listeners, speedListeners, stageListeners);
|
||||
notifyDownloadFinished(outputFolder);
|
||||
res.send("✅ Download Done !");
|
||||
res.send("Download Done !");
|
||||
|
||||
} catch (err) {
|
||||
logger.error(`Server Error in /download : ${err.message}`);
|
||||
@@ -64,6 +65,22 @@ function speedController(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
function stageController(req, res) {
|
||||
res.set({
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
});
|
||||
|
||||
const sendStage = stage => res.write(`data: ${stage}\n\n`);
|
||||
stageListeners.push(sendStage);
|
||||
|
||||
req.on('close', () => {
|
||||
stageListeners.splice(stageListeners.indexOf(sendStage), 1);
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
function cancelDownloadController(req, res) {
|
||||
const cancelled = cancelDownload();
|
||||
if (cancelled) {
|
||||
@@ -75,4 +92,4 @@ function cancelDownloadController(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { downloadController, progressController, speedController, cancelDownloadController};
|
||||
module.exports = { downloadController, progressController, speedController, stageController, cancelDownloadController};
|
||||
|
||||
@@ -10,7 +10,7 @@ async function infoController(req, res){
|
||||
|
||||
// If no url, non-string url or invalid url
|
||||
|
||||
if (!url || typeof url !== "string" || !isValidUrl(url)) return res.status(400).send("❌ Invalid URL Or Missing");
|
||||
if (!url || typeof url !== "string" || !isValidUrl(url)) return res.status(400).send("Invalid URL Or Missing");
|
||||
|
||||
logger.info(`/Info Request receive by the Info Controller. URL: ${url}`);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const { downloadController, progressController, speedController, cancelDownloadController } = require("../controller/download.controller");
|
||||
const { downloadController, progressController, speedController, stageController, cancelDownloadController } = require("../controller/download.controller");
|
||||
|
||||
router.post("/", downloadController);
|
||||
router.post("/cancel", cancelDownloadController);
|
||||
@@ -8,5 +8,6 @@ router.post("/cancel", cancelDownloadController);
|
||||
// SSE for download progress
|
||||
router.get("/progress", progressController);
|
||||
router.get("/speed", speedController);
|
||||
router.get("/stage", stageController);
|
||||
|
||||
module.exports = router;
|
||||
@@ -9,7 +9,7 @@ const { isSafePath } = require("../helpers/validation.helpers");
|
||||
|
||||
let currentDownloadProcess = null;
|
||||
|
||||
function fetchDownload(options, listeners, speedListeners) {
|
||||
function fetchDownload(options, listeners, speedListeners, stageListeners) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const outputFolder = options.outputFolder || defaultDownloadFolder;
|
||||
@@ -54,7 +54,10 @@ function fetchDownload(options, listeners, speedListeners) {
|
||||
logger.info(`[yt-dlp] ${line}`);
|
||||
|
||||
// Progress Bar
|
||||
if (line.startsWith("[download] Destination:")) listeners.forEach(fn => fn("reset"));
|
||||
if (line.startsWith("[download] Destination:")) {
|
||||
listeners.forEach(fn => fn("reset"));
|
||||
stageListeners.forEach(fn => fn("Downloading..."));
|
||||
}
|
||||
const match = line.match(/\[download\]\s+(\d+\.\d+)%/);
|
||||
if (match) listeners.forEach(fn => fn(parseFloat(match[1])));
|
||||
|
||||
@@ -66,6 +69,23 @@ function fetchDownload(options, listeners, speedListeners) {
|
||||
}
|
||||
}
|
||||
|
||||
// Stage messages
|
||||
if (line.includes("[Merger]")) {
|
||||
stageListeners.forEach(fn => fn("Merging formats..."));
|
||||
}
|
||||
if (line.includes("[ExtractAudio]")) {
|
||||
stageListeners.forEach(fn => fn("Extracting audio..."));
|
||||
}
|
||||
if (line.includes("[Metadata]")) {
|
||||
stageListeners.forEach(fn => fn("Adding metadata..."));
|
||||
}
|
||||
if (line.includes("[ThumbnailsConvertor]")) {
|
||||
stageListeners.forEach(fn => fn("Converting thumbnail..."));
|
||||
}
|
||||
if (line.includes("[EmbedThumbnail]")) {
|
||||
stageListeners.forEach(fn => fn("Embedding thumbnail..."));
|
||||
}
|
||||
|
||||
if (line.match(/ERROR: Could not copy .* cookie database/i)) {
|
||||
notify.notifyCookiesBrowserError();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user