mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
download progress bar on taskbar
+ Get the best possible artwork
This commit is contained in:
@ -2,6 +2,7 @@ const CHANNEL = "downloader";
|
|||||||
const ACTIONS = {
|
const ACTIONS = {
|
||||||
ERROR: "error",
|
ERROR: "error",
|
||||||
METADATA: "metadata",
|
METADATA: "metadata",
|
||||||
|
PROGRESS: "progress",
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@ -6,10 +6,11 @@ const { dialog, ipcMain } = require("electron");
|
|||||||
|
|
||||||
const getSongInfo = require("../../providers/song-info");
|
const getSongInfo = require("../../providers/song-info");
|
||||||
const { injectCSS, listenAction } = require("../utils");
|
const { injectCSS, listenAction } = require("../utils");
|
||||||
|
const { cropMaxWidth } = require("./utils");
|
||||||
const { ACTIONS, CHANNEL } = require("./actions.js");
|
const { ACTIONS, CHANNEL } = require("./actions.js");
|
||||||
const { getImage } = require("../../providers/song-info");
|
const { getImage } = require("../../providers/song-info");
|
||||||
|
|
||||||
const sendError = (win, err) => {
|
const sendError = (err) => {
|
||||||
const dialogOpts = {
|
const dialogOpts = {
|
||||||
type: "info",
|
type: "info",
|
||||||
buttons: ["OK"],
|
buttons: ["OK"],
|
||||||
@ -17,6 +18,7 @@ const sendError = (win, err) => {
|
|||||||
message: "Argh! Apologies, download failed…",
|
message: "Argh! Apologies, download failed…",
|
||||||
detail: err.toString(),
|
detail: err.toString(),
|
||||||
};
|
};
|
||||||
|
win.setProgressBar(-1); // close progress bar
|
||||||
dialog.showMessageBox(dialogOpts);
|
dialog.showMessageBox(dialogOpts);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,14 +31,17 @@ function handle(win) {
|
|||||||
metadata = info;
|
metadata = info;
|
||||||
});
|
});
|
||||||
|
|
||||||
listenAction(CHANNEL, (event, action, error) => {
|
listenAction(CHANNEL, (event, action, arg) => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ACTIONS.ERROR:
|
case ACTIONS.ERROR: //arg = error
|
||||||
sendError(win, error);
|
sendError(arg);
|
||||||
break;
|
break;
|
||||||
case ACTIONS.METADATA:
|
case ACTIONS.METADATA:
|
||||||
event.returnValue = JSON.stringify(metadata);
|
event.returnValue = JSON.stringify(metadata);
|
||||||
break;
|
break;
|
||||||
|
case ACTIONS.PROGRESS: //arg = progress
|
||||||
|
win.setProgressBar(arg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("Unknown action: " + action);
|
console.log("Unknown action: " + action);
|
||||||
}
|
}
|
||||||
@ -46,7 +51,7 @@ function handle(win) {
|
|||||||
let fileBuffer = songBuffer;
|
let fileBuffer = songBuffer;
|
||||||
|
|
||||||
if (currentMetadata.imageSrc) {
|
if (currentMetadata.imageSrc) {
|
||||||
currentMetadata.image = await getImage(currentMetadata.imageSrc);
|
currentMetadata.image = cropMaxWidth(await getImage(currentMetadata.imageSrc));
|
||||||
}
|
}
|
||||||
|
|
||||||
const songMetadata = { ...metadata, ...currentMetadata };
|
const songMetadata = { ...metadata, ...currentMetadata };
|
||||||
@ -69,7 +74,7 @@ function handle(win) {
|
|||||||
writer.addTag();
|
writer.addTag();
|
||||||
fileBuffer = Buffer.from(writer.arrayBuffer);
|
fileBuffer = Buffer.from(writer.arrayBuffer);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
sendError(win, error);
|
sendError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileSync(filePath, fileBuffer);
|
writeFileSync(filePath, fileBuffer);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ const observer = new MutationObserver((mutations, observer) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const reinit = () => {
|
const reinit = () => {
|
||||||
|
triggerAction(CHANNEL, ACTIONS.PROGRESS, -1); // closes progress bar
|
||||||
if (!progress) {
|
if (!progress) {
|
||||||
console.warn("Cannot update progress");
|
console.warn("Cannot update progress");
|
||||||
} else {
|
} else {
|
||||||
@ -38,6 +39,7 @@ const baseUrl = defaultConfig.url;
|
|||||||
// contextBridge.exposeInMainWorld("downloader", {
|
// contextBridge.exposeInMainWorld("downloader", {
|
||||||
// download: () => {
|
// download: () => {
|
||||||
global.download = () => {
|
global.download = () => {
|
||||||
|
triggerAction(CHANNEL, ACTIONS.PROGRESS, 2); // starts with indefinite progress bar
|
||||||
let metadata;
|
let metadata;
|
||||||
let videoUrl = getSongMenu()
|
let videoUrl = getSongMenu()
|
||||||
?.querySelector('ytmusic-menu-navigation-item-renderer.iron-selected[tabindex="0"]')
|
?.querySelector('ytmusic-menu-navigation-item-renderer.iron-selected[tabindex="0"]')
|
||||||
@ -53,12 +55,15 @@ global.download = () => {
|
|||||||
|
|
||||||
downloadVideoToMP3(
|
downloadVideoToMP3(
|
||||||
videoUrl,
|
videoUrl,
|
||||||
(feedback) => {
|
(feedback, ratio = undefined) => {
|
||||||
if (!progress) {
|
if (!progress) {
|
||||||
console.warn("Cannot update progress");
|
console.warn("Cannot update progress");
|
||||||
} else {
|
} else {
|
||||||
progress.innerHTML = feedback;
|
progress.innerHTML = feedback;
|
||||||
}
|
}
|
||||||
|
if (ratio) {
|
||||||
|
triggerAction(CHANNEL, ACTIONS.PROGRESS, ratio);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
triggerAction(CHANNEL, ACTIONS.ERROR, error);
|
triggerAction(CHANNEL, ACTIONS.ERROR, error);
|
||||||
|
|||||||
@ -32,7 +32,7 @@ module.exports = (win, options) => {
|
|||||||
const currentURL = metadataURL || win.webContents.getURL();
|
const currentURL = metadataURL || win.webContents.getURL();
|
||||||
const playlistID = new URL(currentURL).searchParams.get("list");
|
const playlistID = new URL(currentURL).searchParams.get("list");
|
||||||
if (!playlistID) {
|
if (!playlistID) {
|
||||||
sendError(win, new Error("No playlist ID found"));
|
sendError(new Error("No playlist ID found"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,6 @@ module.exports = (win, options) => {
|
|||||||
const playlistFolder = join(folder, playlistTitle);
|
const playlistFolder = join(folder, playlistTitle);
|
||||||
if (existsSync(playlistFolder)) {
|
if (existsSync(playlistFolder)) {
|
||||||
sendError(
|
sendError(
|
||||||
win,
|
|
||||||
new Error(`The folder ${playlistFolder} already exists`)
|
new Error(`The folder ${playlistFolder} already exists`)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -3,3 +3,33 @@ const electron = require("electron");
|
|||||||
module.exports.getFolder = (customFolder) =>
|
module.exports.getFolder = (customFolder) =>
|
||||||
customFolder || (electron.app || electron.remote.app).getPath("downloads");
|
customFolder || (electron.app || electron.remote.app).getPath("downloads");
|
||||||
module.exports.defaultMenuDownloadLabel = "Download playlist";
|
module.exports.defaultMenuDownloadLabel = "Download playlist";
|
||||||
|
|
||||||
|
module.exports.UrlToJPG = (imgUrl, videoId) => {
|
||||||
|
if (!imgUrl || imgUrl.includes(".jpg")) return imgUrl;
|
||||||
|
if (imgUrl.includes("maxresdefault")) {
|
||||||
|
return "https://img.youtube.com/vi/"+videoId+"/maxresdefault.jpg";
|
||||||
|
}
|
||||||
|
if (imgUrl.includes("hqdefault")) {
|
||||||
|
return "https://img.youtube.com/vi/"+videoId+"/hqdefault.jpg";
|
||||||
|
} //it will almost never get further than hq
|
||||||
|
if (imgUrl.includes("mqdefault")) {
|
||||||
|
return "https://img.youtube.com/vi/"+videoId+"/mqdefault.jpg";
|
||||||
|
}
|
||||||
|
if (imgUrl.includes("sdddefault")) {
|
||||||
|
return "https://img.youtube.com/vi/"+videoId+"/sdddefault.jpg";
|
||||||
|
}
|
||||||
|
return "https://img.youtube.com/vi/"+videoId+"/default.jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.cropMaxWidth = (image) => {
|
||||||
|
const imageSize = image.getSize();
|
||||||
|
if (imageSize.width === 1280 && imageSize.height === 720) {
|
||||||
|
return image.crop({
|
||||||
|
x: 280,
|
||||||
|
y: 0,
|
||||||
|
width: 720,
|
||||||
|
height: 720
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const ytdl = require("ytdl-core");
|
|||||||
|
|
||||||
const { triggerAction, triggerActionSync } = require("../utils");
|
const { triggerAction, triggerActionSync } = require("../utils");
|
||||||
const { ACTIONS, CHANNEL } = require("./actions.js");
|
const { ACTIONS, CHANNEL } = require("./actions.js");
|
||||||
const { getFolder } = require("./utils");
|
const { getFolder, UrlToJPG } = require("./utils");
|
||||||
const { cleanupArtistName } = require("../../providers/song-info");
|
const { cleanupArtistName } = require("../../providers/song-info");
|
||||||
|
|
||||||
const { createFFmpeg } = FFmpeg;
|
const { createFFmpeg } = FFmpeg;
|
||||||
@ -37,12 +37,14 @@ const downloadVideoToMP3 = async (
|
|||||||
sendFeedback("Downloading…");
|
sendFeedback("Downloading…");
|
||||||
|
|
||||||
if (metadata === null) {
|
if (metadata === null) {
|
||||||
const info = await ytdl.getInfo(videoUrl);
|
const { videoDetails } = await ytdl.getInfo(videoUrl);
|
||||||
const thumbnails = info.videoDetails?.author?.thumbnails;
|
const thumbnails = videoDetails?.thumbnails;
|
||||||
metadata = {
|
metadata = {
|
||||||
artist: info.videoDetails?.media?.artist || cleanupArtistName(info.videoDetails?.author?.name) || "",
|
artist: videoDetails?.media?.artist || cleanupArtistName(videoDetails?.author?.name) || "",
|
||||||
title: info.videoDetails?.media?.song || info.videoDetails?.title || "",
|
title: videoDetails?.media?.song || videoDetails?.title || "",
|
||||||
imageSrc: thumbnails ? thumbnails[thumbnails.length - 1].url : ""
|
imageSrc: thumbnails ?
|
||||||
|
UrlToJPG(thumbnails[thumbnails.length - 1].url, videoDetails?.videoId)
|
||||||
|
: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,9 +67,10 @@ const downloadVideoToMP3 = async (
|
|||||||
.on("data", (chunk) => {
|
.on("data", (chunk) => {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
})
|
})
|
||||||
.on("progress", (chunkLength, downloaded, total) => {
|
.on("progress", (_chunkLength, downloaded, total) => {
|
||||||
const progress = Math.floor((downloaded / total) * 100);
|
const ratio = downloaded / total;
|
||||||
sendFeedback("Download: " + progress + "%");
|
const progress = Math.floor(ratio * 100);
|
||||||
|
sendFeedback("Download: " + progress + "%", ratio);
|
||||||
})
|
})
|
||||||
.on("info", (info, format) => {
|
.on("info", (info, format) => {
|
||||||
videoName = info.videoDetails.title.replace("|", "").toString("ascii");
|
videoName = info.videoDetails.title.replace("|", "").toString("ascii");
|
||||||
@ -112,7 +115,7 @@ const toMP3 = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (!ffmpeg.isLoaded()) {
|
if (!ffmpeg.isLoaded()) {
|
||||||
sendFeedback("Loading…");
|
sendFeedback("Loading…", 2); // indefinite progress bar after download
|
||||||
await ffmpeg.load();
|
await ffmpeg.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user