mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-16 12:42:06 +00:00
Merge branch 'master' into lastfm
This commit is contained in:
@ -15,21 +15,22 @@ const defaultConfig = {
|
|||||||
trayClickPlayPause: false,
|
trayClickPlayPause: false,
|
||||||
autoResetAppCache: false,
|
autoResetAppCache: false,
|
||||||
resumeOnStart: true,
|
resumeOnStart: true,
|
||||||
|
proxy: "",
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
// Enabled plugins
|
// Enabled plugins
|
||||||
navigation: {
|
navigation: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
shortcuts: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
adblocker: {
|
adblocker: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
cache: true,
|
cache: true,
|
||||||
additionalBlockLists: [], // Additional list of filters, e.g "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"
|
additionalBlockLists: [], // Additional list of filters, e.g "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"
|
||||||
},
|
},
|
||||||
// Disabled plugins
|
// Disabled plugins
|
||||||
|
shortcuts: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
downloader: {
|
downloader: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
ffmpegArgs: [], // e.g. ["-b:a", "192k"] for an audio bitrate of 192kb/s
|
ffmpegArgs: [], // e.g. ["-b:a", "192k"] for an audio bitrate of 192kb/s
|
||||||
@ -43,9 +44,15 @@ const defaultConfig = {
|
|||||||
suffixesToRemove: [' - Topic', 'VEVO'] // removes suffixes of the artist name, for better recognition
|
suffixesToRemove: [' - Topic', 'VEVO'] // removes suffixes of the artist name, for better recognition
|
||||||
},
|
},
|
||||||
discord: {
|
discord: {
|
||||||
|
enabled: false,
|
||||||
activityTimoutEnabled: true, // if enabled, the discord rich presence gets cleared when music paused after the time specified below
|
activityTimoutEnabled: true, // if enabled, the discord rich presence gets cleared when music paused after the time specified below
|
||||||
activityTimoutTime: 10 * 60 * 1000 // 10 minutes
|
activityTimoutTime: 10 * 60 * 1000 // 10 minutes
|
||||||
},
|
},
|
||||||
|
notifications: {
|
||||||
|
enabled: false,
|
||||||
|
urgency: "normal",
|
||||||
|
unpauseNotification: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
13
index.js
13
index.js
@ -32,6 +32,10 @@ if (config.get("options.disableHardwareAcceleration")) {
|
|||||||
app.disableHardwareAcceleration();
|
app.disableHardwareAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.get("options.proxy")) {
|
||||||
|
app.commandLine.appendSwitch("proxy-server", config.get("options.proxy"));
|
||||||
|
}
|
||||||
|
|
||||||
// Adds debug features like hotkeys for triggering dev tools and reload
|
// Adds debug features like hotkeys for triggering dev tools and reload
|
||||||
require("electron-debug")();
|
require("electron-debug")();
|
||||||
|
|
||||||
@ -75,6 +79,7 @@ function createMainWindow() {
|
|||||||
const windowSize = config.get("window-size");
|
const windowSize = config.get("window-size");
|
||||||
const windowMaximized = config.get("window-maximized");
|
const windowMaximized = config.get("window-maximized");
|
||||||
const windowPosition = config.get("window-position");
|
const windowPosition = config.get("window-position");
|
||||||
|
const useInlineMenu = config.plugins.isEnabled("in-app-menu");
|
||||||
|
|
||||||
const win = new electron.BrowserWindow({
|
const win = new electron.BrowserWindow({
|
||||||
icon: icon,
|
icon: icon,
|
||||||
@ -99,8 +104,12 @@ function createMainWindow() {
|
|||||||
}
|
}
|
||||||
: undefined),
|
: undefined),
|
||||||
},
|
},
|
||||||
frame: !is.macOS(),
|
frame: !is.macOS() && !useInlineMenu,
|
||||||
titleBarStyle: is.macOS() ? "hiddenInset" : "default",
|
titleBarStyle: useInlineMenu
|
||||||
|
? "hidden"
|
||||||
|
: is.macOS()
|
||||||
|
? "hiddenInset"
|
||||||
|
: "default",
|
||||||
autoHideMenuBar: config.get("options.hideMenu"),
|
autoHideMenuBar: config.get("options.hideMenu"),
|
||||||
});
|
});
|
||||||
if (windowPosition) {
|
if (windowPosition) {
|
||||||
|
|||||||
100
menu.js
100
menu.js
@ -7,7 +7,7 @@ const is = require("electron-is");
|
|||||||
const { getAllPlugins } = require("./plugins/utils");
|
const { getAllPlugins } = require("./plugins/utils");
|
||||||
const config = require("./config");
|
const config = require("./config");
|
||||||
|
|
||||||
const pluginEnabledMenu = (plugin, label = "") => ({
|
const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({
|
||||||
label: label || plugin,
|
label: label || plugin,
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
checked: config.plugins.isEnabled(plugin),
|
checked: config.plugins.isEnabled(plugin),
|
||||||
@ -17,26 +17,29 @@ const pluginEnabledMenu = (plugin, label = "") => ({
|
|||||||
} else {
|
} else {
|
||||||
config.plugins.disable(plugin);
|
config.plugins.disable(plugin);
|
||||||
}
|
}
|
||||||
|
if (hasSubmenu) {
|
||||||
|
this.setApplicationMenu(win);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const mainMenuTemplate = (win) => [
|
const mainMenuTemplate = (win, withRoles = true, isTray = false) => [
|
||||||
{
|
{
|
||||||
label: "Plugins",
|
label: "Plugins",
|
||||||
submenu: [
|
submenu: [
|
||||||
...getAllPlugins().map((plugin) => {
|
...getAllPlugins().map((plugin) => {
|
||||||
const pluginPath = path.join(__dirname, "plugins", plugin, "menu.js");
|
const pluginPath = path.join(__dirname, "plugins", plugin, "menu.js");
|
||||||
|
|
||||||
if (!config.plugins.isEnabled(plugin)) {
|
|
||||||
return pluginEnabledMenu(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existsSync(pluginPath)) {
|
if (existsSync(pluginPath)) {
|
||||||
|
if (!config.plugins.isEnabled(plugin)) {
|
||||||
|
return pluginEnabledMenu(win, plugin, "", true);
|
||||||
|
}
|
||||||
|
|
||||||
const getPluginMenu = require(pluginPath);
|
const getPluginMenu = require(pluginPath);
|
||||||
return {
|
return {
|
||||||
label: plugin,
|
label: plugin,
|
||||||
submenu: [
|
submenu: [
|
||||||
pluginEnabledMenu(plugin, "Enabled"),
|
pluginEnabledMenu(win, plugin, "Enabled", true),
|
||||||
...getPluginMenu(win, config.plugins.getOptions(plugin), () =>
|
...getPluginMenu(win, config.plugins.getOptions(plugin), () =>
|
||||||
module.exports.setApplicationMenu(win)
|
module.exports.setApplicationMenu(win)
|
||||||
),
|
),
|
||||||
@ -44,7 +47,7 @@ const mainMenuTemplate = (win) => [
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return pluginEnabledMenu(plugin);
|
return pluginEnabledMenu(win, plugin);
|
||||||
}),
|
}),
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
@ -189,17 +192,59 @@ const mainMenuTemplate = (win) => [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
...(!isTray
|
||||||
label: "View",
|
? [
|
||||||
submenu: [
|
{
|
||||||
{ role: "reload" },
|
label: "View",
|
||||||
{ role: "forceReload" },
|
submenu: withRoles
|
||||||
{ type: "separator" },
|
? [
|
||||||
{ role: "zoomIn" },
|
{ role: "reload" },
|
||||||
{ role: "zoomOut" },
|
{ role: "forceReload" },
|
||||||
{ role: "resetZoom" },
|
{ type: "separator" },
|
||||||
],
|
{ role: "zoomIn" },
|
||||||
},
|
{ role: "zoomOut" },
|
||||||
|
{ role: "resetZoom" },
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
label: "Reload",
|
||||||
|
click: () => {
|
||||||
|
win.webContents.reload();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Force Reload",
|
||||||
|
click: () => {
|
||||||
|
win.webContents.reloadIgnoringCache();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ type: "separator" },
|
||||||
|
{
|
||||||
|
label: "Zoom In",
|
||||||
|
click: () => {
|
||||||
|
win.webContents.setZoomLevel(
|
||||||
|
win.webContents.getZoomLevel() + 1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Zoom Out",
|
||||||
|
click: () => {
|
||||||
|
win.webContents.setZoomLevel(
|
||||||
|
win.webContents.getZoomLevel() - 1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Reset Zoom",
|
||||||
|
click: () => {
|
||||||
|
win.webContents.setZoomLevel(0);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
label: "Navigation",
|
label: "Navigation",
|
||||||
submenu: [
|
submenu: [
|
||||||
@ -219,6 +264,23 @@ const mainMenuTemplate = (win) => [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Restart App",
|
||||||
|
click: () => {
|
||||||
|
app.relaunch();
|
||||||
|
app.quit();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(!isTray
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: "Quit App",
|
||||||
|
click: () => {
|
||||||
|
app.quit();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -62,12 +62,13 @@
|
|||||||
"npm": "Please use yarn and not npm"
|
"npm": "Please use yarn and not npm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cliqz/adblocker-electron": "^1.20.0",
|
"@cliqz/adblocker-electron": "^1.20.1",
|
||||||
"@ffmpeg/core": "^0.8.5",
|
"@ffmpeg/core": "^0.8.5",
|
||||||
"@ffmpeg/ffmpeg": "^0.9.7",
|
"@ffmpeg/ffmpeg": "^0.9.7",
|
||||||
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.1",
|
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.1",
|
||||||
"async-mutex": "^0.3.1",
|
"async-mutex": "^0.3.1",
|
||||||
"browser-id3-writer": "^4.4.0",
|
"browser-id3-writer": "^4.4.0",
|
||||||
|
"custom-electron-titlebar": "^3.2.6",
|
||||||
"discord-rpc": "^3.2.0",
|
"discord-rpc": "^3.2.0",
|
||||||
"downloads-folder": "^3.0.1",
|
"downloads-folder": "^3.0.1",
|
||||||
"electron-debug": "^3.2.0",
|
"electron-debug": "^3.2.0",
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
const { writeFileSync } = require("fs");
|
|
||||||
const { join } = require("path");
|
const { join } = require("path");
|
||||||
|
|
||||||
const ID3Writer = require("browser-id3-writer");
|
const { dialog } = require("electron");
|
||||||
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");
|
||||||
@ -40,35 +38,6 @@ function handle(win) {
|
|||||||
console.log("Unknown action: " + action);
|
console.log("Unknown action: " + action);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("add-metadata", (event, filePath, songBuffer) => {
|
|
||||||
let fileBuffer = songBuffer;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const writer = new ID3Writer(songBuffer);
|
|
||||||
if (metadata.image) {
|
|
||||||
const coverBuffer = metadata.image.toPNG();
|
|
||||||
|
|
||||||
// Create the metadata tags
|
|
||||||
writer
|
|
||||||
.setFrame("TIT2", metadata.title)
|
|
||||||
.setFrame("TPE1", [metadata.artist])
|
|
||||||
.setFrame("APIC", {
|
|
||||||
type: 3,
|
|
||||||
data: coverBuffer,
|
|
||||||
description: "",
|
|
||||||
});
|
|
||||||
writer.addTag();
|
|
||||||
}
|
|
||||||
fileBuffer = Buffer.from(writer.arrayBuffer);
|
|
||||||
} catch (error) {
|
|
||||||
sendError(win, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFileSync(filePath, fileBuffer);
|
|
||||||
// Notify the youtube-dl file
|
|
||||||
event.reply("add-metadata-done");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = handle;
|
module.exports = handle;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const { contextBridge } = require("electron");
|
const { contextBridge } = require("electron");
|
||||||
|
|
||||||
|
const { defaultConfig } = require("../../config");
|
||||||
const { getSongMenu } = require("../../providers/dom-elements");
|
const { getSongMenu } = require("../../providers/dom-elements");
|
||||||
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
|
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
|
||||||
const { ACTIONS, CHANNEL } = require("./actions.js");
|
const { ACTIONS, CHANNEL } = require("./actions.js");
|
||||||
@ -31,11 +32,19 @@ const reinit = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const baseUrl = defaultConfig.url;
|
||||||
|
|
||||||
// TODO: re-enable once contextIsolation is set to true
|
// TODO: re-enable once contextIsolation is set to true
|
||||||
// contextBridge.exposeInMainWorld("downloader", {
|
// contextBridge.exposeInMainWorld("downloader", {
|
||||||
// download: () => {
|
// download: () => {
|
||||||
global.download = () => {
|
global.download = () => {
|
||||||
const videoUrl = window.location.href;
|
let videoUrl = getSongMenu()
|
||||||
|
.querySelector("ytmusic-menu-navigation-item-renderer")
|
||||||
|
.querySelector("#navigation-endpoint")
|
||||||
|
.getAttribute("href");
|
||||||
|
videoUrl = !videoUrl
|
||||||
|
? global.songInfo.url || window.location.href
|
||||||
|
: baseUrl + videoUrl;
|
||||||
|
|
||||||
downloadVideoToMP3(
|
downloadVideoToMP3(
|
||||||
videoUrl,
|
videoUrl,
|
||||||
@ -51,7 +60,8 @@ global.download = () => {
|
|||||||
reinit();
|
reinit();
|
||||||
},
|
},
|
||||||
reinit,
|
reinit,
|
||||||
pluginOptions
|
pluginOptions,
|
||||||
|
global.songInfo
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
// });
|
// });
|
||||||
|
|||||||
@ -2,62 +2,89 @@ const { existsSync, mkdirSync } = require("fs");
|
|||||||
const { join } = require("path");
|
const { join } = require("path");
|
||||||
const { URL } = require("url");
|
const { URL } = require("url");
|
||||||
|
|
||||||
const { ipcMain } = require("electron");
|
const { dialog, ipcMain } = require("electron");
|
||||||
const is = require("electron-is");
|
const is = require("electron-is");
|
||||||
const ytpl = require("ytpl");
|
const ytpl = require("ytpl");
|
||||||
|
|
||||||
|
const { setOptions } = require("../../config/plugins");
|
||||||
|
const getSongInfo = require("../../providers/song-info");
|
||||||
const { sendError } = require("./back");
|
const { sendError } = require("./back");
|
||||||
const { defaultMenuDownloadLabel, getFolder } = require("./utils");
|
const { defaultMenuDownloadLabel, getFolder } = require("./utils");
|
||||||
|
|
||||||
let downloadLabel = defaultMenuDownloadLabel;
|
let downloadLabel = defaultMenuDownloadLabel;
|
||||||
|
let metadataURL = undefined;
|
||||||
|
let callbackIsRegistered = false;
|
||||||
|
|
||||||
module.exports = (win, options, refreshMenu) => [
|
module.exports = (win, options, refreshMenu) => {
|
||||||
{
|
if (!callbackIsRegistered) {
|
||||||
label: downloadLabel,
|
const registerCallback = getSongInfo(win);
|
||||||
click: async () => {
|
registerCallback((info) => {
|
||||||
const currentURL = win.webContents.getURL();
|
metadataURL = info.url;
|
||||||
const playlistID = new URL(currentURL).searchParams.get("list");
|
});
|
||||||
if (!playlistID) {
|
callbackIsRegistered = true;
|
||||||
sendError(win, new Error("No playlist ID found"));
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const playlist = await ytpl(playlistID);
|
return [
|
||||||
const playlistTitle = playlist.title;
|
{
|
||||||
|
label: downloadLabel,
|
||||||
|
click: async () => {
|
||||||
|
const currentURL = metadataURL || win.webContents.getURL();
|
||||||
|
const playlistID = new URL(currentURL).searchParams.get("list");
|
||||||
|
if (!playlistID) {
|
||||||
|
sendError(win, new Error("No playlist ID found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const folder = getFolder(options.downloadFolder);
|
const playlist = await ytpl(playlistID);
|
||||||
const playlistFolder = join(folder, playlistTitle);
|
const playlistTitle = playlist.title;
|
||||||
if (existsSync(playlistFolder)) {
|
|
||||||
sendError(
|
|
||||||
win,
|
|
||||||
new Error(`The folder ${playlistFolder} already exists`)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mkdirSync(playlistFolder, { recursive: true });
|
|
||||||
|
|
||||||
ipcMain.on("downloader-feedback", (_, feedback) => {
|
const folder = getFolder(options.downloadFolder);
|
||||||
downloadLabel = feedback;
|
const playlistFolder = join(folder, playlistTitle);
|
||||||
|
if (existsSync(playlistFolder)) {
|
||||||
|
sendError(
|
||||||
|
win,
|
||||||
|
new Error(`The folder ${playlistFolder} already exists`)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mkdirSync(playlistFolder, { recursive: true });
|
||||||
|
|
||||||
|
ipcMain.on("downloader-feedback", (_, feedback) => {
|
||||||
|
downloadLabel = feedback;
|
||||||
|
refreshMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
downloadLabel = `Downloading "${playlistTitle}"`;
|
||||||
refreshMenu();
|
refreshMenu();
|
||||||
});
|
|
||||||
|
|
||||||
downloadLabel = `Downloading "${playlistTitle}"`;
|
if (is.dev()) {
|
||||||
refreshMenu();
|
console.log(
|
||||||
|
`Downloading playlist "${playlistTitle}" (${playlist.items.length} songs)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (is.dev()) {
|
playlist.items.slice(0, options.playlistMaxItems).forEach((song) => {
|
||||||
console.log(
|
win.webContents.send(
|
||||||
`Downloading playlist "${playlistTitle}" (${playlist.items.length} songs)`
|
"downloader-download-playlist",
|
||||||
);
|
song,
|
||||||
}
|
playlistTitle,
|
||||||
|
options
|
||||||
playlist.items.slice(0, options.playlistMaxItems).forEach((song) => {
|
);
|
||||||
win.webContents.send(
|
});
|
||||||
"downloader-download-playlist",
|
},
|
||||||
song,
|
|
||||||
playlistTitle,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
];
|
label: "Choose download folder",
|
||||||
|
click: () => {
|
||||||
|
let result = dialog.showOpenDialogSync({
|
||||||
|
properties: ["openDirectory", "createDirectory"],
|
||||||
|
defaultPath: getFolder(options.downloadFolder),
|
||||||
|
});
|
||||||
|
if (result) {
|
||||||
|
options.downloadFolder = result[0];
|
||||||
|
setOptions("downloader", options);
|
||||||
|
} // else = user pressed cancel
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|||||||
@ -19,11 +19,11 @@
|
|||||||
<g class="style-scope yt-icon">
|
<g class="style-scope yt-icon">
|
||||||
<path
|
<path
|
||||||
d="M25.462,19.105v6.848H4.515v-6.848H0.489v8.861c0,1.111,0.9,2.012,2.016,2.012h24.967c1.115,0,2.016-0.9,2.016-2.012v-8.861H25.462z"
|
d="M25.462,19.105v6.848H4.515v-6.848H0.489v8.861c0,1.111,0.9,2.012,2.016,2.012h24.967c1.115,0,2.016-0.9,2.016-2.012v-8.861H25.462z"
|
||||||
class="style-scope yt-icon"
|
class="style-scope yt-icon" fill="#aaaaaa"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M14.62,18.426l-5.764-6.965c0,0-0.877-0.828,0.074-0.828s3.248,0,3.248,0s0-0.557,0-1.416c0-2.449,0-6.906,0-8.723c0,0-0.129-0.494,0.615-0.494c0.75,0,4.035,0,4.572,0c0.536,0,0.524,0.416,0.524,0.416c0,1.762,0,6.373,0,8.742c0,0.768,0,1.266,0,1.266s1.842,0,2.998,0c1.154,0,0.285,0.867,0.285,0.867s-4.904,6.51-5.588,7.193C15.092,18.979,14.62,18.426,14.62,18.426z"
|
d="M14.62,18.426l-5.764-6.965c0,0-0.877-0.828,0.074-0.828s3.248,0,3.248,0s0-0.557,0-1.416c0-2.449,0-6.906,0-8.723c0,0-0.129-0.494,0.615-0.494c0.75,0,4.035,0,4.572,0c0.536,0,0.524,0.416,0.524,0.416c0,1.762,0,6.373,0,8.742c0,0.768,0,1.266,0,1.266s1.842,0,2.998,0c1.154,0,0.285,0.867,0.285,0.867s-4.904,6.51-5.588,7.193C15.092,18.979,14.62,18.426,14.62,18.426z"
|
||||||
class="style-scope yt-icon"
|
class="style-scope yt-icon" fill="#aaaaaa"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ const { writeFileSync } = require("fs");
|
|||||||
const { join } = require("path");
|
const { join } = require("path");
|
||||||
|
|
||||||
const Mutex = require("async-mutex").Mutex;
|
const Mutex = require("async-mutex").Mutex;
|
||||||
|
const ID3Writer = require("browser-id3-writer");
|
||||||
const { ipcRenderer } = require("electron");
|
const { ipcRenderer } = require("electron");
|
||||||
const is = require("electron-is");
|
const is = require("electron-is");
|
||||||
const filenamify = require("filenamify");
|
const filenamify = require("filenamify");
|
||||||
@ -120,7 +121,7 @@ const toMP3 = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const folder = getFolder(options.downloadFolder);
|
const folder = getFolder(options.downloadFolder);
|
||||||
const name = metadata
|
const name = metadata.title
|
||||||
? `${metadata.artist ? `${metadata.artist} - ` : ""}${metadata.title}`
|
? `${metadata.artist ? `${metadata.artist} - ` : ""}${metadata.title}`
|
||||||
: videoName;
|
: videoName;
|
||||||
const filename = filenamify(name + "." + extension, {
|
const filename = filenamify(name + "." + extension, {
|
||||||
@ -130,15 +131,29 @@ const toMP3 = async (
|
|||||||
const filePath = join(folder, subfolder, filename);
|
const filePath = join(folder, subfolder, filename);
|
||||||
const fileBuffer = ffmpeg.FS("readFile", safeVideoName + "." + extension);
|
const fileBuffer = ffmpeg.FS("readFile", safeVideoName + "." + extension);
|
||||||
|
|
||||||
if (existingMetadata) {
|
// Add the metadata
|
||||||
writeFileSync(filePath, fileBuffer);
|
try {
|
||||||
|
const writer = new ID3Writer(fileBuffer);
|
||||||
|
if (metadata.image) {
|
||||||
|
const coverBuffer = metadata.image.toPNG();
|
||||||
|
|
||||||
|
// Create the metadata tags
|
||||||
|
writer
|
||||||
|
.setFrame("TIT2", metadata.title)
|
||||||
|
.setFrame("TPE1", [metadata.artist])
|
||||||
|
.setFrame("APIC", {
|
||||||
|
type: 3,
|
||||||
|
data: coverBuffer,
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
writer.addTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(filePath, Buffer.from(writer.arrayBuffer));
|
||||||
|
} catch (error) {
|
||||||
|
sendError(error);
|
||||||
|
} finally {
|
||||||
reinit();
|
reinit();
|
||||||
} else {
|
|
||||||
// Add the metadata
|
|
||||||
sendFeedback("Adding metadata…");
|
|
||||||
ipcRenderer.send("add-metadata", filePath, fileBuffer);
|
|
||||||
ipcRenderer.once("add-metadata-done", reinit);
|
|
||||||
sendFeedback("Finished converting", metadata);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendError(e);
|
sendError(e);
|
||||||
|
|||||||
76
plugins/in-app-menu/back.js
Normal file
76
plugins/in-app-menu/back.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const { Menu } = require("electron");
|
||||||
|
const electronLocalshortcut = require("electron-localshortcut");
|
||||||
|
|
||||||
|
const config = require("../../config");
|
||||||
|
const { setApplicationMenu } = require("../../menu");
|
||||||
|
const { injectCSS } = require("../utils");
|
||||||
|
|
||||||
|
//check that menu doesn't get created twice
|
||||||
|
let done = false;
|
||||||
|
// win hook for fixing menu
|
||||||
|
let win;
|
||||||
|
|
||||||
|
const originalBuildMenu = Menu.buildFromTemplate;
|
||||||
|
// This function natively gets called on all submenu so no more reason to use recursion
|
||||||
|
Menu.buildFromTemplate = (template) => {
|
||||||
|
// Fix checkboxes and radio buttons
|
||||||
|
updateCheckboxesAndRadioButtons(win, template);
|
||||||
|
|
||||||
|
// return as normal
|
||||||
|
return originalBuildMenu(template);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = (winImport) => {
|
||||||
|
win = winImport;
|
||||||
|
|
||||||
|
// css for custom scrollbar + disable drag area(was causing bugs)
|
||||||
|
injectCSS(win.webContents, path.join(__dirname, "style.css"));
|
||||||
|
|
||||||
|
win.on("ready-to-show", () => {
|
||||||
|
// (apparently ready-to-show is called twice)
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
|
||||||
|
setApplicationMenu(win);
|
||||||
|
|
||||||
|
//register keyboard shortcut && hide menu if hideMenu is enabled
|
||||||
|
if (config.get("options.hideMenu")) {
|
||||||
|
switchMenuVisibility(win);
|
||||||
|
electronLocalshortcut.register(win, "Esc", () => {
|
||||||
|
switchMenuVisibility(win);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let visible = true;
|
||||||
|
function switchMenuVisibility(win) {
|
||||||
|
visible = !visible;
|
||||||
|
win.webContents.send("updateMenu", visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCheckbox(win, item) {
|
||||||
|
//check item
|
||||||
|
item.checked = !item.checked;
|
||||||
|
//update menu (closes it)
|
||||||
|
win.webContents.send("updateMenu", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update checkboxes/radio buttons
|
||||||
|
function updateCheckboxesAndRadioButtons(win, template) {
|
||||||
|
for (let item of template) {
|
||||||
|
// Change onClick of checkbox+radio
|
||||||
|
if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) {
|
||||||
|
let originalOnclick = item.click;
|
||||||
|
item.click = (itemClicked) => {
|
||||||
|
originalOnclick(itemClicked);
|
||||||
|
checkCheckbox(win, itemClicked);
|
||||||
|
};
|
||||||
|
item.fixed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
plugins/in-app-menu/front.js
Normal file
24
plugins/in-app-menu/front.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const { remote, ipcRenderer } = require("electron");
|
||||||
|
|
||||||
|
const customTitlebar = require("custom-electron-titlebar");
|
||||||
|
|
||||||
|
module.exports = () => {
|
||||||
|
const bar = new customTitlebar.Titlebar({
|
||||||
|
backgroundColor: customTitlebar.Color.fromHex("#050505"),
|
||||||
|
itemBackgroundColor: customTitlebar.Color.fromHex("#121212"),
|
||||||
|
});
|
||||||
|
bar.updateTitle(" ");
|
||||||
|
document.title = "Youtube Music";
|
||||||
|
|
||||||
|
ipcRenderer.on("updateMenu", function (event, menu) {
|
||||||
|
if (menu) {
|
||||||
|
bar.updateMenu(remote.Menu.getApplicationMenu());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
bar.updateMenu(null);
|
||||||
|
} catch (e) {
|
||||||
|
//will always throw type error - null isn't menu, but it works
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
69
plugins/in-app-menu/style.css
Normal file
69
plugins/in-app-menu/style.css
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/* increase font size for menu and menuItems */
|
||||||
|
.titlebar,
|
||||||
|
.menubar-menu-container .action-label {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allow submenu's to show correctly */
|
||||||
|
.menubar-menu-container {
|
||||||
|
overflow-y: visible !important;
|
||||||
|
}
|
||||||
|
/* fixes scrollbar positioning relative to nav bar */
|
||||||
|
#nav-bar-background.ytmusic-app-layout {
|
||||||
|
right: 15px !important;
|
||||||
|
}
|
||||||
|
/* remove window dragging for nav bar (conflict with titlebar drag) */
|
||||||
|
ytmusic-nav-bar,
|
||||||
|
.tab-titleiron-icon,
|
||||||
|
ytmusic-pivot-bar-item-renderer {
|
||||||
|
-webkit-app-region: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move navBar downwards and make it opaque */
|
||||||
|
ytmusic-app-layout {
|
||||||
|
--ytmusic-nav-bar-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ytmusic-search-box.ytmusic-nav-bar {
|
||||||
|
margin-top: 29px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-content.ytmusic-nav-bar {
|
||||||
|
background: #030303;
|
||||||
|
}
|
||||||
|
yt-page-navigation-progress,
|
||||||
|
#progress.yt-page-navigation-progress,
|
||||||
|
ytmusic-item-section-renderer[has-item-section-tabbed-header-renderer_]
|
||||||
|
#header.ytmusic-item-section-renderer,
|
||||||
|
ytmusic-header-renderer.ytmusic-search-page {
|
||||||
|
top: 90px !important;
|
||||||
|
}
|
||||||
|
/* Custom scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
background-color: #030303;
|
||||||
|
border-radius: 100px;
|
||||||
|
-moz-border-radius: 100px;
|
||||||
|
-webkit-border-radius: 100px;
|
||||||
|
}
|
||||||
|
/* hover effect for both scrollbar area, and scrollbar 'thumb' */
|
||||||
|
::-webkit-scrollbar:hover {
|
||||||
|
background-color: rgba(15, 15, 15, 0.699);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The scrollbar 'thumb' ...that marque oval shape in a scrollbar */
|
||||||
|
::-webkit-scrollbar-thumb:vertical {
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 2px solid rgba(0, 0, 0, 0);
|
||||||
|
|
||||||
|
background: rgb(49, 0, 0);
|
||||||
|
border-radius: 100px;
|
||||||
|
-moz-border-radius: 100px;
|
||||||
|
-webkit-border-radius: 100px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:vertical:active {
|
||||||
|
background: rgb(56, 0, 0); /* Some darker color when you click it */
|
||||||
|
border-radius: 100px;
|
||||||
|
-moz-border-radius: 100px;
|
||||||
|
-webkit-border-radius: 100px;
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
const { Notification } = require("electron");
|
const { Notification } = require("electron");
|
||||||
const getSongInfo = require("../../providers/song-info");
|
const getSongInfo = require("../../providers/song-info");
|
||||||
|
|
||||||
const notify = info => {
|
const notify = (info, options) => {
|
||||||
let notificationImage = "assets/youtube-music.png";
|
let notificationImage = "assets/youtube-music.png";
|
||||||
|
|
||||||
if (info.image) {
|
if (info.image) {
|
||||||
@ -14,27 +14,38 @@ const notify = info => {
|
|||||||
body: info.artist,
|
body: info.artist,
|
||||||
icon: notificationImage,
|
icon: notificationImage,
|
||||||
silent: true,
|
silent: true,
|
||||||
|
urgency: options.urgency,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the notification
|
// Send the notification
|
||||||
currentNotification = new Notification(notification);
|
const currentNotification = new Notification(notification);
|
||||||
currentNotification.show()
|
currentNotification.show()
|
||||||
|
|
||||||
return currentNotification;
|
return currentNotification;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = (win) => {
|
module.exports = (win, options) => {
|
||||||
const registerCallback = getSongInfo(win);
|
const registerCallback = getSongInfo(win);
|
||||||
let oldNotification;
|
let oldNotification;
|
||||||
|
let oldURL = "";
|
||||||
win.on("ready-to-show", () => {
|
win.on("ready-to-show", () => {
|
||||||
// Register the callback for new song information
|
// Register the callback for new song information
|
||||||
registerCallback(songInfo => {
|
registerCallback(songInfo => {
|
||||||
// If song is playing send notification
|
// on pause - reset url? and skip notification
|
||||||
if (!songInfo.isPaused) {
|
if (songInfo.isPaused) {
|
||||||
|
//reset oldURL if unpause notification option is on
|
||||||
|
if (options.unpauseNotification) {
|
||||||
|
oldURL = "";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If url isn't the same as last one - send notification
|
||||||
|
if (songInfo.url !== oldURL) {
|
||||||
|
oldURL = songInfo.url;
|
||||||
// Close the old notification
|
// Close the old notification
|
||||||
oldNotification?.close();
|
oldNotification?.close();
|
||||||
// This fixes a weird bug that would cause the notification to be updated instead of showing
|
// This fixes a weird bug that would cause the notification to be updated instead of showing
|
||||||
setTimeout(()=>{ oldNotification = notify(songInfo) }, 10);
|
setTimeout(()=>{ oldNotification = notify(songInfo, options) }, 10);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
19
plugins/notifications/menu.js
Normal file
19
plugins/notifications/menu.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const {urgencyLevels, setUrgency, setUnpause} = require("./utils");
|
||||||
|
|
||||||
|
module.exports = (win, options) => [
|
||||||
|
{
|
||||||
|
label: "Notification Priority",
|
||||||
|
submenu: urgencyLevels.map(level => ({
|
||||||
|
label: level.name,
|
||||||
|
type: "radio",
|
||||||
|
checked: options.urgency === level.value,
|
||||||
|
click: () => setUrgency(options, level.value)
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Show notification on unpause",
|
||||||
|
type: "checkbox",
|
||||||
|
checked: options.unpauseNotification,
|
||||||
|
click: (item) => setUnpause(options, item.checked)
|
||||||
|
}
|
||||||
|
];
|
||||||
19
plugins/notifications/utils.js
Normal file
19
plugins/notifications/utils.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const {setOptions} = require("../../config/plugins");
|
||||||
|
|
||||||
|
module.exports.urgencyLevels = [
|
||||||
|
{name: "Low", value: "low"},
|
||||||
|
{name: "Normal", value: "normal"},
|
||||||
|
{name: "High", value: "critical"},
|
||||||
|
];
|
||||||
|
module.exports.setUrgency = (options, level) => {
|
||||||
|
options.urgency = level
|
||||||
|
setOption(options)
|
||||||
|
};
|
||||||
|
module.exports.setUnpause = (options, value) => {
|
||||||
|
options.unpauseNotification = value
|
||||||
|
setOption(options)
|
||||||
|
};
|
||||||
|
|
||||||
|
let setOption = options => {
|
||||||
|
setOptions("notifications", options)
|
||||||
|
};
|
||||||
BIN
plugins/taskbar-mediacontrol/assets/backward.png
Normal file
BIN
plugins/taskbar-mediacontrol/assets/backward.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 269 B |
BIN
plugins/taskbar-mediacontrol/assets/forward.png
Normal file
BIN
plugins/taskbar-mediacontrol/assets/forward.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 250 B |
BIN
plugins/taskbar-mediacontrol/assets/pause.png
Normal file
BIN
plugins/taskbar-mediacontrol/assets/pause.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 B |
BIN
plugins/taskbar-mediacontrol/assets/play.png
Normal file
BIN
plugins/taskbar-mediacontrol/assets/play.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 265 B |
58
plugins/taskbar-mediacontrol/back.js
Normal file
58
plugins/taskbar-mediacontrol/back.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
const getSongControls = require('../../providers/song-controls');
|
||||||
|
const getSongInfo = require('../../providers/song-info');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = win => {
|
||||||
|
win.hide = function () {
|
||||||
|
win.minimize();
|
||||||
|
win.setSkipTaskbar(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const show = win.show;
|
||||||
|
win.show = function () {
|
||||||
|
win.restore();
|
||||||
|
win.focus();
|
||||||
|
win.setSkipTaskbar(false);
|
||||||
|
show.apply(win);
|
||||||
|
};
|
||||||
|
|
||||||
|
win.isVisible = function () {
|
||||||
|
return !win.isMinimized();
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerCallback = getSongInfo(win);
|
||||||
|
const {playPause, next, previous} = getSongControls(win);
|
||||||
|
|
||||||
|
// If the page is ready, register the callback
|
||||||
|
win.on('ready-to-show', () => {
|
||||||
|
registerCallback(songInfo => {
|
||||||
|
// Wait for song to start before setting thumbar
|
||||||
|
if (songInfo.title === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win32 require full rewrite of components
|
||||||
|
win.setThumbarButtons([
|
||||||
|
{
|
||||||
|
tooltip: 'Previous',
|
||||||
|
icon: get('backward.png'),
|
||||||
|
click() {previous(win.webContents);}
|
||||||
|
}, {
|
||||||
|
tooltip: 'Play/Pause',
|
||||||
|
// Update icon based on play state
|
||||||
|
icon: songInfo.isPaused ? get('play.png') : get('pause.png'),
|
||||||
|
click() {playPause(win.webContents);}
|
||||||
|
}, {
|
||||||
|
tooltip: 'Next',
|
||||||
|
icon: get('forward.png'),
|
||||||
|
click() {next(win.webContents);}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Util
|
||||||
|
function get(file) {
|
||||||
|
return path.join(__dirname,"assets", file);
|
||||||
|
}
|
||||||
@ -1,22 +1,32 @@
|
|||||||
const { ipcRenderer } = require("electron");
|
const { ipcRenderer } = require("electron");
|
||||||
|
|
||||||
|
const { getImage } = require("./song-info");
|
||||||
|
|
||||||
|
global.songInfo = {};
|
||||||
|
|
||||||
|
ipcRenderer.on("update-song-info", async (_, extractedSongInfo) => {
|
||||||
|
global.songInfo = JSON.parse(extractedSongInfo);
|
||||||
|
global.songInfo.image = await getImage(global.songInfo.imageSrc);
|
||||||
|
});
|
||||||
|
|
||||||
const injectListener = () => {
|
const injectListener = () => {
|
||||||
var oldXHR = window.XMLHttpRequest;
|
var oldXHR = window.XMLHttpRequest;
|
||||||
function newXHR() {
|
function newXHR() {
|
||||||
var realXHR = new oldXHR();
|
var realXHR = new oldXHR();
|
||||||
realXHR.addEventListener("readystatechange", () => {
|
realXHR.addEventListener(
|
||||||
if(realXHR.readyState==4 && realXHR.status==200){
|
"readystatechange",
|
||||||
if (realXHR.responseURL.includes('/player')){
|
() => {
|
||||||
// if the request is the contains the song info send the response to ipcMain
|
if (realXHR.readyState == 4 && realXHR.status == 200) {
|
||||||
ipcRenderer.send(
|
if (realXHR.responseURL.includes("/player")) {
|
||||||
"song-info-request",
|
// if the request contains the song info, send the response to ipcMain
|
||||||
realXHR.responseText
|
ipcRenderer.send("song-info-request", realXHR.responseText);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, false);
|
false
|
||||||
|
);
|
||||||
return realXHR;
|
return realXHR;
|
||||||
}
|
}
|
||||||
window.XMLHttpRequest = newXHR;
|
window.XMLHttpRequest = newXHR;
|
||||||
}
|
};
|
||||||
module.exports = injectListener;
|
module.exports = injectListener;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
const { ipcMain } = require("electron");
|
const { ipcMain, nativeImage } = require("electron");
|
||||||
const { nativeImage } = require("electron");
|
|
||||||
|
|
||||||
const fetch = require("node-fetch");
|
const fetch = require("node-fetch");
|
||||||
|
|
||||||
@ -38,21 +37,25 @@ const songInfo = {
|
|||||||
uploadDate: "",
|
uploadDate: "",
|
||||||
imageSrc: "",
|
imageSrc: "",
|
||||||
image: null,
|
image: null,
|
||||||
isPaused: true,
|
isPaused: undefined,
|
||||||
songDuration: 0,
|
songDuration: 0,
|
||||||
elapsedSeconds: 0,
|
elapsedSeconds: 0,
|
||||||
|
url: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleData = async (_event, responseText) => {
|
const handleData = async (responseText, win) => {
|
||||||
data = JSON.parse(responseText);
|
let data = JSON.parse(responseText);
|
||||||
songInfo.title = data?.videoDetails?.title;
|
songInfo.title = data?.videoDetails?.title;
|
||||||
songInfo.artist = data?.videoDetails?.author;
|
songInfo.artist = data?.videoDetails?.author;
|
||||||
songInfo.views = data?.videoDetails?.viewCount;
|
songInfo.views = data?.videoDetails?.viewCount;
|
||||||
songInfo.imageSrc = data?.videoDetails?.thumbnail?.thumbnails?.[0]?.url;
|
songInfo.imageSrc = data?.videoDetails?.thumbnail?.thumbnails?.pop()?.url;
|
||||||
songInfo.songDuration = data?.videoDetails?.lengthSeconds;
|
songInfo.songDuration = data?.videoDetails?.lengthSeconds;
|
||||||
songInfo.image = await getImage(songInfo.imageSrc);
|
songInfo.image = await getImage(songInfo.imageSrc);
|
||||||
songInfo.uploadDate = data?.microformat?.microformatDataRenderer?.uploadDate;
|
songInfo.uploadDate = data?.microformat?.microformatDataRenderer?.uploadDate;
|
||||||
}
|
songInfo.url = data?.microformat?.microformatDataRenderer?.urlCanonical;
|
||||||
|
|
||||||
|
win.webContents.send("update-song-info", JSON.stringify(songInfo));
|
||||||
|
};
|
||||||
|
|
||||||
const registerProvider = (win) => {
|
const registerProvider = (win) => {
|
||||||
// This variable will be filled with the callbacks once they register
|
// This variable will be filled with the callbacks once they register
|
||||||
@ -77,9 +80,15 @@ const registerProvider = (win) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// This will be called when the song-info-front finds a new request with song data
|
// This will be called when the song-info-front finds a new request with song data
|
||||||
ipcMain.on('song-info-request', handleData);
|
ipcMain.on("song-info-request", async (_, responseText) => {
|
||||||
|
await handleData(responseText, win);
|
||||||
|
callbacks.forEach((c) => {
|
||||||
|
c(songInfo);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return registerCallback;
|
return registerCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = registerProvider;
|
module.exports = registerProvider;
|
||||||
|
module.exports.getImage = getImage;
|
||||||
|
|||||||
8
tray.js
8
tray.js
@ -32,7 +32,7 @@ module.exports.setUpTray = (app, win) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const trayMenu = Menu.buildFromTemplate([
|
let template = [
|
||||||
{
|
{
|
||||||
label: "Play/Pause",
|
label: "Play/Pause",
|
||||||
click: () => {
|
click: () => {
|
||||||
@ -57,13 +57,15 @@ module.exports.setUpTray = (app, win) => {
|
|||||||
win.show();
|
win.show();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...mainMenuTemplate(win),
|
...mainMenuTemplate(win, true, true),
|
||||||
{
|
{
|
||||||
label: "Quit",
|
label: "Quit",
|
||||||
click: () => {
|
click: () => {
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
const trayMenu = Menu.buildFromTemplate(template);
|
||||||
tray.setContextMenu(trayMenu);
|
tray.setContextMenu(trayMenu);
|
||||||
};
|
};
|
||||||
|
|||||||
67
yarn.lock
67
yarn.lock
@ -282,45 +282,45 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@cliqz/adblocker-content@^1.20.0":
|
"@cliqz/adblocker-content@^1.20.3":
|
||||||
version "1.20.0"
|
version "1.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-content/-/adblocker-content-1.20.0.tgz#fcfa2845a577ba8d9af282afbae2fc437b3f1c70"
|
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-content/-/adblocker-content-1.20.3.tgz#198c8719cd62ef3c67a5c98e7a54336b7812ed86"
|
||||||
integrity sha512-KcokmK2B+tAnVMi7nGHgzXUVf78wAODG1Uk+K3tBPf9VAo3mwldYZ472uTj6LUfZv5oeTwe4PwfmPWXWZy3Eew==
|
integrity sha512-aCBTiIiNgVbmDIQyUcsn0j3n+umvs0DuVlL6dccPE3qfeFxT4whUvMwjxUS2/dIBfJK9A1LywmvVke2eSPw9wg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cliqz/adblocker-extended-selectors" "^1.20.0"
|
"@cliqz/adblocker-extended-selectors" "^1.20.3"
|
||||||
|
|
||||||
"@cliqz/adblocker-electron-preload@^1.20.0":
|
"@cliqz/adblocker-electron-preload@^1.20.3":
|
||||||
version "1.20.0"
|
version "1.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron-preload/-/adblocker-electron-preload-1.20.0.tgz#997b694fbb1b1206e04b1fd570690234cc7ef630"
|
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron-preload/-/adblocker-electron-preload-1.20.3.tgz#17dff446ad742cb6e68a4572e7a75cff1fa33f95"
|
||||||
integrity sha512-brNQFjIoGTMClmFphtoK0EnjOlbqfxr6sA3CrOZiHfG0e07Id5GoU95re8+s8xA+/nd1GrJl/k5/b4aks+S9Gw==
|
integrity sha512-fWAFEGj+F0VOUKZd2FqWLuguXmGzkRQz5wTCqasvndX4HSe0P8Pd2666pWK9RJW1dLJE7U61mQfTbYqlUFVTMA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cliqz/adblocker-content" "^1.20.0"
|
"@cliqz/adblocker-content" "^1.20.3"
|
||||||
|
|
||||||
"@cliqz/adblocker-electron@^1.20.0":
|
"@cliqz/adblocker-electron@^1.20.1":
|
||||||
version "1.20.0"
|
version "1.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron/-/adblocker-electron-1.20.0.tgz#bacfb9feaf1d3dab339b992e3defa111a4b5ed3c"
|
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron/-/adblocker-electron-1.20.3.tgz#f2b4bf5dddf90f64251c46f89238526dc0037384"
|
||||||
integrity sha512-zD881g+YxxO4BM6NB5qZtSevg9Cj7QtlCJ4tkcKZnD9MDQsNXQVIFFEWwqhd00kLkTUS0+jT0px9b81cigAPNg==
|
integrity sha512-ZcEl3W7R/aoUA0IPIMtvdn7gVE6O9+rDQ9OllIH/s/gVeElXZsgPEtpPMSuoJWbi9d2mlr8yo3UFvkV3u7c4gw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cliqz/adblocker" "^1.20.0"
|
"@cliqz/adblocker" "^1.20.3"
|
||||||
"@cliqz/adblocker-electron-preload" "^1.20.0"
|
"@cliqz/adblocker-electron-preload" "^1.20.3"
|
||||||
tldts-experimental "^5.6.21"
|
tldts-experimental "^5.6.21"
|
||||||
|
|
||||||
"@cliqz/adblocker-extended-selectors@^1.20.0":
|
"@cliqz/adblocker-extended-selectors@^1.20.3":
|
||||||
version "1.20.0"
|
version "1.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-extended-selectors/-/adblocker-extended-selectors-1.20.0.tgz#95ede657b670f627b39f92d85a97093cecee6ffe"
|
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-extended-selectors/-/adblocker-extended-selectors-1.20.3.tgz#a817915948ec4e64c8b878a80a71d911ea0412c8"
|
||||||
integrity sha512-dnBPIngGe1eDWvYX49eP2yyCE2AY1QD5E+8SaXW6lslnjS0GQnkcXCAkkGR2am4Qdk78HAiWTXL65Zt9hdkupA==
|
integrity sha512-Xsrqg4qgpNVx80UJrAz/nS8jcbgCTIGvir0MrjoXrw0GheqRxsgE540XXP9JA7QlifLNVEOO44DpHvhUmISkQw==
|
||||||
|
|
||||||
"@cliqz/adblocker@^1.20.0":
|
"@cliqz/adblocker@^1.20.3":
|
||||||
version "1.20.0"
|
version "1.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cliqz/adblocker/-/adblocker-1.20.0.tgz#514746e9ee72fcd886f1e2e1aaf13b28fc63f232"
|
resolved "https://registry.yarnpkg.com/@cliqz/adblocker/-/adblocker-1.20.3.tgz#4e8d03ed03c476f7b4388d25f910b1b9e0b15cc9"
|
||||||
integrity sha512-lkEj0Pj1ikwMURrvoFv0YnLfaXFuJI+jexI7zdh4fDmlwRppzDDgOhPXgCczoAlYacJk5x2mf7pan6JybRD9Kw==
|
integrity sha512-Dqj8fJ399kFsFQ53uW0ajA5jH5VJ5ppawOjtoV2s+7NILj1ydvw40jTrr3l/ObMvxaAGaDUj2Euo4beg3/EtRQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cliqz/adblocker-content" "^1.20.0"
|
"@cliqz/adblocker-content" "^1.20.3"
|
||||||
"@cliqz/adblocker-extended-selectors" "^1.20.0"
|
"@cliqz/adblocker-extended-selectors" "^1.20.3"
|
||||||
"@remusao/guess-url-type" "^1.1.2"
|
"@remusao/guess-url-type" "^1.1.2"
|
||||||
"@remusao/small" "^1.1.2"
|
"@remusao/small" "^1.1.2"
|
||||||
"@remusao/smaz" "^1.7.1"
|
"@remusao/smaz" "^1.7.1"
|
||||||
"@types/chrome" "^0.0.128"
|
"@types/chrome" "^0.0.133"
|
||||||
"@types/firefox-webext-browser" "^82.0.0"
|
"@types/firefox-webext-browser" "^82.0.0"
|
||||||
tldts-experimental "^5.6.21"
|
tldts-experimental "^5.6.21"
|
||||||
|
|
||||||
@ -1014,10 +1014,10 @@
|
|||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
"@types/responselike" "*"
|
"@types/responselike" "*"
|
||||||
|
|
||||||
"@types/chrome@^0.0.128":
|
"@types/chrome@^0.0.133":
|
||||||
version "0.0.128"
|
version "0.0.133"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.128.tgz#5dbd8b2539a367353fbe4386f119b510105f8b6a"
|
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.133.tgz#9e1d55441584ba2d5274ca84db36427da9c5dc6e"
|
||||||
integrity sha512-eGc599TDtersMBW1cSnExHm0IHrXrO5xdk6Sa2Dq30ED+hR1rpT1ez0NNcCgvGO52nmktGfyvd3Uyquzv3LL4g==
|
integrity sha512-G8uIUdaCTBILprQvQXBWGXZxjAWbkCkFQit17cdH3zYQEwU8f/etNl8+M7e8MRz9Xj8daHaVpysneMZMx8/ldQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/filesystem" "*"
|
"@types/filesystem" "*"
|
||||||
"@types/har-format" "*"
|
"@types/har-format" "*"
|
||||||
@ -2653,6 +2653,11 @@ cssstyle@^2.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
cssom "~0.3.6"
|
cssom "~0.3.6"
|
||||||
|
|
||||||
|
custom-electron-titlebar@^3.2.6:
|
||||||
|
version "3.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/custom-electron-titlebar/-/custom-electron-titlebar-3.2.6.tgz#4cd064efa5020954c09732efa8c667a7ee3636e3"
|
||||||
|
integrity sha512-P3ZGEr0eouUHqhdBBXllpuy2bFhfSmp+32HQBPcwzujjIsUhQxQj/nCpJiFa4SUGAEp1ifu/icuZdDKNNX72Tw==
|
||||||
|
|
||||||
dashdash@^1.12.0:
|
dashdash@^1.12.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||||
|
|||||||
Reference in New Issue
Block a user