From b04e2ea13075b667856b96763f59c8829fda9faf Mon Sep 17 00:00:00 2001 From: TC Date: Fri, 8 Jan 2021 22:26:44 +0100 Subject: [PATCH 01/11] Defensive: handle null/undefined ffmpeg args --- plugins/downloader/youtube-dl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/downloader/youtube-dl.js b/plugins/downloader/youtube-dl.js index 3b0c7688..890659f3 100644 --- a/plugins/downloader/youtube-dl.js +++ b/plugins/downloader/youtube-dl.js @@ -87,7 +87,7 @@ const toMP3 = async ( await ffmpeg.run( "-i", safeVideoName, - ...options.ffmpegArgs, + ...(options.ffmpegArgs || []), safeVideoName + ".mp3" ); From 6a100c8cb16c36f567439523004dde9bae41213d Mon Sep 17 00:00:00 2001 From: TC Date: Fri, 8 Jan 2021 22:29:25 +0100 Subject: [PATCH 02/11] Allow custom audio extensions in downloader --- plugins/downloader/youtube-dl.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/downloader/youtube-dl.js b/plugins/downloader/youtube-dl.js index 890659f3..e2f57575 100644 --- a/plugins/downloader/youtube-dl.js +++ b/plugins/downloader/youtube-dl.js @@ -73,6 +73,7 @@ const toMP3 = async ( options ) => { const safeVideoName = randomBytes(32).toString("hex"); + const extension = options.extension || "mp3"; try { if (!ffmpeg.isLoaded()) { @@ -88,14 +89,16 @@ const toMP3 = async ( "-i", safeVideoName, ...(options.ffmpegArgs || []), - safeVideoName + ".mp3" + safeVideoName + "." + extension ); const folder = options.downloadFolder || downloadsFolder(); - const filename = filenamify(videoName + ".mp3", { replacement: "_" }); + const filename = filenamify(videoName + "." + extension, { + replacement: "_", + }); writeFileSync( join(folder, filename), - ffmpeg.FS("readFile", safeVideoName + ".mp3") + ffmpeg.FS("readFile", safeVideoName + "." + extension) ); reinit(); From a9a840b6c3158536f5c4d3927d74a1b9dda9604f Mon Sep 17 00:00:00 2001 From: TC Date: Fri, 8 Jan 2021 22:41:17 +0100 Subject: [PATCH 03/11] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c24d6069..00beabb7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "youtube-music", "productName": "YouTube Music", - "version": "1.8.0", + "version": "1.8.1", "description": "YouTube Music Desktop App - including custom plugins", "license": "MIT", "repository": "th-ch/youtube-music", From a8ce87f2ccb4f0fdbd36676883e6a0497bebc263 Mon Sep 17 00:00:00 2001 From: semvis123 Date: Tue, 12 Jan 2021 20:16:49 +0100 Subject: [PATCH 04/11] Added Discord rich presence and added extra properties to songinfo provider --- package.json | 9 +++++++- plugins/discord-rpc/back.js | 41 +++++++++++++++++++++++++++++++++++++ providers/song-info/back.js | 23 ++++++++++++++++++++- yarn.lock | 13 ++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 plugins/discord-rpc/back.js diff --git a/package.json b/package.json index 21de8721..8a6afb8d 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,13 @@ "linux": { "icon": "assets/generated/icons/png", "category": "AudioVideo", - "target": ["AppImage", "snap", "freebsd", "deb", "rpm"] + "target": [ + "AppImage", + "snap", + "freebsd", + "deb", + "rpm" + ] } }, "scripts": { @@ -53,6 +59,7 @@ "@ffmpeg/core": "^0.8.4", "@ffmpeg/ffmpeg": "^0.9.5", "YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.0", + "discord-rpc": "^3.1.4", "downloads-folder": "^3.0.1", "electron-debug": "^3.1.0", "electron-is": "^3.0.0", diff --git a/plugins/discord-rpc/back.js b/plugins/discord-rpc/back.js new file mode 100644 index 00000000..dbf3c6e0 --- /dev/null +++ b/plugins/discord-rpc/back.js @@ -0,0 +1,41 @@ +const DiscordRPC = require('discord-rpc'); +const rpc = new DiscordRPC.Client({ + transport: 'ipc' +}); + +const clientId = '790655993809338398'; + +module.exports = win => { + // If the page is ready, register the callback + win.on('ready-to-show', () => { + // Startup the rpc client + rpc.login({ + clientId + }).catch(console.error); + + // Register the callback + global.songInfo.onNewData(songInfo => { + // Song information changed, so lets update the rich presence + + const activityInfo = { + details: songInfo.title, + state: songInfo.artist, + largeImageKey: 'logo', + largeImageText: songInfo.views + ' - ' + songInfo.likes + }; + + if (songInfo.isPaused) { + // Add an idle icon to show that the song is paused + activityInfo.smallImageKey = 'idle'; + activityInfo.smallImageText = 'idle/paused'; + } else { + // Add the start and end time of the song + const songStartTime = Date.now() - (songInfo.elapsedSeconds * 1000); + activityInfo.startTimestamp = songStartTime; + activityInfo.endTimestamp = songStartTime + (songInfo.songDuration * 1000); + } + + rpc.setActivity(activityInfo); + }); + }); +}; diff --git a/providers/song-info/back.js b/providers/song-info/back.js index c7c78a19..117ec947 100644 --- a/providers/song-info/back.js +++ b/providers/song-info/back.js @@ -10,6 +10,9 @@ const imageSelector = '#layout > ytmusic-player-bar > div.middle-controls.style- // This selects the song subinfo, this includes artist, views, likes const subInfoSelector = '#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > div.content-info-wrapper.style-scope.ytmusic-player-bar > span'; +// This selects the progress bar, used for songlength and current progress +const progressSelector = '#progress-bar'; + // This is used for to control the songs const presskey = (window, key) => { window.webContents.sendInputEvent({ @@ -55,6 +58,18 @@ const getSubInfo = async win => { return subInfo; }; +// Grab the progress using the selector +const getProgress = async win => { + // Get max value of the progressbar element + const songDuration = await win.webContents.executeJavaScript( + 'document.querySelector("' + progressSelector + '").max'); + // Get current value of the progressbar element + const elapsedSeconds = await win.webContents.executeJavaScript( + 'document.querySelector("' + progressSelector + '").value'); + + return {songDuration, elapsedSeconds}; +}; + // Grab the native image using the src const getImage = async src => { const result = await fetch(src); @@ -79,7 +94,9 @@ module.exports = win => { likes: '', imageSrc: '', image: null, - isPaused: true + isPaused: true, + songDuration: 0, + elapsedSeconds: 0 }; // The song control functions global.songControls = { @@ -102,6 +119,10 @@ module.exports = win => { global.songInfo.title = await getTitle(win); global.songInfo.isPaused = await getPausedStatus(win); + const {songDuration, elapsedSeconds} = await getProgress(win); + global.songInfo.songDuration = songDuration; + global.songInfo.elapsedSeconds = elapsedSeconds; + // If title changed then we do need to update other info if (oldTitle !== global.songInfo.title) { const subInfo = await getSubInfo(win); diff --git a/yarn.lock b/yarn.lock index cb496761..6ffdc3ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2885,6 +2885,14 @@ dir-glob@^2.2.2: dependencies: path-type "^3.0.0" +discord-rpc@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/discord-rpc/-/discord-rpc-3.1.4.tgz#6d449a682e6a0dec4f0444d5f36f9ebfabaccf91" + integrity sha512-QaBu+gHica2SzgRAmTpuJ4J8DX9+fDwAqhvaie3hcbkU9WPqewEPh21pWdd/7vTI/JNuapU7PFm2ZKg3BTkbGg== + dependencies: + node-fetch "^2.6.1" + ws "^7.3.1" + dmg-builder@22.8.1: version "22.8.1" resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.8.1.tgz#9b3bcbbc43e5fed232525d61a5567ea4b66085c3" @@ -8745,6 +8753,11 @@ ws@^7.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== +ws@^7.3.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd" + integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA== + xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" From 18df1223af830acdac3b66ac6bcdd6b4d171be6e Mon Sep 17 00:00:00 2001 From: TC Date: Tue, 12 Jan 2021 21:28:26 +0100 Subject: [PATCH 05/11] Add portable target to windows builds --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index af935450..6609f410 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "icon": "assets/generated/icons/mac/icon.icns" }, "win": { - "icon": "assets/generated/icons/win/icon.ico" + "icon": "assets/generated/icons/win/icon.ico", + "target": ["nsis", "portable"] }, "linux": { "icon": "assets/generated/icons/png", From 67c4422eb8ac23a01f53c6e496cbc2aa24953188 Mon Sep 17 00:00:00 2001 From: TC Date: Tue, 12 Jan 2021 21:37:02 +0100 Subject: [PATCH 06/11] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6609f410..9ae98213 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "youtube-music", "productName": "YouTube Music", - "version": "1.8.1", + "version": "1.8.2", "description": "YouTube Music Desktop App - including custom plugins", "license": "MIT", "repository": "th-ch/youtube-music", From 79e8fc2fac69858321f6ad3f2b922e650eaeaefb Mon Sep 17 00:00:00 2001 From: TC Date: Tue, 12 Jan 2021 22:52:21 +0100 Subject: [PATCH 07/11] Fix downloader plugin with context isolation --- plugins/downloader/front.js | 42 ++++++++++++---------- plugins/downloader/templates/download.html | 2 +- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/plugins/downloader/front.js b/plugins/downloader/front.js index 00459281..d235da65 100644 --- a/plugins/downloader/front.js +++ b/plugins/downloader/front.js @@ -1,3 +1,5 @@ +const { contextBridge } = require("electron"); + const { ElementFromFile, templatePath, triggerAction } = require("../utils"); const { ACTIONS, CHANNEL } = require("./actions.js"); const { downloadVideoToMP3 } = require("./youtube-dl"); @@ -28,26 +30,28 @@ const reinit = () => { } }; -global.download = () => { - const videoUrl = window.location.href; +contextBridge.exposeInMainWorld("downloader", { + download: () => { + const videoUrl = window.location.href; - downloadVideoToMP3( - videoUrl, - (feedback) => { - if (!progress) { - console.warn("Cannot update progress"); - } else { - progress.innerHTML = feedback; - } - }, - (error) => { - triggerAction(CHANNEL, ACTIONS.ERROR, error); - reinit(); - }, - reinit, - pluginOptions - ); -}; + downloadVideoToMP3( + videoUrl, + (feedback) => { + if (!progress) { + console.warn("Cannot update progress"); + } else { + progress.innerHTML = feedback; + } + }, + (error) => { + triggerAction(CHANNEL, ACTIONS.ERROR, error); + reinit(); + }, + reinit, + pluginOptions + ); + }, +}); function observeMenu(options) { pluginOptions = { ...pluginOptions, ...options }; diff --git a/plugins/downloader/templates/download.html b/plugins/downloader/templates/download.html index 138464fb..7f9ab3ce 100644 --- a/plugins/downloader/templates/download.html +++ b/plugins/downloader/templates/download.html @@ -4,7 +4,7 @@ tabindex="-1" aria-disabled="false" aria-selected="false" - onclick="download()" + onclick="downloader.download()" >