Skip to content

Commit eae95be

Browse files
committed
Merge branch 'master' of github.com:th-ch/youtube-music into refactor-providers
# By TC (9) and semvis123 (2) # Via GitHub (4) and semvis123 (1) * 'master' of github.com:th-ch/youtube-music: renamed DiscordRPC to Discord Downloader plugin: log audio bitrate Disable context isolation (to load ffmpeg wasm) Use contextBridge in preload script + update navigation plugin Fix downloader plugin with context isolation Bump version Add portable target to windows builds Added Discord rich presence and added extra properties to songinfo provider Bump version Allow custom audio extensions in downloader Defensive: handle null/undefined ffmpeg args
2 parents f7cbf2c + f0200e7 commit eae95be

File tree

10 files changed

+131
-27
lines changed

10 files changed

+131
-27
lines changed

index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ function createMainWindow() {
7575
backgroundColor: "#000",
7676
show: false,
7777
webPreferences: {
78-
contextIsolation: true,
78+
// TODO: re-enable contextIsolation once it can work with ffmepg.wasm
79+
// Possible bundling? https://github.com/ffmpegwasm/ffmpeg.wasm/issues/126
80+
contextIsolation: false,
7981
preload: path.join(__dirname, "preload.js"),
8082
nodeIntegrationInSubFrames: true,
8183
nativeWindowOpen: true, // window.open return Window object(like in regular browsers), not BrowserWindowProxy

package.json

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "youtube-music",
33
"productName": "YouTube Music",
4-
"version": "1.8.0",
4+
"version": "1.8.2",
55
"description": "YouTube Music Desktop App - including custom plugins",
66
"license": "MIT",
77
"repository": "th-ch/youtube-music",
@@ -18,12 +18,19 @@
1818
"icon": "assets/generated/icons/mac/icon.icns"
1919
},
2020
"win": {
21-
"icon": "assets/generated/icons/win/icon.ico"
21+
"icon": "assets/generated/icons/win/icon.ico",
22+
"target": ["nsis", "portable"]
2223
},
2324
"linux": {
2425
"icon": "assets/generated/icons/png",
2526
"category": "AudioVideo",
26-
"target": ["AppImage", "snap", "freebsd", "deb", "rpm"]
27+
"target": [
28+
"AppImage",
29+
"snap",
30+
"freebsd",
31+
"deb",
32+
"rpm"
33+
]
2734
}
2835
},
2936
"scripts": {
@@ -53,6 +60,7 @@
5360
"@ffmpeg/core": "^0.8.5",
5461
"@ffmpeg/ffmpeg": "^0.9.6",
5562
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.0",
63+
"discord-rpc": "^3.1.4",
5664
"downloads-folder": "^3.0.1",
5765
"electron-debug": "^3.1.0",
5866
"electron-is": "^3.0.0",

plugins/discord-rpc/back.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const Discord = require('discord-rpc');
2+
const rpc = new Discord.Client({
3+
transport: 'ipc'
4+
});
5+
6+
const clientId = '790655993809338398';
7+
8+
module.exports = win => {
9+
// If the page is ready, register the callback
10+
win.on('ready-to-show', () => {
11+
// Startup the rpc client
12+
rpc.login({
13+
clientId
14+
}).catch(console.error);
15+
16+
// Register the callback
17+
global.songInfo.onNewData(songInfo => {
18+
// Song information changed, so lets update the rich presence
19+
20+
const activityInfo = {
21+
details: songInfo.title,
22+
state: songInfo.artist,
23+
largeImageKey: 'logo',
24+
largeImageText: songInfo.views + ' - ' + songInfo.likes
25+
};
26+
27+
if (songInfo.isPaused) {
28+
// Add an idle icon to show that the song is paused
29+
activityInfo.smallImageKey = 'idle';
30+
activityInfo.smallImageText = 'idle/paused';
31+
} else {
32+
// Add the start and end time of the song
33+
const songStartTime = Date.now() - (songInfo.elapsedSeconds * 1000);
34+
activityInfo.startTimestamp = songStartTime;
35+
activityInfo.endTimestamp = songStartTime + (songInfo.songDuration * 1000);
36+
}
37+
38+
rpc.setActivity(activityInfo);
39+
});
40+
});
41+
};

plugins/downloader/front.js

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const { contextBridge } = require("electron");
2+
13
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
24
const { ACTIONS, CHANNEL } = require("./actions.js");
35
const { downloadVideoToMP3 } = require("./youtube-dl");
@@ -28,6 +30,9 @@ const reinit = () => {
2830
}
2931
};
3032

33+
// TODO: re-enable once contextIsolation is set to true
34+
// contextBridge.exposeInMainWorld("downloader", {
35+
// download: () => {
3136
global.download = () => {
3237
const videoUrl = window.location.href;
3338

@@ -48,6 +53,7 @@ global.download = () => {
4853
pluginOptions
4954
);
5055
};
56+
// });
5157

5258
function observeMenu(options) {
5359
pluginOptions = { ...pluginOptions, ...options };

plugins/downloader/youtube-dl.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ const downloadVideoToMP3 = (
5454
.on("info", (info, format) => {
5555
videoName = info.videoDetails.title.replace("|", "").toString("ascii");
5656
if (is.dev()) {
57-
console.log("Downloading video - name:", videoName);
57+
console.log(
58+
"Downloading video - name:",
59+
videoName,
60+
"- quality:",
61+
format.audioBitrate + "kbits/s"
62+
);
5863
}
5964
})
6065
.on("error", sendError)
@@ -73,6 +78,7 @@ const toMP3 = async (
7378
options
7479
) => {
7580
const safeVideoName = randomBytes(32).toString("hex");
81+
const extension = options.extension || "mp3";
7682

7783
try {
7884
if (!ffmpeg.isLoaded()) {
@@ -87,15 +93,17 @@ const toMP3 = async (
8793
await ffmpeg.run(
8894
"-i",
8995
safeVideoName,
90-
...options.ffmpegArgs,
91-
safeVideoName + ".mp3"
96+
...(options.ffmpegArgs || []),
97+
safeVideoName + "." + extension
9298
);
9399

94100
const folder = options.downloadFolder || downloadsFolder();
95-
const filename = filenamify(videoName + ".mp3", { replacement: "_" });
101+
const filename = filenamify(videoName + "." + extension, {
102+
replacement: "_",
103+
});
96104
writeFileSync(
97105
join(folder, filename),
98-
ffmpeg.FS("readFile", safeVideoName + ".mp3")
106+
ffmpeg.FS("readFile", safeVideoName + "." + extension)
99107
);
100108

101109
reinit();

plugins/navigation/actions.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
const { triggerAction } = require('../utils');
1+
const { triggerAction } = require("../utils");
22

33
const CHANNEL = "navigation";
44
const ACTIONS = {
5-
NEXT: "next",
6-
BACK: 'back',
7-
}
5+
NEXT: "next",
6+
BACK: "back",
7+
};
88

99
function goToNextPage() {
10-
triggerAction(CHANNEL, ACTIONS.NEXT);
10+
triggerAction(CHANNEL, ACTIONS.NEXT);
1111
}
1212

1313
function goToPreviousPage() {
14-
triggerAction(CHANNEL, ACTIONS.BACK);
14+
triggerAction(CHANNEL, ACTIONS.BACK);
1515
}
1616

1717
module.exports = {
18-
CHANNEL: CHANNEL,
19-
ACTIONS: ACTIONS,
20-
global: {
21-
goToNextPage: goToNextPage,
22-
goToPreviousPage: goToPreviousPage,
23-
}
18+
CHANNEL: CHANNEL,
19+
ACTIONS: ACTIONS,
20+
actions: {
21+
goToNextPage: goToNextPage,
22+
goToPreviousPage: goToPreviousPage,
23+
},
2424
};

plugins/navigation/back.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
const path = require("path");
22

33
const { injectCSS, listenAction } = require("../utils");
4-
const { ACTIONS, CHANNEL } = require("./actions.js");
4+
const { ACTIONS, CHANNEL } = require("./actions.js");
55

66
function handle(win) {
77
injectCSS(win.webContents, path.join(__dirname, "style.css"));
88
listenAction(CHANNEL, (event, action) => {
99
switch (action) {
10-
case ACTIONS.NEXT:
10+
case ACTIONS.NEXT:
1111
if (win.webContents.canGoForward()) {
1212
win.webContents.goForward();
1313
}
1414
break;
15-
case ACTIONS.BACK:
15+
case ACTIONS.BACK:
1616
if (win.webContents.canGoBack()) {
1717
win.webContents.goBack();
1818
}
1919
break;
20-
default:
20+
default:
2121
console.log("Unknown action: " + action);
2222
}
2323
});

preload.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const path = require("path");
22

3-
const { remote } = require("electron");
3+
const { contextBridge, remote } = require("electron");
44

55
const config = require("./config");
66
const { fileExists } = require("./plugins/utils");
@@ -10,7 +10,10 @@ const plugins = config.plugins.getEnabled();
1010
plugins.forEach(([plugin, options]) => {
1111
const pluginPath = path.join(__dirname, "plugins", plugin, "actions.js");
1212
fileExists(pluginPath, () => {
13-
const actions = require(pluginPath).global || {};
13+
const actions = require(pluginPath).actions || {};
14+
15+
// TODO: re-enable once contextIsolation is set to true
16+
// contextBridge.exposeInMainWorld(plugin + "Actions", actions);
1417
Object.keys(actions).forEach((actionName) => {
1518
global[actionName] = actions[actionName];
1619
});

providers/song-info.js

+23
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const imageSelector =
1313
const subInfoSelector =
1414
"#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > div.content-info-wrapper.style-scope.ytmusic-player-bar > span";
1515

16+
// This selects the progress bar, used for songlength and current progress
17+
const progressSelector = "#progress-bar";
18+
1619
// Grab the title using the selector
1720
const getTitle = (win) => {
1821
return win.webContents
@@ -53,6 +56,20 @@ const getSubInfo = async (win) => {
5356
return subInfo;
5457
};
5558

59+
// Grab the progress using the selector
60+
const getProgress = async (win) => {
61+
// Get max value of the progressbar element
62+
const songDuration = await win.webContents.executeJavaScript(
63+
'document.querySelector("' + progressSelector + '").max'
64+
);
65+
// Get current value of the progressbar element
66+
const elapsedSeconds = await win.webContents.executeJavaScript(
67+
'document.querySelector("' + progressSelector + '").value'
68+
);
69+
70+
return { songDuration, elapsedSeconds };
71+
};
72+
5673
// Grab the native image using the src
5774
const getImage = async (src) => {
5875
const result = await fetch(src);
@@ -74,6 +91,8 @@ const songInfo = {
7491
imageSrc: "",
7592
image: null,
7693
isPaused: true,
94+
songDuration: 0,
95+
elapsedSeconds: 0,
7796
};
7897

7998
const registerProvider = (win) => {
@@ -92,6 +111,10 @@ const registerProvider = (win) => {
92111
songInfo.title = await getTitle(win);
93112
songInfo.isPaused = await getPausedStatus(win);
94113

114+
const { songDuration, elapsedSeconds } = await getProgress(win);
115+
songInfo.songDuration = songDuration;
116+
songInfo.elapsedSeconds = elapsedSeconds;
117+
95118
// If title changed then we do need to update other info
96119
if (oldTitle !== songInfo.title) {
97120
const subInfo = await getSubInfo(win);

yarn.lock

+13
Original file line numberDiff line numberDiff line change
@@ -2911,6 +2911,14 @@ dir-glob@^2.2.2:
29112911
dependencies:
29122912
path-type "^3.0.0"
29132913

2914+
discord-rpc@^3.1.4:
2915+
version "3.1.4"
2916+
resolved "https://registry.yarnpkg.com/discord-rpc/-/discord-rpc-3.1.4.tgz#6d449a682e6a0dec4f0444d5f36f9ebfabaccf91"
2917+
integrity sha512-QaBu+gHica2SzgRAmTpuJ4J8DX9+fDwAqhvaie3hcbkU9WPqewEPh21pWdd/7vTI/JNuapU7PFm2ZKg3BTkbGg==
2918+
dependencies:
2919+
node-fetch "^2.6.1"
2920+
ws "^7.3.1"
2921+
29142922
29152923
version "22.8.1"
29162924
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.8.1.tgz#9b3bcbbc43e5fed232525d61a5567ea4b66085c3"
@@ -8773,6 +8781,11 @@ ws@^7.2.3:
87738781
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
87748782
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
87758783

8784+
ws@^7.3.1:
8785+
version "7.4.2"
8786+
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd"
8787+
integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==
8788+
87768789
xdg-basedir@^4.0.0:
87778790
version "4.0.0"
87788791
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"

0 commit comments

Comments
 (0)