Compare commits

...

49 Commits

Author SHA1 Message Date
TC
2cb6f56feb Bump version 2021-07-01 22:43:35 +02:00
TC
46285a5ed0 Bump glob-parent and add lint script (xo) 2021-06-27 21:19:33 +02:00
TC
496836b33b Bump dependencies to fix vulnerabilities 2021-06-27 21:12:28 +02:00
af127879a5 Merge pull request #339 from th-ch/fix-downloader-plugin
Fix downloader plugin
2021-06-27 21:10:35 +02:00
TC
38ef452801 Bump ffmpeg version 2021-06-27 20:46:48 +02:00
TC
a9a5d99676 Do not add network filters in adblocker cache to fix session enhancing 2021-06-27 20:46:48 +02:00
TC
e5ab50cebd Override content security policy to allow FFmpeg worker 2021-06-27 20:46:46 +02:00
TC
49194f8141 nit: re-order dependencies 2021-06-27 20:39:05 +02:00
TC
641ae27efd Update ytdl-core and ytpl 2021-06-27 20:38:08 +02:00
47a5dec465 Merge pull request #337 from th-ch/snyk-upgrade-fe663d0d7c5fc658f327e05ae5966f76
[Snyk] Upgrade @cliqz/adblocker-electron from 1.22.0 to 1.22.1
2021-06-25 23:11:25 +02:00
c93eabb400 Merge pull request #249 from Araxeus/update-in-app-menu
Update and simplify in-app-menu
2021-06-25 23:03:05 +02:00
492a47321d Merge branch 'master' into update-in-app-menu 2021-06-25 12:19:39 +03:00
c89f6af8c6 fix: upgrade @cliqz/adblocker-electron from 1.22.0 to 1.22.1
Snyk has created this PR to upgrade @cliqz/adblocker-electron from 1.22.0 to 1.22.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-06-25 04:53:24 +00:00
9687c6c8e4 Merge pull request #331 from th-ch/dependabot/npm_and_yarn/hosted-git-info-2.8.9
Bump hosted-git-info from 2.8.8 to 2.8.9
2021-06-24 22:06:27 +02:00
ef0a89126a Merge pull request #330 from th-ch/dependabot/npm_and_yarn/lodash-4.17.21
Bump lodash from 4.17.20 to 4.17.21
2021-06-24 22:05:46 +02:00
8ce71d628d Bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

---
updated-dependencies:
- dependency-name: lodash
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-24 20:04:35 +00:00
ca95d105c8 Merge pull request #328 from th-ch/snyk-upgrade-dacfb8f0b574367961e405e4912a4659
[Snyk] Upgrade ytdl-core from 4.8.0 to 4.8.2
2021-06-24 22:04:32 +02:00
12568c2b09 Merge pull request #324 from th-ch/snyk-upgrade-f650e2139b30a36951a4a00ed1b78d70
[Snyk] Upgrade electron-updater from 4.3.8 to 4.3.9
2021-06-24 22:03:15 +02:00
82abb4d4d3 Merge pull request #323 from th-ch/dependabot/npm_and_yarn/normalize-url-4.5.1
Bump normalize-url from 4.5.0 to 4.5.1
2021-06-24 22:02:52 +02:00
3c0a5dbbe5 Merge pull request #320 from th-ch/dependabot/npm_and_yarn/trim-newlines-3.0.1
Bump trim-newlines from 3.0.0 to 3.0.1
2021-06-24 22:02:33 +02:00
0b98eef06f Merge pull request #317 from th-ch/snyk-upgrade-a785f5d95c7765e2d47737e150a2263d
[Snyk] Upgrade @ffmpeg/core from 0.9.0 to 0.10.0
2021-06-24 22:02:05 +02:00
TC
18e69c9f2a Merge branch 'master' of github.com:th-ch/youtube-music into snyk-upgrade-a785f5d95c7765e2d47737e150a2263d
# By Araxeus (2) and snyk-bot (2)
# Via GitHub (3) and Araxeus (1)
* 'master' of github.com:th-ch/youtube-music:
  check if native image is empty before writing id tag
  fix unsupported hidden webp coverart
  fix: upgrade @ffmpeg/ffmpeg from 0.9.8 to 0.10.0
  fix: upgrade custom-electron-titlebar from 3.2.6 to 3.2.7
2021-06-24 22:01:16 +02:00
8f5d06d420 Merge pull request #316 from th-ch/snyk-upgrade-6e93904bf885d198521c6a5d8110bde3
[Snyk] Upgrade @ffmpeg/ffmpeg from 0.9.8 to 0.10.0
2021-06-24 22:00:07 +02:00
8a299461a0 Merge pull request #311 from th-ch/snyk-upgrade-44a5db26689d4091f6a2c3c3c69b869a
[Snyk] Upgrade custom-electron-titlebar from 3.2.6 to 3.2.7
2021-06-24 21:49:53 +02:00
0c58bec921 Bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

---
updated-dependencies:
- dependency-name: hosted-git-info
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-19 20:29:37 +00:00
e0cb132686 Merge pull request #318 from Araxeus/fix-hidden-webp-cover-art
fix hidden webp thumbnail throwing MIME type error in downloader
2021-06-19 22:28:59 +02:00
2a192f39f9 fix: upgrade ytdl-core from 4.8.0 to 4.8.2
Snyk has created this PR to upgrade ytdl-core from 4.8.0 to 4.8.2.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-06-19 05:30:04 +00:00
b7ebb7d499 fix: upgrade electron-updater from 4.3.8 to 4.3.9
Snyk has created this PR to upgrade electron-updater from 4.3.8 to 4.3.9.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-06-12 04:23:30 +00:00
fffeac21b7 Bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 04:46:20 +00:00
4387cb485d Bump trim-newlines from 3.0.0 to 3.0.1
Bumps [trim-newlines](https://github.com/sindresorhus/trim-newlines) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sindresorhus/trim-newlines/releases)
- [Commits](https://github.com/sindresorhus/trim-newlines/commits)

---
updated-dependencies:
- dependency-name: trim-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-09 18:57:55 +00:00
2a58dc823a check if native image is empty before writing id tag 2021-06-09 20:05:14 +03:00
8eb38271ff fix unsupported hidden webp coverart 2021-06-09 19:53:04 +03:00
1987ad1d4f fix: upgrade @ffmpeg/core from 0.9.0 to 0.10.0
Snyk has created this PR to upgrade @ffmpeg/core from 0.9.0 to 0.10.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-06-08 04:08:44 +00:00
cc4dae60ef fix: upgrade @ffmpeg/ffmpeg from 0.9.8 to 0.10.0
Snyk has created this PR to upgrade @ffmpeg/ffmpeg from 0.9.8 to 0.10.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-06-08 04:08:41 +00:00
1943116aa1 fix: upgrade custom-electron-titlebar from 3.2.6 to 3.2.7
Snyk has created this PR to upgrade custom-electron-titlebar from 3.2.6 to 3.2.7.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-06-05 03:53:12 +00:00
3485d26b11 Merge pull request #308 from th-ch/sponsorblock-plugin
Add Sponsorblock plugin
2021-06-04 22:25:26 +02:00
TC
4a60aa9f20 Keep segments when skipping 2021-06-03 22:15:36 +02:00
TC
cda07c9675 Update adblocking 2021-06-03 22:04:49 +02:00
TC
ca64a77ed0 Add SponsorBlock plugin 2021-06-03 21:47:26 +02:00
TC
30e94d1d6f Refactor videoElement getter into a provider with callback 2021-06-03 21:45:28 +02:00
TC
b8c6ebfa53 Set test environment per test file 2021-06-03 21:43:07 +02:00
b26748ded8 Merge pull request #305 from th-ch/snyk-upgrade-15656c519a90f5bc0f5c8742a9fb04e9
[Snyk] Upgrade @ffmpeg/ffmpeg from 0.9.7 to 0.9.8
2021-05-31 22:06:17 +02:00
f186da0834 fix: upgrade @ffmpeg/ffmpeg from 0.9.7 to 0.9.8
Snyk has created this PR to upgrade @ffmpeg/ffmpeg from 0.9.7 to 0.9.8.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/th-ch/project/81809c53-bb7b-46b9-a0d7-806d45d74ac6?utm_source=github&utm_medium=upgrade-pr
2021-05-29 04:38:22 +00:00
33855f17dd update refreshMenu() function 2021-05-23 17:57:54 +03:00
541c7f34b7 restore menuItems roles that were fixed 2021-05-15 17:24:01 +03:00
090ca828c0 Merge branch 'master' into update-in-app-menu 2021-05-08 01:03:14 +03:00
78974c02e5 save in-app-menu activation state on launch 2021-05-05 21:12:52 +03:00
4508464fd1 update custom-electron-titlebar version 2021-05-05 20:39:08 +03:00
dd6455a559 update in-app-menu 2021-05-05 20:37:29 +03:00
20 changed files with 873 additions and 633 deletions

View File

@ -63,7 +63,19 @@ const defaultConfig = {
volumeDown: "Shift+PageDown"
},
savedVolume: undefined //plugin save volume between session here
}
},
sponsorblock: {
enabled: false,
apiURL: "https://sponsor.ajay.app",
categories: [
"sponsor",
"intro",
"outro",
"interaction",
"selfpromo",
"music_offtopic",
],
},
},
};

View File

@ -2,6 +2,7 @@
const path = require("path");
const electron = require("electron");
const enhanceWebRequest = require("electron-better-web-request").default;
const is = require("electron-is");
const unhandled = require("electron-unhandled");
const { autoUpdater } = require("electron-updater");
@ -143,17 +144,19 @@ function createMainWindow() {
});
}
});
win.webContents.on("render-process-gone", (event, webContents, details) => {
showUnresponsiveDialog(win, details);
});
win.once("ready-to-show", () => {
if (config.get("options.appVisible")) {
win.show();
}
});
removeContentSecurityPolicy();
return win;
}
@ -373,7 +376,45 @@ function showUnresponsiveDialog(win, details) {
app.quit();
break;
default:
break;
break;
}
});
}
function removeContentSecurityPolicy(
session = electron.session.defaultSession
) {
// Allows defining multiple "onHeadersReceived" listeners
// by enhancing the session.
// Some plugins (e.g. adblocker) also define a "onHeadersReceived" listener
enhanceWebRequest(session);
// Custom listener to tweak the content security policy
session.webRequest.onHeadersReceived(function (details, callback) {
if (
!details.responseHeaders["content-security-policy-report-only"] &&
!details.responseHeaders["content-security-policy"]
)
return callback({ cancel: false });
delete details.responseHeaders["content-security-policy-report-only"];
delete details.responseHeaders["content-security-policy"];
callback({ cancel: false, responseHeaders: details.responseHeaders });
});
// When multiple listeners are defined, apply them all
session.webRequest.setResolver("onHeadersReceived", (listeners) => {
const response = listeners.reduce(
async (accumulator, listener) => {
if (accumulator.cancel) {
return accumulator;
}
const result = await listener.apply();
return { ...accumulator, ...result };
},
{ cancel: false }
);
return response;
});
}

View File

@ -2,6 +2,5 @@ module.exports = {
globals: {
__APP__: undefined, // A different app will be launched in each test environment
},
testEnvironment: "./tests/environment",
testTimeout: 30000, // 30s
};

445
menu.js
View File

@ -7,12 +7,13 @@ const is = require("electron-is");
const { getAllPlugins } = require("./plugins/utils");
const config = require("./config");
const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({
// true only if in-app-menu was loaded on launch
const inAppMenuActive = config.plugins.isEnabled("in-app-menu");
const pluginEnabledMenu = (plugin, label = "", hasSubmenu = false, refreshMenu = undefined) => ({
label: label || plugin,
type: "checkbox",
checked: config.plugins.isEnabled(plugin),
//Submenu check used in in-app-menu
hasSubmenu: hasSubmenu || undefined,
click: (item) => {
if (item.checked) {
config.plugins.enable(plugin);
@ -20,252 +21,226 @@ const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({
config.plugins.disable(plugin);
}
if (hasSubmenu) {
this.setApplicationMenu(win);
refreshMenu();
}
},
});
const mainMenuTemplate = (win) => [
{
label: "Plugins",
submenu: [
...getAllPlugins().map((plugin) => {
const pluginPath = path.join(__dirname, "plugins", plugin, "menu.js")
if (existsSync(pluginPath)) {
if (!config.plugins.isEnabled(plugin)) {
return pluginEnabledMenu(win, plugin, "", true);
const mainMenuTemplate = (win) => {
const refreshMenu = () => {
this.setApplicationMenu(win);
if (inAppMenuActive) {
win.webContents.send("updateMenu", true);
}
}
return [
{
label: "Plugins",
submenu: [
...getAllPlugins().map((plugin) => {
const pluginPath = path.join(__dirname, "plugins", plugin, "menu.js")
if (existsSync(pluginPath)) {
if (!config.plugins.isEnabled(plugin)) {
return pluginEnabledMenu(plugin, "", true, refreshMenu);
}
const getPluginMenu = require(pluginPath);
return {
label: plugin,
submenu: [
pluginEnabledMenu(plugin, "Enabled", true, refreshMenu),
...getPluginMenu(win, config.plugins.getOptions(plugin), refreshMenu),
],
};
}
const getPluginMenu = require(pluginPath);
return {
label: plugin,
submenu: [
pluginEnabledMenu(win, plugin, "Enabled", true),
...getPluginMenu(win, config.plugins.getOptions(plugin), () =>
module.exports.setApplicationMenu(win)
),
],
};
}
return pluginEnabledMenu(win, plugin);
}),
],
},
{
label: "Options",
submenu: [
{
label: "Auto-update",
type: "checkbox",
checked: config.get("options.autoUpdates"),
click: (item) => {
config.set("options.autoUpdates", item.checked);
},
},
{
label: "Resume last song when app starts",
type: "checkbox",
checked: config.get("options.resumeOnStart"),
click: (item) => {
config.set("options.resumeOnStart", item.checked);
},
},
...(is.windows() || is.linux()
? [
{
label: "Hide menu",
type: "checkbox",
checked: config.get("options.hideMenu"),
click: (item) => {
config.set("options.hideMenu", item.checked);
},
return pluginEnabledMenu(plugin);
}),
],
},
{
label: "Options",
submenu: [
{
label: "Auto-update",
type: "checkbox",
checked: config.get("options.autoUpdates"),
click: (item) => {
config.set("options.autoUpdates", item.checked);
},
]
: []),
...(is.windows() || is.macOS()
? // Only works on Win/Mac
// https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows
[
{
label: "Start at login",
type: "checkbox",
checked: config.get("options.startAtLogin"),
click: (item) => {
config.set("options.startAtLogin", item.checked);
},
},
]
: []),
{
label: "Tray",
submenu: [
{
label: "Disabled",
type: "radio",
checked: !config.get("options.tray"),
click: () => {
config.set("options.tray", false);
config.set("options.appVisible", true);
},
},
{
label: "Enabled + app visible",
type: "radio",
checked:
config.get("options.tray") && config.get("options.appVisible"),
click: () => {
config.set("options.tray", true);
config.set("options.appVisible", true);
},
},
{
label: "Enabled + app hidden",
type: "radio",
checked:
config.get("options.tray") && !config.get("options.appVisible"),
click: () => {
config.set("options.tray", true);
config.set("options.appVisible", false);
},
},
{ type: "separator" },
{
label: "Play/Pause on click",
type: "checkbox",
checked: config.get("options.trayClickPlayPause"),
click: (item) => {
config.set("options.trayClickPlayPause", item.checked);
},
},
],
},
{ type: "separator" },
{
label: "Advanced options",
submenu: [
{
label: "Disable hardware acceleration",
type: "checkbox",
checked: config.get("options.disableHardwareAcceleration"),
click: (item) => {
config.set("options.disableHardwareAcceleration", item.checked);
},
},
{
label: "Restart on config changes",
type: "checkbox",
checked: config.get("options.restartOnConfigChanges"),
click: (item) => {
config.set("options.restartOnConfigChanges", item.checked);
},
},
{
label: "Reset App cache when app starts",
type: "checkbox",
checked: config.get("options.autoResetAppCache"),
click: (item) => {
config.set("options.autoResetAppCache", item.checked);
},
},
{ type: "separator" },
{
label: "Toggle DevTools",
// Cannot use "toggleDevTools" role in MacOS
click: () => {
const { webContents } = win;
if (webContents.isDevToolsOpened()) {
webContents.closeDevTools();
} else {
const devToolsOptions = {};
webContents.openDevTools(devToolsOptions);
}
},
},
{
label: "Edit config.json",
click: () => {
config.edit();
},
},
]
},
],
},
{
label: "View",
submenu: [
{
label: "Reload",
click: () => {
win.webContents.reload();
},
},
{
label: "Force Reload",
click: () => {
win.webContents.reloadIgnoringCache();
{
label: "Resume last song when app starts",
type: "checkbox",
checked: config.get("options.resumeOnStart"),
click: (item) => {
config.set("options.resumeOnStart", item.checked);
},
},
},
{ type: "separator" },
{
label: "Zoom In",
click: () => {
win.webContents.setZoomLevel(
win.webContents.getZoomLevel() + 1
);
...(is.windows() || is.linux()
? [
{
label: "Hide menu",
type: "checkbox",
checked: config.get("options.hideMenu"),
click: (item) => {
config.set("options.hideMenu", item.checked);
},
},
]
: []),
...(is.windows() || is.macOS()
? // Only works on Win/Mac
// https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows
[
{
label: "Start at login",
type: "checkbox",
checked: config.get("options.startAtLogin"),
click: (item) => {
config.set("options.startAtLogin", item.checked);
},
},
]
: []),
{
label: "Tray",
submenu: [
{
label: "Disabled",
type: "radio",
checked: !config.get("options.tray"),
click: () => {
config.set("options.tray", false);
config.set("options.appVisible", true);
},
},
{
label: "Enabled + app visible",
type: "radio",
checked:
config.get("options.tray") && config.get("options.appVisible"),
click: () => {
config.set("options.tray", true);
config.set("options.appVisible", true);
},
},
{
label: "Enabled + app hidden",
type: "radio",
checked:
config.get("options.tray") && !config.get("options.appVisible"),
click: () => {
config.set("options.tray", true);
config.set("options.appVisible", false);
},
},
{ type: "separator" },
{
label: "Play/Pause on click",
type: "checkbox",
checked: config.get("options.trayClickPlayPause"),
click: (item) => {
config.set("options.trayClickPlayPause", item.checked);
},
},
],
},
},
{
label: "Zoom Out",
click: () => {
win.webContents.setZoomLevel(
win.webContents.getZoomLevel() - 1
);
{ type: "separator" },
{
label: "Advanced options",
submenu: [
{
label: "Disable hardware acceleration",
type: "checkbox",
checked: config.get("options.disableHardwareAcceleration"),
click: (item) => {
config.set("options.disableHardwareAcceleration", item.checked);
},
},
{
label: "Restart on config changes",
type: "checkbox",
checked: config.get("options.restartOnConfigChanges"),
click: (item) => {
config.set("options.restartOnConfigChanges", item.checked);
},
},
{
label: "Reset App cache when app starts",
type: "checkbox",
checked: config.get("options.autoResetAppCache"),
click: (item) => {
config.set("options.autoResetAppCache", item.checked);
},
},
{ type: "separator" },
is.macOS() ?
{
label: "Toggle DevTools",
// Cannot use "toggleDevTools" role in MacOS
click: () => {
const { webContents } = win;
if (webContents.isDevToolsOpened()) {
webContents.closeDevTools();
} else {
const devToolsOptions = {};
webContents.openDevTools(devToolsOptions);
}
},
} :
{ role: "toggleDevTools" },
{
label: "Edit config.json",
click: () => {
config.edit();
},
},
]
},
},
{
label: "Reset Zoom",
click: () => {
win.webContents.setZoomLevel(0);
],
},
{
label: "View",
submenu: [
{ role: "reload" },
{ role: "forceReload" },
{ type: "separator" },
{ role: "zoomIn" },
{ role: "zoomOut" },
{ role: "resetZoom" },
],
},
{
label: "Navigation",
submenu: [
{
label: "Go back",
click: () => {
if (win.webContents.canGoBack()) {
win.webContents.goBack();
}
},
},
},
],
},
{
label: "Navigation",
submenu: [
{
label: "Go back",
click: () => {
if (win.webContents.canGoBack()) {
win.webContents.goBack();
}
{
label: "Go forward",
click: () => {
if (win.webContents.canGoForward()) {
win.webContents.goForward();
}
},
},
},
{
label: "Go forward",
click: () => {
if (win.webContents.canGoForward()) {
win.webContents.goForward();
}
{
label: "Restart App",
click: () => {
app.relaunch();
app.quit();
},
},
},
{
label: "Restart App",
click: () => {
app.relaunch();
app.quit();
},
},
{
label: "Quit App",
click: () => {
app.quit();
},
},
],
},
];
{ role: "quit" },
],
},
];
}
module.exports.mainMenuTemplate = mainMenuTemplate;
module.exports.setApplicationMenu = (win) => {

View File

@ -1,7 +1,7 @@
{
"name": "youtube-music",
"productName": "YouTube Music",
"version": "1.12.1",
"version": "1.12.2",
"description": "YouTube Music Desktop App - including custom plugins",
"license": "MIT",
"repository": "th-ch/youtube-music",
@ -51,6 +51,7 @@
"build:linux": "yarn run clean && electron-builder --linux",
"build:mac": "yarn run clean && electron-builder --mac",
"build:win": "yarn run clean && electron-builder --win",
"lint": "xo",
"plugins": "yarn run plugin:adblocker && yarn run plugin:autoconfirm",
"plugin:adblocker": "rimraf plugins/adblocker/ad-blocker-engine.bin && node plugins/adblocker/blocker.js",
"plugin:autoconfirm": "yarn run generate:package YoutubeNonStop",
@ -59,31 +60,32 @@
"release:win": "yarn run clean && electron-builder --win -p always"
},
"engines": {
"node": ">=12.16.1",
"node": ">=12.20",
"npm": "Please use yarn and not npm"
},
"dependencies": {
"@cliqz/adblocker-electron": "^1.20.5",
"@ffmpeg/core": "^0.9.0",
"@ffmpeg/ffmpeg": "^0.9.7",
"@cliqz/adblocker-electron": "^1.22.1",
"@ffmpeg/core": "^0.10.0",
"@ffmpeg/ffmpeg": "^0.10.0",
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.9.0",
"async-mutex": "^0.3.1",
"browser-id3-writer": "^4.4.0",
"chokidar": "^3.5.1",
"custom-electron-titlebar": "^3.2.6",
"custom-electron-titlebar": "^3.2.7",
"discord-rpc": "^3.2.0",
"electron-better-web-request": "^1.0.1",
"electron-debug": "^3.2.0",
"electron-is": "^3.0.0",
"electron-localshortcut": "^3.2.1",
"electron-store": "^7.0.3",
"electron-unhandled": "^3.0.2",
"electron-updater": "^4.3.8",
"electron-updater": "^4.3.9",
"filenamify": "^4.3.0",
"md5": "^2.3.0",
"node-fetch": "^2.6.1",
"node-notifier": "^9.0.1",
"ytdl-core": "^4.7.0",
"ytpl": "^2.2.0"
"ytdl-core": "^4.8.3",
"ytpl": "^2.2.1"
},
"devDependencies": {
"electron": "^12.0.8",
@ -94,9 +96,11 @@
"jest": "^26.6.3",
"rimraf": "^3.0.2",
"spectron": "^14.0.0",
"xo": "^0.38.2"
"xo": "^0.40.3"
},
"resolutions": {
"glob-parent": "5.1.2",
"minimist": "1.2.5",
"yargs-parser": "18.1.3"
},
"xo": {

View File

@ -8,7 +8,9 @@ const SOURCES = [
"https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt",
// uBlock Origin
"https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt",
"https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters-2020.txt",
"https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters-2021.txt",
// Fanboy Annoyances
"https://secure.fanboy.co.nz/fanboy-annoyance_ubo.txt",
];
const loadAdBlockerEngine = (
@ -31,7 +33,17 @@ const loadAdBlockerEngine = (
...additionalBlockLists,
];
ElectronBlocker.fromLists(fetch, lists, {}, cachingOptions)
ElectronBlocker.fromLists(
fetch,
lists,
{
// when generating the engine for caching, do not load network filters
// So that enhancing the session works as expected
// Allowing to define multiple webRequest listeners
loadNetworkFilters: session !== undefined,
},
cachingOptions
)
.then((blocker) => {
if (session) {
blocker.enableBlockingInSession(session);

View File

@ -1,25 +1,10 @@
let videoElement = null;
const { ontimeupdate } = require("../../providers/video-element");
const observer = new MutationObserver((mutations, observer) => {
if (!videoElement) {
videoElement = document.querySelector("video");
}
if (videoElement) {
videoElement.ontimeupdate = () => {
if (videoElement.currentTime === 0 && videoElement.duration !== NaN) {
// auto-confirm-when-paused plugin can interfere here if not disabled!
videoElement.pause();
}
};
}
});
function observeVideoElement() {
observer.observe(document, {
childList: true,
subtree: true,
module.exports = () => {
ontimeupdate((videoElement) => {
if (videoElement.currentTime === 0 && videoElement.duration !== NaN) {
// auto-confirm-when-paused plugin can interfere here if not disabled!
videoElement.pause();
}
});
}
module.exports = observeVideoElement;
};

View File

@ -55,7 +55,9 @@ function handle(win) {
{ ...nowPlayingMetadata, ...currentMetadata };
try {
const coverBuffer = songMetadata.image ? songMetadata.image.toPNG() : null;
const coverBuffer = songMetadata.image && !songMetadata.image.isEmpty() ?
songMetadata.image.toPNG() : null;
const writer = new ID3Writer(songBuffer);
// Create the metadata tags

View File

@ -1,83 +1,33 @@
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");
//tracks menu visibility
let visible = true;
// 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
updateTemplate(template);
// return as normal
return originalBuildMenu(template);
};
module.exports = (winImport) => {
win = winImport;
module.exports = (win) => {
// css for custom scrollbar + disable drag area(was causing bugs)
injectCSS(win.webContents, path.join(__dirname, "style.css"));
win.once("ready-to-show", () => {
setApplicationMenu(win);
//register keyboard shortcut && hide menu if hideMenu is enabled
if (config.get("options.hideMenu")) {
electronLocalshortcut.register(win, "Esc", () => {
switchMenuVisibility();
setMenuVisibility(!visible);
});
}
});
//set menu visibility on load
win.webContents.once("did-finish-load", () => {
// fix bug with menu not applying on start when no internet connection available
setMenuVisibility(!config.get("options.hideMenu"));
});
function setMenuVisibility(value) {
visible = value;
win.webContents.send("updateMenu", visible);
}
};
function switchMenuVisibility() {
setMenuVisibility(!visible);
}
function setMenuVisibility(value) {
visible = value;
win.webContents.send("updateMenu", visible);
}
function updateCheckboxesAndRadioButtons(item, isRadio, hasSubmenu) {
if (!isRadio) {
//fix checkbox
item.checked = !item.checked;
}
//update menu if radio / hasSubmenu
if (isRadio || hasSubmenu) {
win.webContents.send("updateMenu", true);
}
}
// Update checkboxes/radio buttons
function updateTemplate(template) {
for (let item of template) {
// Change onClick of checkbox+radio
if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) {
const originalOnclick = item.click;
item.click = (itemClicked) => {
originalOnclick(itemClicked);
updateCheckboxesAndRadioButtons(itemClicked, item.type === "radio", item.hasSubmenu);
};
item.fixed = true;
}
}
}

View File

@ -10,15 +10,7 @@ module.exports = () => {
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
}
}
ipcRenderer.on("updateMenu", function (_event, showMenu) {
bar.updateMenu(showMenu ? remote.Menu.getApplicationMenu() : null);
});
};

View File

@ -4,11 +4,6 @@
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;

View File

@ -0,0 +1,51 @@
const fetch = require("node-fetch");
const defaultConfig = require("../../config/defaults");
const registerCallback = require("../../providers/song-info");
const { sortSegments } = require("./segments");
let videoID;
module.exports = (win, options) => {
const { apiURL, categories } = {
...defaultConfig.plugins.sponsorblock,
...options,
};
registerCallback(async (info) => {
const newURL = info.url || win.webContents.getURL();
const newVideoID = new URL(newURL).searchParams.get("v");
if (videoID !== newVideoID) {
videoID = newVideoID;
const segments = await fetchSegments(apiURL, categories);
win.webContents.send("sponsorblock-skip", segments);
}
});
};
const fetchSegments = async (apiURL, categories) => {
const sponsorBlockURL = `${apiURL}/api/skipSegments?videoID=${videoID}&categories=${JSON.stringify(
categories
)}`;
try {
const resp = await fetch(sponsorBlockURL, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
redirect: "follow",
});
if (resp.status !== 200) {
return [];
}
const segments = await resp.json();
const sortedSegments = sortSegments(
segments.map((submission) => submission.segment)
);
return sortedSegments;
} catch {
return [];
}
};

View File

@ -0,0 +1,27 @@
const { ipcRenderer } = require("electron");
const is = require("electron-is");
const { ontimeupdate } = require("../../providers/video-element");
let currentSegments = [];
module.exports = () => {
ipcRenderer.on("sponsorblock-skip", (_, segments) => {
currentSegments = segments;
});
ontimeupdate((videoElement) => {
currentSegments.forEach((segment) => {
if (
videoElement.currentTime >= segment[0] &&
videoElement.currentTime <= segment[1]
) {
videoElement.currentTime = segment[1];
if (is.dev()) {
console.log("SponsorBlock: skipping segment", segment);
}
}
});
});
};

View File

@ -0,0 +1,29 @@
// Segments are an array [ [start, end], … ]
module.exports.sortSegments = (segments) => {
segments.sort((segment1, segment2) =>
segment1[0] === segment2[0]
? segment1[1] - segment2[1]
: segment1[0] - segment2[0]
);
const compiledSegments = [];
let currentSegment;
segments.forEach((segment) => {
if (!currentSegment) {
currentSegment = segment;
return;
}
if (currentSegment[1] < segment[0]) {
compiledSegments.push(currentSegment);
currentSegment = segment;
return;
}
currentSegment[1] = Math.max(currentSegment[1], segment[1]);
});
compiledSegments.push(currentSegment);
return compiledSegments;
};

View File

@ -0,0 +1,34 @@
const { sortSegments } = require("../segments");
test("Segment sorting", () => {
expect(
sortSegments([
[0, 3],
[7, 8],
[5, 6],
])
).toEqual([
[0, 3],
[5, 6],
[7, 8],
]);
expect(
sortSegments([
[0, 5],
[6, 8],
[4, 6],
])
).toEqual([[0, 8]]);
expect(
sortSegments([
[0, 6],
[7, 8],
[4, 6],
])
).toEqual([
[0, 6],
[7, 8],
]);
});

View File

@ -9,18 +9,21 @@ const progressSelector = "#progress-bar";
// Grab the progress using the selector
const getProgress = async (win) => {
// Get current value of the progressbar element
const elapsedSeconds = await win.webContents.executeJavaScript(
return win.webContents.executeJavaScript(
'document.querySelector("' + progressSelector + '").value'
);
return elapsedSeconds;
};
// Grab the native image using the src
const getImage = async (src) => {
const result = await fetch(src);
const buffer = await result.buffer();
return nativeImage.createFromBuffer(buffer);
const output = nativeImage.createFromBuffer(buffer);
if (output.isEmpty() && !src.endsWith(".jpg") && src.includes(".jpg")) { // fix hidden webp files (https://github.com/th-ch/youtube-music/issues/315)
return getImage(src.slice(0, src.lastIndexOf(".jpg")+4));
} else {
return output;
}
};
// To find the paused status, we check if the title contains `-`
@ -30,7 +33,7 @@ const getPausedStatus = async (win) => {
};
const getArtist = async (win) => {
return await win.webContents.executeJavaScript(`
return win.webContents.executeJavaScript(`
document.querySelector(".subtitle.ytmusic-player-bar .yt-formatted-string")
?.textContent
`);
@ -112,4 +115,3 @@ module.exports = registerCallback;
module.exports.setupSongInfo = registerProvider;
module.exports.getImage = getImage;
module.exports.cleanupArtistName = cleanupArtistName;

View File

@ -0,0 +1,22 @@
let videoElement = null;
module.exports.ontimeupdate = (cb) => {
const observer = new MutationObserver((mutations, observer) => {
if (!videoElement) {
videoElement = document.querySelector("video");
if (videoElement) {
observer.disconnect();
videoElement.ontimeupdate = () => cb(videoElement);
}
}
});
if (!videoElement) {
observer.observe(document, {
childList: true,
subtree: true,
});
} else {
videoElement.ontimeupdate = () => cb(videoElement);
}
};

View File

@ -1,3 +1,7 @@
/**
* @jest-environment ./tests/environment
*/
describe("YouTube Music App", () => {
const app = global.__APP__;

View File

@ -63,12 +63,7 @@ module.exports.setUpTray = (app, win) => {
app.quit();
},
},
{
label: "Quit",
click: () => {
app.quit();
},
},
{ role: "quit" }
];
const trayMenu = Menu.buildFromTemplate(template);

655
yarn.lock

File diff suppressed because it is too large Load Diff