mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-13 19:31:46 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc48b920a8 | |||
| 9bc81da6f2 | |||
| 2b3363f5dc | |||
| bcff6e5134 | |||
| 1dcf76b006 | |||
| 7bdc0f42a2 | |||
| 023f5b6bc3 | |||
| 3e97e9307c |
113
index.js
113
index.js
@ -41,33 +41,7 @@ function onClosed() {
|
|||||||
mainWindow = null;
|
mainWindow = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMainWindow() {
|
function loadPlugins(win) {
|
||||||
const windowSize = store.get("window-size");
|
|
||||||
const windowMaximized = store.get("window-maximized");
|
|
||||||
|
|
||||||
const win = new electron.BrowserWindow({
|
|
||||||
icon: icon,
|
|
||||||
width: windowSize.width,
|
|
||||||
height: windowSize.height,
|
|
||||||
backgroundColor: "#000",
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: isTesting(), // Only necessary when testing with Spectron
|
|
||||||
preload: path.join(__dirname, "preload.js"),
|
|
||||||
nativeWindowOpen: true, // window.open return Window object(like in regular browsers), not BrowserWindowProxy
|
|
||||||
enableRemoteModule: true,
|
|
||||||
affinity: "main-window", // main window, and addition windows should work in one process
|
|
||||||
},
|
|
||||||
frame: !is.macOS(),
|
|
||||||
titleBarStyle: is.macOS() ? "hiddenInset" : "default",
|
|
||||||
});
|
|
||||||
if (windowMaximized) {
|
|
||||||
win.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
win.webContents.loadURL(store.get("url"));
|
|
||||||
win.on("closed", onClosed);
|
|
||||||
|
|
||||||
injectCSS(win.webContents, path.join(__dirname, "youtube-music.css"));
|
injectCSS(win.webContents, path.join(__dirname, "youtube-music.css"));
|
||||||
win.webContents.on("did-finish-load", () => {
|
win.webContents.on("did-finish-load", () => {
|
||||||
if (is.dev()) {
|
if (is.dev()) {
|
||||||
@ -84,6 +58,61 @@ function createMainWindow() {
|
|||||||
handle(win);
|
handle(win);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMainWindow() {
|
||||||
|
const windowSize = store.get("window-size");
|
||||||
|
const windowMaximized = store.get("window-maximized");
|
||||||
|
|
||||||
|
const win = new electron.BrowserWindow({
|
||||||
|
icon: icon,
|
||||||
|
width: windowSize.width,
|
||||||
|
height: windowSize.height,
|
||||||
|
backgroundColor: "#000",
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: isTesting(), // Only necessary when testing with Spectron
|
||||||
|
preload: path.join(__dirname, "preload.js"),
|
||||||
|
nodeIntegrationInSubFrames: true,
|
||||||
|
nativeWindowOpen: true, // window.open return Window object(like in regular browsers), not BrowserWindowProxy
|
||||||
|
enableRemoteModule: true,
|
||||||
|
affinity: "main-window", // main window, and addition windows should work in one process
|
||||||
|
},
|
||||||
|
frame: !is.macOS(),
|
||||||
|
titleBarStyle: is.macOS() ? "hiddenInset" : "default",
|
||||||
|
});
|
||||||
|
if (windowMaximized) {
|
||||||
|
win.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
win.webContents.loadURL(store.get("url"));
|
||||||
|
win.on("closed", onClosed);
|
||||||
|
|
||||||
|
win.on("move", () => {
|
||||||
|
let position = win.getPosition();
|
||||||
|
store.set("window-position", { x: position[0], y: position[1] });
|
||||||
|
});
|
||||||
|
|
||||||
|
win.on("resize", () => {
|
||||||
|
const windowSize = win.getSize();
|
||||||
|
|
||||||
|
store.set("window-maximized", win.isMaximized());
|
||||||
|
if (!win.isMaximized()) {
|
||||||
|
store.set("window-size", { width: windowSize[0], height: windowSize[1] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
win.once("ready-to-show", () => {
|
||||||
|
if (isAppVisible()) {
|
||||||
|
win.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on("browser-window-created", (event, win) => {
|
||||||
|
loadPlugins(win);
|
||||||
|
|
||||||
win.webContents.on("did-fail-load", () => {
|
win.webContents.on("did-fail-load", () => {
|
||||||
if (is.dev()) {
|
if (is.dev()) {
|
||||||
@ -92,6 +121,10 @@ function createMainWindow() {
|
|||||||
win.webContents.loadFile(path.join(__dirname, "error.html"));
|
win.webContents.loadFile(path.join(__dirname, "error.html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
win.webContents.on("will-prevent-unload", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
win.webContents.on("did-navigate-in-page", () => {
|
win.webContents.on("did-navigate-in-page", () => {
|
||||||
const url = win.webContents.getURL();
|
const url = win.webContents.getURL();
|
||||||
if (url.startsWith("https://music.youtube.com")) {
|
if (url.startsWith("https://music.youtube.com")) {
|
||||||
@ -124,29 +157,7 @@ function createMainWindow() {
|
|||||||
options.webPreferences.affinity = "main-window";
|
options.webPreferences.affinity = "main-window";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
});
|
||||||
win.on("move", () => {
|
|
||||||
let position = win.getPosition();
|
|
||||||
store.set("window-position", { x: position[0], y: position[1] });
|
|
||||||
});
|
|
||||||
|
|
||||||
win.on("resize", () => {
|
|
||||||
const windowSize = win.getSize();
|
|
||||||
|
|
||||||
store.set("window-maximized", win.isMaximized());
|
|
||||||
if (!win.isMaximized()) {
|
|
||||||
store.set("window-size", { width: windowSize[0], height: windowSize[1] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
win.once("ready-to-show", () => {
|
|
||||||
if (isAppVisible()) {
|
|
||||||
win.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return win;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.on("window-all-closed", () => {
|
app.on("window-all-closed", () => {
|
||||||
if (process.platform !== "darwin") {
|
if (process.platform !== "darwin") {
|
||||||
@ -168,8 +179,8 @@ app.on("activate", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.on("ready", () => {
|
app.on("ready", () => {
|
||||||
setApplicationMenu();
|
|
||||||
mainWindow = createMainWindow();
|
mainWindow = createMainWindow();
|
||||||
|
setApplicationMenu(mainWindow);
|
||||||
setUpTray(app, mainWindow);
|
setUpTray(app, mainWindow);
|
||||||
|
|
||||||
// Autostart at login
|
// Autostart at login
|
||||||
|
|||||||
19
menu.js
19
menu.js
@ -13,7 +13,7 @@ const {
|
|||||||
startAtLogin,
|
startAtLogin,
|
||||||
} = require("./store");
|
} = require("./store");
|
||||||
|
|
||||||
const mainMenuTemplate = [
|
const mainMenuTemplate = (win) => [
|
||||||
{
|
{
|
||||||
label: "Plugins",
|
label: "Plugins",
|
||||||
submenu: getAllPlugins().map((plugin) => {
|
submenu: getAllPlugins().map((plugin) => {
|
||||||
@ -79,13 +79,26 @@ const mainMenuTemplate = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Toggle DevTools",
|
||||||
|
// Cannot use "toggleDevTools" role in MacOS
|
||||||
|
click: () => {
|
||||||
|
const { webContents } = win;
|
||||||
|
if (webContents.isDevToolsOpened()) {
|
||||||
|
webContents.closeDevTools();
|
||||||
|
} else {
|
||||||
|
const devToolsOptions = {};
|
||||||
|
webContents.openDevTools(devToolsOptions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports.mainMenuTemplate = mainMenuTemplate;
|
module.exports.mainMenuTemplate = mainMenuTemplate;
|
||||||
module.exports.setApplicationMenu = () => {
|
module.exports.setApplicationMenu = (win) => {
|
||||||
const menuTemplate = [...mainMenuTemplate];
|
const menuTemplate = [...mainMenuTemplate(win)];
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
const name = app.name;
|
const name = app.name;
|
||||||
menuTemplate.unshift({
|
menuTemplate.unshift({
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "youtube-music",
|
"name": "youtube-music",
|
||||||
"productName": "YouTube Music",
|
"productName": "YouTube Music",
|
||||||
"version": "1.5.0",
|
"version": "1.6.0",
|
||||||
"description": "YouTube Music Desktop App - including custom plugins",
|
"description": "YouTube Music Desktop App - including custom plugins",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "th-ch/youtube-music",
|
"repository": "th-ch/youtube-music",
|
||||||
@ -47,11 +47,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cliqz/adblocker-electron": "^1.18.3",
|
"@cliqz/adblocker-electron": "^1.18.3",
|
||||||
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.7.1",
|
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.0",
|
||||||
"electron-debug": "^3.1.0",
|
"electron-debug": "^3.1.0",
|
||||||
"electron-is": "^3.0.0",
|
"electron-is": "^3.0.0",
|
||||||
"electron-localshortcut": "^3.2.1",
|
"electron-localshortcut": "^3.2.1",
|
||||||
"electron-store": "^6.0.0",
|
"electron-store": "^6.0.1",
|
||||||
"electron-updater": "^4.3.5",
|
"electron-updater": "^4.3.5",
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
},
|
},
|
||||||
|
|||||||
18
plugins/notifications/actions.js
Normal file
18
plugins/notifications/actions.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const { triggerAction } = require("../utils");
|
||||||
|
|
||||||
|
const CHANNEL = "notification";
|
||||||
|
const ACTIONS = {
|
||||||
|
NOTIFICATION: "notification",
|
||||||
|
};
|
||||||
|
|
||||||
|
function notify(info) {
|
||||||
|
triggerAction(CHANNEL, ACTIONS.NOTIFICATION, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CHANNEL,
|
||||||
|
ACTIONS,
|
||||||
|
global: {
|
||||||
|
notify,
|
||||||
|
},
|
||||||
|
};
|
||||||
33
plugins/notifications/back.js
Normal file
33
plugins/notifications/back.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { nativeImage, Notification } = require("electron");
|
||||||
|
|
||||||
|
const { listenAction } = require("../utils");
|
||||||
|
const { ACTIONS, CHANNEL } = require("./actions.js");
|
||||||
|
|
||||||
|
function notify(info) {
|
||||||
|
let notificationImage = "assets/youtube-music.png";
|
||||||
|
if (info.image) {
|
||||||
|
notificationImage = nativeImage.createFromDataURL(info.image);
|
||||||
|
}
|
||||||
|
|
||||||
|
const notification = {
|
||||||
|
title: info.title || "Playing",
|
||||||
|
body: info.artist,
|
||||||
|
icon: notificationImage,
|
||||||
|
silent: true,
|
||||||
|
};
|
||||||
|
new Notification(notification).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function listenAndNotify() {
|
||||||
|
listenAction(CHANNEL, (event, action, imageSrc) => {
|
||||||
|
switch (action) {
|
||||||
|
case ACTIONS.NOTIFICATION:
|
||||||
|
notify(imageSrc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("Unknown action: " + action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = listenAndNotify;
|
||||||
86
plugins/notifications/front.js
Normal file
86
plugins/notifications/front.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
let videoElement = null;
|
||||||
|
let image = null;
|
||||||
|
|
||||||
|
const observer = new MutationObserver((mutations, observer) => {
|
||||||
|
if (!videoElement) {
|
||||||
|
videoElement = document.querySelector("video");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!image) {
|
||||||
|
image = document.querySelector(".ytmusic-player-bar.image");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoElement !== null && image !== null) {
|
||||||
|
observer.disconnect();
|
||||||
|
let notificationImage = null;
|
||||||
|
|
||||||
|
videoElement.addEventListener("play", () => {
|
||||||
|
notify({
|
||||||
|
title: getTitle(),
|
||||||
|
artist: getArtist(),
|
||||||
|
image: notificationImage,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
image.addEventListener("load", () => {
|
||||||
|
notificationImage = null;
|
||||||
|
const imageInBase64 = convertImageToBase64(image);
|
||||||
|
if (image && image.complete && image.naturalHeight !== 0) {
|
||||||
|
notificationImage = imageInBase64;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert an image (DOM element) to base64 string
|
||||||
|
const convertImageToBase64 = (image, size = 256) => {
|
||||||
|
image.setAttribute("crossorigin", "anonymous");
|
||||||
|
|
||||||
|
const c = document.createElement("canvas");
|
||||||
|
c.height = size;
|
||||||
|
c.width = size;
|
||||||
|
|
||||||
|
const ctx = c.getContext("2d");
|
||||||
|
ctx.drawImage(
|
||||||
|
image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
image.naturalWidth,
|
||||||
|
image.naturalHeight,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
c.width,
|
||||||
|
c.height
|
||||||
|
);
|
||||||
|
|
||||||
|
const imageInBase64 = c.toDataURL();
|
||||||
|
return imageInBase64;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTitle = () => {
|
||||||
|
const title = document.querySelector(".title.ytmusic-player-bar").textContent;
|
||||||
|
return title;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getArtist = () => {
|
||||||
|
const bar = document.querySelectorAll(".subtitle.ytmusic-player-bar")[0];
|
||||||
|
let artist;
|
||||||
|
|
||||||
|
if (bar.querySelectorAll(".yt-simple-endpoint.yt-formatted-string")[0]) {
|
||||||
|
artist = bar.querySelectorAll(".yt-simple-endpoint.yt-formatted-string")[0]
|
||||||
|
.textContent;
|
||||||
|
} else if (bar.querySelectorAll(".byline.ytmusic-player-bar")[0]) {
|
||||||
|
artist = bar.querySelectorAll(".byline.ytmusic-player-bar")[0].textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return artist;
|
||||||
|
};
|
||||||
|
|
||||||
|
const observeVideoAndThumbnail = () => {
|
||||||
|
observer.observe(document, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = observeVideoAndThumbnail;
|
||||||
@ -20,8 +20,8 @@ module.exports.templatePath = (pluginPath, name) => {
|
|||||||
return path.join(pluginPath, "templates", name);
|
return path.join(pluginPath, "templates", name);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.triggerAction = (channel, action) => {
|
module.exports.triggerAction = (channel, action, ...args) => {
|
||||||
return ipcRenderer.send(channel, action);
|
return ipcRenderer.send(channel, action, ...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.listenAction = (channel, callback) => {
|
module.exports.listenAction = (channel, callback) => {
|
||||||
|
|||||||
2
tray.js
2
tray.js
@ -61,7 +61,7 @@ module.exports.setUpTray = (app, win) => {
|
|||||||
win.show();
|
win.show();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...mainMenuTemplate,
|
...mainMenuTemplate(win),
|
||||||
{
|
{
|
||||||
label: "Quit",
|
label: "Quit",
|
||||||
click: () => {
|
click: () => {
|
||||||
|
|||||||
16
yarn.lock
16
yarn.lock
@ -1379,9 +1379,9 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@wdio/logger" "6.4.7"
|
"@wdio/logger" "6.4.7"
|
||||||
|
|
||||||
"YoutubeNonStop@git://github.com/lawfx/YoutubeNonStop.git#v0.7.1":
|
"YoutubeNonStop@git://github.com/lawfx/YoutubeNonStop.git#v0.8.0":
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
resolved "git://github.com/lawfx/YoutubeNonStop.git#4f8cc214636226ecc8ea2b830435855c067aea2e"
|
resolved "git://github.com/lawfx/YoutubeNonStop.git#c2252130fbd63baa70047355a84fe424adb1c9e0"
|
||||||
|
|
||||||
abab@^2.0.3:
|
abab@^2.0.3:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
@ -2392,7 +2392,7 @@ concat-stream@^1.6.2:
|
|||||||
readable-stream "^2.2.2"
|
readable-stream "^2.2.2"
|
||||||
typedarray "^0.0.6"
|
typedarray "^0.0.6"
|
||||||
|
|
||||||
conf@^7.1.1:
|
conf@^7.1.2:
|
||||||
version "7.1.2"
|
version "7.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/conf/-/conf-7.1.2.tgz#d9678a9d8f04de8bf5cd475105da8fdae49c2ec4"
|
resolved "https://registry.yarnpkg.com/conf/-/conf-7.1.2.tgz#d9678a9d8f04de8bf5cd475105da8fdae49c2ec4"
|
||||||
integrity sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==
|
integrity sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==
|
||||||
@ -3022,12 +3022,12 @@ electron-publish@22.8.1:
|
|||||||
lazy-val "^1.0.4"
|
lazy-val "^1.0.4"
|
||||||
mime "^2.4.6"
|
mime "^2.4.6"
|
||||||
|
|
||||||
electron-store@^6.0.0:
|
electron-store@^6.0.1:
|
||||||
version "6.0.0"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-6.0.0.tgz#92a5f8295a326f074281ae0d6a307454e6f68243"
|
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-6.0.1.tgz#2178b9dc37aeb749d99cf9d1d1bc090890b922dc"
|
||||||
integrity sha512-ujb0a/6gxMxb9vOQ2BjOehK9VCyq5OKvttekd9v/tohA9oBHnAdV+Vxu4eoRh+/F9ShPFhcvDZkMdqO5i+TXUw==
|
integrity sha512-8rdM0XEmDGsLuZM2oRABzsLX+XmD5x3rwxPMEPv0MrN9/BWanyy3ilb2v+tCrKtIZVF3MxUiZ9Bfqe8e0popKQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
conf "^7.1.1"
|
conf "^7.1.2"
|
||||||
type-fest "^0.16.0"
|
type-fest "^0.16.0"
|
||||||
|
|
||||||
electron-updater@^4.3.5:
|
electron-updater@^4.3.5:
|
||||||
|
|||||||
Reference in New Issue
Block a user