Skip to content

Commit bcff6e5

Browse files
committed
Add notifications plugin (notify of song on play event)
1 parent 1dcf76b commit bcff6e5

File tree

4 files changed

+139
-2
lines changed

4 files changed

+139
-2
lines changed

plugins/notifications/actions.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { triggerAction } = require("../utils");
2+
3+
const CHANNEL = "notification";
4+
const ACTIONS = {
5+
NOTIFICATION: "notification",
6+
};
7+
8+
function notify(info) {
9+
triggerAction(CHANNEL, ACTIONS.NOTIFICATION, info);
10+
}
11+
12+
module.exports = {
13+
CHANNEL,
14+
ACTIONS,
15+
global: {
16+
notify,
17+
},
18+
};

plugins/notifications/back.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const { nativeImage, Notification } = require("electron");
2+
3+
const { listenAction } = require("../utils");
4+
const { ACTIONS, CHANNEL } = require("./actions.js");
5+
6+
function notify(info) {
7+
let notificationImage = "assets/youtube-music.png";
8+
if (info.image) {
9+
notificationImage = nativeImage.createFromDataURL(info.image);
10+
}
11+
12+
const notification = {
13+
title: info.title || "Playing",
14+
body: info.artist,
15+
icon: notificationImage,
16+
silent: true,
17+
};
18+
new Notification(notification).show();
19+
}
20+
21+
function listenAndNotify() {
22+
listenAction(CHANNEL, (event, action, imageSrc) => {
23+
switch (action) {
24+
case ACTIONS.NOTIFICATION:
25+
notify(imageSrc);
26+
break;
27+
default:
28+
console.log("Unknown action: " + action);
29+
}
30+
});
31+
}
32+
33+
module.exports = listenAndNotify;

plugins/notifications/front.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
let videoElement = null;
2+
let image = null;
3+
4+
const observer = new MutationObserver((mutations, observer) => {
5+
if (!videoElement) {
6+
videoElement = document.querySelector("video");
7+
}
8+
9+
if (!image) {
10+
image = document.querySelector(".ytmusic-player-bar.image");
11+
}
12+
13+
if (videoElement !== null && image !== null) {
14+
observer.disconnect();
15+
let notificationImage = null;
16+
17+
videoElement.addEventListener("play", () => {
18+
notify({
19+
title: getTitle(),
20+
artist: getArtist(),
21+
image: notificationImage,
22+
});
23+
});
24+
25+
image.addEventListener("load", () => {
26+
notificationImage = null;
27+
const imageInBase64 = convertImageToBase64(image);
28+
if (image && image.complete && image.naturalHeight !== 0) {
29+
notificationImage = imageInBase64;
30+
}
31+
});
32+
}
33+
});
34+
35+
// Convert an image (DOM element) to base64 string
36+
const convertImageToBase64 = (image, size = 256) => {
37+
image.setAttribute("crossorigin", "anonymous");
38+
39+
const c = document.createElement("canvas");
40+
c.height = size;
41+
c.width = size;
42+
43+
const ctx = c.getContext("2d");
44+
ctx.drawImage(
45+
image,
46+
0,
47+
0,
48+
image.naturalWidth,
49+
image.naturalHeight,
50+
0,
51+
0,
52+
c.width,
53+
c.height
54+
);
55+
56+
const imageInBase64 = c.toDataURL();
57+
return imageInBase64;
58+
};
59+
60+
const getTitle = () => {
61+
const title = document.querySelector(".title.ytmusic-player-bar").textContent;
62+
return title;
63+
};
64+
65+
const getArtist = () => {
66+
const bar = document.querySelectorAll(".subtitle.ytmusic-player-bar")[0];
67+
let artist;
68+
69+
if (bar.querySelectorAll(".yt-simple-endpoint.yt-formatted-string")[0]) {
70+
artist = bar.querySelectorAll(".yt-simple-endpoint.yt-formatted-string")[0]
71+
.textContent;
72+
} else if (bar.querySelectorAll(".byline.ytmusic-player-bar")[0]) {
73+
artist = bar.querySelectorAll(".byline.ytmusic-player-bar")[0].textContent;
74+
}
75+
76+
return artist;
77+
};
78+
79+
const observeVideoAndThumbnail = () => {
80+
observer.observe(document, {
81+
childList: true,
82+
subtree: true,
83+
});
84+
};
85+
86+
module.exports = observeVideoAndThumbnail;

plugins/utils.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ module.exports.templatePath = (pluginPath, name) => {
2020
return path.join(pluginPath, "templates", name);
2121
};
2222

23-
module.exports.triggerAction = (channel, action) => {
24-
return ipcRenderer.send(channel, action);
23+
module.exports.triggerAction = (channel, action, ...args) => {
24+
return ipcRenderer.send(channel, action, ...args);
2525
};
2626

2727
module.exports.listenAction = (channel, callback) => {

0 commit comments

Comments
 (0)