Compare commits

...

95 Commits

Author SHA1 Message Date
TC
644950ccc0 Bump version 2021-01-15 09:31:34 +01:00
TC
d848937e6a Rename discord plugin 2021-01-14 23:16:59 +01:00
446aa8becc Merge pull request #121 from th-ch/snyk-upgrade-d29b51f488ba9deb228b72952a3f7b8e
[Snyk] Upgrade electron-debug from 3.1.0 to 3.2.0
2021-01-14 23:12:27 +01:00
TC
d52dbee13c Prettier utils 2021-01-14 23:09:16 +01:00
TC
ed09304ed7 Refactor tray to use provider + play/pause on click 2021-01-14 23:09:04 +01:00
TC
2861473097 Set title/artist metadata in downloader 2021-01-14 23:01:26 +01:00
25fd48697b Merge pull request #125 from th-ch/refactor-providers
Refactor providers
2021-01-14 21:03:26 +01:00
TC
aec542e95e Update discord plugin for new provider + wait for ready 2021-01-13 22:22:22 +01:00
TC
eae95befe1 Merge branch 'master' of github.com:th-ch/youtube-music into refactor-providers
# By TC (9) and semvis123 (2)
# Via GitHub (4) and semvis123 (1)
* 'master' of github.com:th-ch/youtube-music:
  renamed DiscordRPC to Discord
  Downloader plugin: log audio bitrate
  Disable context isolation (to load ffmpeg wasm)
  Use contextBridge in preload script + update navigation plugin
  Fix downloader plugin with context isolation
  Bump version
  Add portable target to windows builds
  Added Discord rich presence and added extra properties to songinfo provider
  Bump version
  Allow custom audio extensions in downloader
  Defensive: handle null/undefined ffmpeg args
2021-01-13 22:10:10 +01:00
f0200e7b38 Merge pull request #124 from semvis123/master
Added Discord rich presence and added extra properties to songInfo provider
2021-01-13 22:08:10 +01:00
70775f4988 renamed DiscordRPC to Discord 2021-01-13 21:56:53 +01:00
074840ef56 Merge pull request #127 from th-ch/fix-plugins-context-isolation
Fix plugins with context isolation
2021-01-13 21:44:37 +01:00
TC
b54c501eeb Downloader plugin: log audio bitrate 2021-01-13 21:36:16 +01:00
6d587cb432 Merge pull request #126 from th-ch/windows-portable
Windows portable exe
2021-01-13 21:32:53 +01:00
TC
39c8031cd7 Disable context isolation (to load ffmpeg wasm) 2021-01-13 21:28:42 +01:00
TC
9ad1dad6df Use contextBridge in preload script + update navigation plugin 2021-01-12 22:54:04 +01:00
TC
79e8fc2fac Fix downloader plugin with context isolation 2021-01-12 22:52:21 +01:00
TC
67c4422eb8 Bump version 2021-01-12 21:37:02 +01:00
TC
18df1223af Add portable target to windows builds 2021-01-12 21:36:38 +01:00
0fafed7c53 Merge pull request #118 from th-ch/dl-custom-audio-format
Downloader plugin - custom audio format
2021-01-12 21:35:55 +01:00
TC
f7cbf2c221 No autoloading of providers (loaded on demand in plugins) 2021-01-12 21:19:01 +01:00
TC
87d2693e2b Use refactored provider in shortcuts plugin 2021-01-12 21:18:32 +01:00
TC
de1e4196d9 Use refactored provider in notification plugin 2021-01-12 21:18:05 +01:00
TC
9110e79c16 Use refactored providers in touchbar plugin 2021-01-12 21:17:40 +01:00
TC
0743034de0 Split providers in 2 2021-01-12 21:17:08 +01:00
TC
f1ddb92886 nit: prettier 2021-01-12 21:16:29 +01:00
a8ce87f2cc Added Discord rich presence and added extra properties to songinfo provider 2021-01-12 20:16:49 +01:00
86a329a61b fix: upgrade electron-debug from 3.1.0 to 3.2.0
Snyk has created this PR to upgrade electron-debug from 3.1.0 to 3.2.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-01-12 02:24:30 +00:00
77e24f41a5 Merge pull request #102 from semvis123/master
Globalized the song info and song controls, and updated Touch Bar for it.
2021-01-10 22:15:11 +01:00
3a5d9bd973 Loads providers before plugins 2021-01-10 21:37:50 +01:00
69f486d53f moved the song info file and removed the capital letters in folder name 2021-01-10 21:22:01 +01:00
5d89043884 fixed typo, plugins/songInfo/back.js
Co-authored-by: th-ch <th-ch@users.noreply.github.com>
2021-01-10 20:31:55 +01:00
2b297c245a Merge pull request #120 from th-ch/bump-electron
Bump electron to v11
2021-01-10 14:37:07 +01:00
TC
945a61fafd Remove warning by setting contextIsolation 2021-01-10 14:10:16 +01:00
TC
1ba166a172 Bump electron to v11 2021-01-10 12:29:12 +01:00
TC
a9a840b6c3 Bump version 2021-01-08 22:41:17 +01:00
TC
6a100c8cb1 Allow custom audio extensions in downloader 2021-01-08 22:29:25 +01:00
TC
b04e2ea130 Defensive: handle null/undefined ffmpeg args 2021-01-08 22:26:44 +01:00
5ac356bf91 Merge pull request #116 from th-ch/snyk-upgrade-041c3272121410c1cb380e1fbc7f9dce
[Snyk] Upgrade electron-updater from 4.3.5 to 4.3.6
2021-01-08 21:29:50 +01:00
35ceb7e83e Merge pull request #117 from th-ch/snyk-upgrade-7528cf91d37d9330a45f611ce738e40e
[Snyk] Upgrade @cliqz/adblocker-electron from 1.18.8 to 1.19.0
2021-01-08 21:28:08 +01:00
5c0cc08d80 fix: upgrade @cliqz/adblocker-electron from 1.18.8 to 1.19.0
Snyk has created this PR to upgrade @cliqz/adblocker-electron from 1.18.8 to 1.19.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-01-07 02:19:51 +00:00
0bf77e592a fix: upgrade electron-updater from 4.3.5 to 4.3.6
Snyk has created this PR to upgrade electron-updater from 4.3.5 to 4.3.6.

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-01-05 02:20:56 +00:00
ec3c1578d2 Merge pull request #109 from th-ch/snyk-upgrade-4e8a81615c57edf69b21ad2a68431dfd
[Snyk] Upgrade ytdl-core from 4.1.1 to 4.1.2
2020-12-29 16:35:55 +01:00
e8ed580ecc Merge pull request #104 from th-ch/dependabot/npm_and_yarn/node-notifier-8.0.1
Bump node-notifier from 8.0.0 to 8.0.1
2020-12-29 16:30:59 +01:00
e2cc2628ae fix: upgrade ytdl-core from 4.1.1 to 4.1.2
Snyk has created this PR to upgrade ytdl-core from 4.1.1 to 4.1.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
2020-12-25 02:20:03 +00:00
9e31e753f0 Bump node-notifier from 8.0.0 to 8.0.1
Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](https://github.com/mikaelbr/node-notifier/compare/v8.0.0...v8.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-22 16:02:44 +00:00
588e0019d6 Changed function style in notifications 2020-12-21 22:11:23 +01:00
5bffdbd628 Simplifies the notification plugin to use the globalized song info 2020-12-21 21:35:02 +01:00
ee239da647 removed unnecessary await keyword 2020-12-21 21:11:00 +01:00
9be3e1afe9 Globalized the songinfo and song controls, and changed the pause/play button.
Globalized the songinfo and song controls, and changed the pause/play button. The songInfo file should eventually get another location, because it isn't really a plugin.
2020-12-21 18:18:34 +01:00
TC
af02b60ce3 Bump version 2020-12-20 20:17:24 +01:00
fbdae0452c Add touchbar plugin to list 2020-12-20 20:16:35 +01:00
d68d73eb4c Merge pull request #101 from semvis123/master
Added Touch Bar plugin
2020-12-20 20:14:49 +01:00
2de27d2e24 Merge pull request #99 from th-ch/snyk-upgrade-d3ff12c0e23681ac817994e603d37491
[Snyk] Upgrade @ffmpeg/core from 0.8.4 to 0.8.5
2020-12-19 22:03:40 +01:00
TC
15591e24d7 Merge branch 'master' of github.com:th-ch/youtube-music into snyk-upgrade-d3ff12c0e23681ac817994e603d37491
# By snyk-bot
# Via GitHub (1) and snyk-bot (1)
* 'master' of github.com:th-ch/youtube-music:
  fix: upgrade @ffmpeg/ffmpeg from 0.9.5 to 0.9.6
2020-12-19 21:50:24 +01:00
715d59e3d4 Merge pull request #100 from th-ch/snyk-upgrade-9aa7c0fdfaaeba26ca66fb92b731a1af
[Snyk] Upgrade @ffmpeg/ffmpeg from 0.9.5 to 0.9.6
2020-12-19 21:32:10 +01:00
f71e0e9da9 Switched like/dislike positions
Switched like/dislike positions to match youtube musics layout
2020-12-16 20:30:01 +01:00
ba3779db07 Updated comment - touchbar plugin 2020-12-16 20:10:05 +01:00
d99aa0242f Added like and dislike buttons 2020-12-16 19:54:44 +01:00
3ea859de09 Added song image 2020-12-16 19:44:26 +01:00
7473677477 touchbar plugin - fixed code style 2020-12-16 15:57:24 +01:00
c3e2c13808 added initial touchbar support 2020-12-16 15:52:01 +01:00
5074ba2f48 fix: upgrade @ffmpeg/ffmpeg from 0.9.5 to 0.9.6
Snyk has created this PR to upgrade @ffmpeg/ffmpeg from 0.9.5 to 0.9.6.

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
2020-12-16 02:20:20 +00:00
a8b8f1079f fix: upgrade @ffmpeg/core from 0.8.4 to 0.8.5
Snyk has created this PR to upgrade @ffmpeg/core from 0.8.4 to 0.8.5.

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
2020-12-16 02:20:16 +00:00
e52987df92 Merge pull request #96 from th-ch/svg-readme
[Readme] Web folder for readme assets + new SVG animation
2020-12-13 16:40:55 +01:00
TC
01fc965170 Web folder for readme assets + new svg animation 2020-12-13 16:19:33 +01:00
ff71f29206 Merge pull request #94 from th-ch/linux-deb-rpm
Add new Linux targets (deb, freebsd, rpm)
2020-12-13 13:40:07 +01:00
TC
a47c5144ac Additional linux targets 2020-12-12 23:08:05 +01:00
TC
9b979b2273 Bump version 2020-12-12 22:28:38 +01:00
TC
81198192bb Add new linux targets (deb, rpm) 2020-12-12 22:28:30 +01:00
c03a08e76a Merge pull request #92 from th-ch/dependabot/npm_and_yarn/ini-1.3.7
Bump ini from 1.3.5 to 1.3.7
2020-12-12 11:26:49 +01:00
807e21eabf Bump ini from 1.3.5 to 1.3.7
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-11 18:36:02 +00:00
36f9d640df Merge pull request #90 from th-ch/adblocking
Fix adblocking
2020-12-11 19:35:10 +01:00
TC
39f30b143b Bump version to 1.7.4 2020-12-10 18:42:17 +01:00
TC
49497d0efb Bump adblocker dependency 2020-12-10 18:42:00 +01:00
TC
79c795927a Add uBlock Origin filters to default sources 2020-12-10 18:40:31 +01:00
TC
66c5ce46ca Fix adblocker preloading to inject scripts/styles 2020-12-10 18:39:58 +01:00
TC
22c7f70c93 Adblocker: add option to disable default lists 2020-12-08 21:40:03 +01:00
TC
467171a17e Bugfix: only use cache with no additional blocklists 2020-12-06 17:55:51 +01:00
3022facbea Readme: add youtube-music logo to badges 2020-12-05 12:33:26 +01:00
a67bf5ea43 Readme: add link to releases 2020-12-05 12:31:14 +01:00
3a1a3d4241 Merge pull request #82 from hbarsaiyan/master
Add AUR badge + beautify badges
2020-12-05 12:29:26 +01:00
9197f4a0e1 Add link to AUR badge
Co-authored-by: th-ch <th-ch@users.noreply.github.com>
2020-12-05 16:18:54 +05:30
d21220693b Add AUR tag + beautify tags 2020-12-05 08:07:17 +00:00
TC
e07cac2406 Bump version to 1.7.1 2020-12-03 22:30:15 +01:00
TC
fd97576611 Option to restart the app on config changes 2020-12-03 22:29:46 +01:00
6ab01056e0 Merge pull request #79 from th-ch/advanced-config
Refactor config, custom plugin options
2020-12-03 21:48:57 +01:00
TC
4dcbd2e7f0 Add plugin options in migration 2020-12-03 20:43:04 +01:00
TC
b94d0d4e8b Adblocker - advanced options (caching or not, additional lists) 2020-12-03 20:43:04 +01:00
TC
7b20b9339d Download plugin - advanced options (folder, FFmpeg args) 2020-12-03 20:43:04 +01:00
TC
4d4a1f038b Custom plugin options 2020-12-03 20:42:58 +01:00
TC
e5ec79e345 Watch changes in config and update menu 2020-12-03 18:31:01 +01:00
TC
f4fe5c2a58 Allow editing config (advanced) 2020-12-03 18:25:31 +01:00
TC
a5130c1d3f Move migrations into separate const 2020-12-03 18:25:08 +01:00
TC
8ab2da0482 Refactor config for simpler use and advanced options in plugins 2020-12-03 18:16:37 +01:00
35 changed files with 1363 additions and 566 deletions

39
config/defaults.js Normal file
View File

@ -0,0 +1,39 @@
const defaultConfig = {
"window-size": {
width: 1100,
height: 550,
},
url: "https://music.youtube.com",
options: {
tray: false,
appVisible: true,
autoUpdates: true,
hideMenu: false,
startAtLogin: false,
disableHardwareAcceleration: false,
restartOnConfigChanges: false,
trayClickPlayPause: false,
},
plugins: {
// Enabled plugins
navigation: {
enabled: true,
},
shortcuts: {
enabled: true,
},
adblocker: {
enabled: true,
cache: true,
additionalBlockLists: [], // Additional list of filters, e.g "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"
},
// Disabled plugins
downloader: {
enabled: false,
ffmpegArgs: [], // e.g. ["-b:a", "192k"] for an audio bitrate of 192kb/s
downloadFolder: undefined, // Custom download folder (absolute path)
},
},
};
module.exports = defaultConfig;

21
config/index.js Normal file
View File

@ -0,0 +1,21 @@
const plugins = require("./plugins");
const store = require("./store");
const set = (key, value) => {
store.set(key, value);
};
const get = (key) => {
return store.get(key);
};
module.exports = {
get,
set,
edit: () => store.openInEditor(),
watch: (cb) => {
store.onDidChange("options", cb);
store.onDidChange("plugins", cb);
},
plugins,
};

41
config/plugins.js Normal file
View File

@ -0,0 +1,41 @@
const store = require("./store");
function getEnabled() {
const plugins = store.get("plugins");
const enabledPlugins = Object.entries(plugins).filter(([plugin, options]) =>
isEnabled(plugin)
);
return enabledPlugins;
}
function isEnabled(plugin) {
const pluginConfig = store.get("plugins")[plugin];
return pluginConfig !== undefined && pluginConfig.enabled;
}
function setOptions(plugin, options) {
const plugins = store.get("plugins");
store.set("plugins", {
...plugins,
[plugin]: {
...plugins[plugin],
...options,
},
});
}
function enable(plugin) {
setOptions(plugin, { enabled: true });
}
function disable(plugin) {
setOptions(plugin, { enabled: false });
}
module.exports = {
isEnabled,
getEnabled,
enable,
disable,
setOptions,
};

40
config/store.js Normal file
View File

@ -0,0 +1,40 @@
const Store = require("electron-store");
const defaults = require("./defaults");
const migrations = {
">=1.7.0": (store) => {
const enabledPlugins = store.get("plugins");
if (!Array.isArray(enabledPlugins)) {
console.warn("Plugins are not in array format, cannot migrate");
return;
}
// Include custom options
const plugins = {
adblocker: {
enabled: true,
cache: true,
additionalBlockLists: [],
},
downloader: {
enabled: false,
ffmpegArgs: [], // e.g. ["-b:a", "192k"] for an audio bitrate of 192kb/s
downloadFolder: undefined, // Custom download folder (absolute path)
},
};
enabledPlugins.forEach((enabledPlugin) => {
plugins[enabledPlugin] = {
...plugins[enabledPlugin],
enabled: true,
};
});
store.set("plugins", plugins);
},
};
module.exports = new Store({
defaults,
clearInvalidConfig: false,
migrations,
});

View File

@ -5,18 +5,8 @@ const electron = require("electron");
const is = require("electron-is");
const { autoUpdater } = require("electron-updater");
const config = require("./config");
const { setApplicationMenu } = require("./menu");
const {
autoUpdate,
disableHardwareAcceleration,
getEnabledPlugins,
hideMenu,
isAppVisible,
isTrayEnabled,
setOptions,
store,
startAtLogin,
} = require("./store");
const { fileExists, injectCSS } = require("./plugins/utils");
const { isTesting } = require("./utils/testing");
const { setUpTray } = require("./tray");
@ -28,7 +18,7 @@ app.commandLine.appendSwitch(
"--experimental-wasm-threads --experimental-wasm-bulk-memory"
);
app.allowRendererProcessReuse = true; // https://github.com/electron/electron/issues/18397
if (disableHardwareAcceleration()) {
if (config.get("options.disableHardwareAcceleration")) {
if (is.dev()) {
console.log("Disabling hardware acceleration");
}
@ -64,19 +54,19 @@ function loadPlugins(win) {
}
});
getEnabledPlugins().forEach((plugin) => {
config.plugins.getEnabled().forEach(([plugin, options]) => {
console.log("Loaded plugin - " + plugin);
const pluginPath = path.join(__dirname, "plugins", plugin, "back.js");
fileExists(pluginPath, () => {
const handle = require(pluginPath);
handle(win);
handle(win, options);
});
});
}
function createMainWindow() {
const windowSize = store.get("window-size");
const windowMaximized = store.get("window-maximized");
const windowSize = config.get("window-size");
const windowMaximized = config.get("window-maximized");
const win = new electron.BrowserWindow({
icon: icon,
@ -85,40 +75,52 @@ function createMainWindow() {
backgroundColor: "#000",
show: false,
webPreferences: {
nodeIntegration: isTesting(), // Only necessary when testing with Spectron
// TODO: re-enable contextIsolation once it can work with ffmepg.wasm
// Possible bundling? https://github.com/ffmpegwasm/ffmpeg.wasm/issues/126
contextIsolation: false,
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
...(isTesting()
? {
// Only necessary when testing with Spectron
contextIsolation: false,
nodeIntegration: true,
}
: undefined),
},
frame: !is.macOS(),
titleBarStyle: is.macOS() ? "hiddenInset" : "default",
autoHideMenuBar: hideMenu(),
autoHideMenuBar: config.get("options.hideMenu"),
});
if (windowMaximized) {
win.maximize();
}
win.webContents.loadURL(store.get("url"));
win.webContents.loadURL(config.get("url"));
win.on("closed", onClosed);
win.on("move", () => {
let position = win.getPosition();
store.set("window-position", { x: position[0], y: position[1] });
config.set("window-position", { x: position[0], y: position[1] });
});
win.on("resize", () => {
const windowSize = win.getSize();
store.set("window-maximized", win.isMaximized());
config.set("window-maximized", win.isMaximized());
if (!win.isMaximized()) {
store.set("window-size", { width: windowSize[0], height: windowSize[1] });
config.set("window-size", {
width: windowSize[0],
height: windowSize[1],
});
}
});
win.once("ready-to-show", () => {
if (isAppVisible()) {
if (config.get("options.appVisible")) {
win.show();
}
});
@ -143,7 +145,7 @@ app.on("browser-window-created", (event, win) => {
win.webContents.on("did-navigate-in-page", () => {
const url = win.webContents.getURL();
if (url.startsWith("https://music.youtube.com")) {
store.set("url", url);
config.set("url", url);
}
});
@ -196,14 +198,20 @@ app.on("activate", () => {
app.on("ready", () => {
mainWindow = createMainWindow();
setApplicationMenu(mainWindow);
if (config.get("options.restartOnConfigChanges")) {
config.watch(() => {
app.relaunch();
app.exit();
});
}
setUpTray(app, mainWindow);
// Autostart at login
app.setLoginItemSettings({
openAtLogin: startAtLogin(),
openAtLogin: config.get("options.startAtLogin"),
});
if (!is.dev() && autoUpdate()) {
if (!is.dev() && config.get("options.autoUpdates")) {
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on("update-available", () => {
const downloadLink =
@ -223,7 +231,7 @@ app.on("ready", () => {
break;
// Disable updates
case 2:
setOptions({ autoUpdates: false });
config.set("options.autoUpdates", false);
break;
default:
break;
@ -234,7 +242,7 @@ app.on("ready", () => {
// Optimized for Mac OS X
if (is.macOS()) {
if (!isAppVisible()) {
if (!config.get("options.appVisible")) {
app.dock.hide();
}
}
@ -244,7 +252,7 @@ app.on("ready", () => {
forceQuit = true;
});
if (is.macOS() || isTrayEnabled()) {
if (is.macOS() || config.get("options.tray")) {
mainWindow.on("close", (event) => {
// Hide the window instead of quitting (quit is available in tray options)
if (!forceQuit) {

111
menu.js
View File

@ -2,36 +2,34 @@ const { app, Menu } = require("electron");
const is = require("electron-is");
const { getAllPlugins } = require("./plugins/utils");
const {
isPluginEnabled,
enablePlugin,
disablePlugin,
autoUpdate,
hideMenu,
isAppVisible,
isTrayEnabled,
setOptions,
startAtLogin,
disableHardwareAcceleration,
} = require("./store");
const config = require("./config");
const mainMenuTemplate = (win) => [
{
label: "Plugins",
submenu: getAllPlugins().map((plugin) => {
return {
label: plugin,
type: "checkbox",
checked: isPluginEnabled(plugin),
click: (item) => {
if (item.checked) {
enablePlugin(plugin);
} else {
disablePlugin(plugin);
}
submenu: [
...getAllPlugins().map((plugin) => {
return {
label: plugin,
type: "checkbox",
checked: config.plugins.isEnabled(plugin),
click: (item) => {
if (item.checked) {
config.plugins.enable(plugin);
} else {
config.plugins.disable(plugin);
}
},
};
}),
{ type: "separator" },
{
label: "Advanced options",
click: () => {
config.edit();
},
};
}),
},
],
},
{
label: "Options",
@ -39,17 +37,25 @@ const mainMenuTemplate = (win) => [
{
label: "Auto-update",
type: "checkbox",
checked: autoUpdate(),
checked: config.get("options.autoUpdates"),
click: (item) => {
setOptions({ autoUpdates: item.checked });
config.set("options.autoUpdates", item.checked);
},
},
{
label: "Disable hardware acceleration",
type: "checkbox",
checked: disableHardwareAcceleration(),
checked: config.get("options.disableHardwareAcceleration"),
click: (item) => {
setOptions({ disableHardwareAcceleration: item.checked });
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);
},
},
...(is.windows() || is.linux()
@ -57,9 +63,9 @@ const mainMenuTemplate = (win) => [
{
label: "Hide menu",
type: "checkbox",
checked: hideMenu(),
checked: config.get("options.hideMenu"),
click: (item) => {
setOptions({ hideMenu: item.checked });
config.set("options.hideMenu", item.checked);
},
},
]
@ -71,9 +77,9 @@ const mainMenuTemplate = (win) => [
{
label: "Start at login",
type: "checkbox",
checked: startAtLogin(),
checked: config.get("options.startAtLogin"),
click: (item) => {
setOptions({ startAtLogin: item.checked });
config.set("options.startAtLogin", item.checked);
},
},
]
@ -84,23 +90,44 @@ const mainMenuTemplate = (win) => [
{
label: "Disabled",
type: "radio",
checked: !isTrayEnabled(),
click: () => setOptions({ tray: false, appVisible: true }),
checked: !config.get("options.tray"),
click: () => {
config.set("options.tray", false);
config.set("options.appVisible", true);
},
},
{
label: "Enabled + app visible",
type: "radio",
checked: isTrayEnabled() && isAppVisible(),
click: () => setOptions({ tray: true, appVisible: true }),
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: isTrayEnabled() && !isAppVisible(),
click: () => setOptions({ tray: true, appVisible: false }),
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: "Toggle DevTools",
// Cannot use "toggleDevTools" role in MacOS
@ -114,6 +141,12 @@ const mainMenuTemplate = (win) => [
}
},
},
{
label: "Advanced options",
click: () => {
config.edit();
},
},
],
},
];

View File

@ -1,7 +1,7 @@
{
"name": "youtube-music",
"productName": "YouTube Music",
"version": "1.6.5",
"version": "1.9.0",
"description": "YouTube Music Desktop App - including custom plugins",
"license": "MIT",
"repository": "th-ch/youtube-music",
@ -18,11 +18,19 @@
"icon": "assets/generated/icons/mac/icon.icns"
},
"win": {
"icon": "assets/generated/icons/win/icon.ico"
"icon": "assets/generated/icons/win/icon.ico",
"target": ["nsis", "portable"]
},
"linux": {
"icon": "assets/generated/icons/png",
"category": "AudioVideo"
"category": "AudioVideo",
"target": [
"AppImage",
"snap",
"freebsd",
"deb",
"rpm"
]
}
},
"scripts": {
@ -48,29 +56,30 @@
"npm": "Please use yarn and not npm"
},
"dependencies": {
"@cliqz/adblocker-electron": "^1.18.6",
"@ffmpeg/core": "^0.8.4",
"@ffmpeg/ffmpeg": "^0.9.5",
"@cliqz/adblocker-electron": "^1.19.0",
"@ffmpeg/core": "^0.8.5",
"@ffmpeg/ffmpeg": "^0.9.6",
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.0",
"discord-rpc": "^3.1.4",
"downloads-folder": "^3.0.1",
"electron-debug": "^3.1.0",
"electron-debug": "^3.2.0",
"electron-is": "^3.0.0",
"electron-localshortcut": "^3.2.1",
"electron-store": "^6.0.1",
"electron-updater": "^4.3.5",
"electron-updater": "^4.3.6",
"filenamify": "^4.2.0",
"node-fetch": "^2.6.1",
"ytdl-core": "^4.1.1"
"ytdl-core": "^4.1.2"
},
"devDependencies": {
"electron": "^10.1.3",
"electron": "^11.1.1",
"electron-builder": "^22.8.1",
"electron-devtools-installer": "^3.1.1",
"electron-icon-maker": "0.0.5",
"get-port": "^5.1.1",
"jest": "^26.4.2",
"rimraf": "^3.0.2",
"spectron": "^12.0.0",
"spectron": "^13.0.0",
"xo": "^0.33.1"
},
"resolutions": {

View File

@ -1,2 +1,8 @@
const { loadAdBlockerEngine } = require("./blocker");
module.exports = (win) => loadAdBlockerEngine(win.webContents.session);
module.exports = (win, options) =>
loadAdBlockerEngine(
win.webContents.session,
options.cache,
options.additionalBlockLists,
options.disableDefaultLists
);

View File

@ -6,19 +6,32 @@ const fetch = require("node-fetch");
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",
];
const loadAdBlockerEngine = (session = undefined) =>
ElectronBlocker.fromLists(
fetch,
SOURCES,
{},
{
path: path.resolve(__dirname, "ad-blocker-engine.bin"),
read: promises.readFile,
write: promises.writeFile,
}
)
const loadAdBlockerEngine = (
session = undefined,
cache = true,
additionalBlockLists = [],
disableDefaultLists = false
) => {
// Only use cache if no additional blocklists are passed
const cachingOptions =
cache && additionalBlockLists.length === 0
? {
path: path.resolve(__dirname, "ad-blocker-engine.bin"),
read: promises.readFile,
write: promises.writeFile,
}
: undefined;
const lists = [
...(disableDefaultLists ? [] : SOURCES),
...additionalBlockLists,
];
ElectronBlocker.fromLists(fetch, lists, {}, cachingOptions)
.then((blocker) => {
if (session) {
blocker.enableBlockingInSession(session);
@ -27,6 +40,7 @@ const loadAdBlockerEngine = (session = undefined) =>
}
})
.catch((err) => console.log("Error loading adBlocker engine", err));
};
module.exports = { loadAdBlockerEngine };
if (require.main === module) {

View File

@ -0,0 +1,4 @@
module.exports = () => {
// Preload adblocker to inject scripts/styles
require("@cliqz/adblocker-electron-preload/dist/preload.cjs");
};

51
plugins/discord/back.js Normal file
View File

@ -0,0 +1,51 @@
const Discord = require("discord-rpc");
const getSongInfo = require("../../providers/song-info");
const rpc = new Discord.Client({
transport: "ipc",
});
// Application ID registered by @semvis123
const clientId = "790655993809338398";
module.exports = (win) => {
const registerCallback = getSongInfo(win);
// If the page is ready, register the callback
win.on("ready-to-show", () => {
rpc.on("ready", () => {
// Register the callback
registerCallback((songInfo) => {
// Song information changed, so lets update the rich presence
const activityInfo = {
details: songInfo.title,
state: songInfo.artist,
largeImageKey: "logo",
largeImageText: songInfo.views + " - " + songInfo.likes,
};
if (songInfo.isPaused) {
// Add an idle icon to show that the song is paused
activityInfo.smallImageKey = "idle";
activityInfo.smallImageText = "idle/paused";
} else {
// Add the start and end time of the song
const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000;
activityInfo.startTimestamp = songStartTime;
activityInfo.endTimestamp =
songStartTime + songInfo.songDuration * 1000;
}
rpc.setActivity(activityInfo);
});
});
// Startup the rpc client
rpc
.login({
clientId,
})
.catch(console.error);
});
};

View File

@ -1,6 +1,7 @@
const CHANNEL = "downloader";
const ACTIONS = {
ERROR: "error",
METADATA: "metadata",
};
module.exports = {

View File

@ -2,6 +2,7 @@ const { join } = require("path");
const { dialog } = require("electron");
const getSongInfo = require("../../providers/song-info");
const { injectCSS, listenAction } = require("../utils");
const { ACTIONS, CHANNEL } = require("./actions.js");
@ -16,14 +17,26 @@ const sendError = (win, err) => {
dialog.showMessageBox(dialogOpts);
};
let metadata = {};
function handle(win) {
injectCSS(win.webContents, join(__dirname, "style.css"));
const registerCallback = getSongInfo(win);
registerCallback((info) => {
metadata = {
...info,
image: info.image ? info.image.toDataURL() : undefined,
};
});
listenAction(CHANNEL, (event, action, error) => {
switch (action) {
case ACTIONS.ERROR:
sendError(win, error);
break;
case ACTIONS.METADATA:
event.returnValue = JSON.stringify(metadata);
break;
default:
console.log("Unknown action: " + action);
}

View File

@ -1,3 +1,5 @@
const { contextBridge } = require("electron");
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
const { ACTIONS, CHANNEL } = require("./actions.js");
const { downloadVideoToMP3 } = require("./youtube-dl");
@ -7,6 +9,7 @@ let progress = null;
const downloadButton = ElementFromFile(
templatePath(__dirname, "download.html")
);
let pluginOptions = {};
const observer = new MutationObserver((mutations, observer) => {
if (!menu) {
@ -27,6 +30,9 @@ const reinit = () => {
}
};
// TODO: re-enable once contextIsolation is set to true
// contextBridge.exposeInMainWorld("downloader", {
// download: () => {
global.download = () => {
const videoUrl = window.location.href;
@ -43,11 +49,14 @@ global.download = () => {
triggerAction(CHANNEL, ACTIONS.ERROR, error);
reinit();
},
reinit
reinit,
pluginOptions
);
};
// });
function observeMenu() {
function observeMenu(options) {
pluginOptions = { ...pluginOptions, ...options };
observer.observe(document, {
childList: true,
subtree: true,

View File

@ -12,6 +12,9 @@ const filenamify = require("filenamify");
const FFmpeg = require("@ffmpeg/ffmpeg/dist/ffmpeg.min");
const ytdl = require("ytdl-core");
const { triggerActionSync } = require("../utils");
const { ACTIONS, CHANNEL } = require("./actions.js");
const { createFFmpeg } = FFmpeg;
const ffmpeg = createFFmpeg({
log: false,
@ -19,7 +22,13 @@ const ffmpeg = createFFmpeg({
progress: () => {}, // console.log,
});
const downloadVideoToMP3 = (videoUrl, sendFeedback, sendError, reinit) => {
const downloadVideoToMP3 = (
videoUrl,
sendFeedback,
sendError,
reinit,
options
) => {
sendFeedback("Downloading…");
let videoName = "YouTube Music - Unknown title";
@ -48,18 +57,31 @@ const downloadVideoToMP3 = (videoUrl, sendFeedback, sendError, reinit) => {
.on("info", (info, format) => {
videoName = info.videoDetails.title.replace("|", "").toString("ascii");
if (is.dev()) {
console.log("Downloading video - name:", videoName);
console.log(
"Downloading video - name:",
videoName,
"- quality:",
format.audioBitrate + "kbits/s"
);
}
})
.on("error", sendError)
.on("end", () => {
const buffer = Buffer.concat(chunks);
toMP3(videoName, buffer, sendFeedback, sendError, reinit);
toMP3(videoName, buffer, sendFeedback, sendError, reinit, options);
});
};
const toMP3 = async (videoName, buffer, sendFeedback, sendError, reinit) => {
const toMP3 = async (
videoName,
buffer,
sendFeedback,
sendError,
reinit,
options
) => {
const safeVideoName = randomBytes(32).toString("hex");
const extension = options.extension || "mp3";
try {
if (!ffmpeg.isLoaded()) {
@ -71,12 +93,21 @@ const toMP3 = async (videoName, buffer, sendFeedback, sendError, reinit) => {
ffmpeg.FS("writeFile", safeVideoName, buffer);
sendFeedback("Converting…");
await ffmpeg.run("-i", safeVideoName, safeVideoName + ".mp3");
await ffmpeg.run(
"-i",
safeVideoName,
...getFFmpegMetadataArgs(),
...(options.ffmpegArgs || []),
safeVideoName + "." + extension
);
const filename = filenamify(videoName + ".mp3", { replacement: "_" });
const folder = options.downloadFolder || downloadsFolder();
const filename = filenamify(videoName + "." + extension, {
replacement: "_",
});
writeFileSync(
join(downloadsFolder(), filename),
ffmpeg.FS("readFile", safeVideoName + ".mp3")
join(folder, filename),
ffmpeg.FS("readFile", safeVideoName + "." + extension)
);
reinit();
@ -85,6 +116,20 @@ const toMP3 = async (videoName, buffer, sendFeedback, sendError, reinit) => {
}
};
const getFFmpegMetadataArgs = () => {
const metadata = JSON.parse(triggerActionSync(CHANNEL, ACTIONS.METADATA));
if (!metadata) {
return;
}
return [
"-metadata",
`title=${metadata.title}`,
"-metadata",
`artist=${metadata.artist}`,
];
};
module.exports = {
downloadVideoToMP3,
};

View File

@ -1,24 +1,24 @@
const { triggerAction } = require('../utils');
const { triggerAction } = require("../utils");
const CHANNEL = "navigation";
const ACTIONS = {
NEXT: "next",
BACK: 'back',
}
NEXT: "next",
BACK: "back",
};
function goToNextPage() {
triggerAction(CHANNEL, ACTIONS.NEXT);
triggerAction(CHANNEL, ACTIONS.NEXT);
}
function goToPreviousPage() {
triggerAction(CHANNEL, ACTIONS.BACK);
triggerAction(CHANNEL, ACTIONS.BACK);
}
module.exports = {
CHANNEL: CHANNEL,
ACTIONS: ACTIONS,
global: {
goToNextPage: goToNextPage,
goToPreviousPage: goToPreviousPage,
}
CHANNEL: CHANNEL,
ACTIONS: ACTIONS,
actions: {
goToNextPage: goToNextPage,
goToPreviousPage: goToPreviousPage,
},
};

View File

@ -1,23 +1,23 @@
const path = require("path");
const { injectCSS, listenAction } = require("../utils");
const { ACTIONS, CHANNEL } = require("./actions.js");
const { ACTIONS, CHANNEL } = require("./actions.js");
function handle(win) {
injectCSS(win.webContents, path.join(__dirname, "style.css"));
listenAction(CHANNEL, (event, action) => {
switch (action) {
case ACTIONS.NEXT:
case ACTIONS.NEXT:
if (win.webContents.canGoForward()) {
win.webContents.goForward();
}
break;
case ACTIONS.BACK:
case ACTIONS.BACK:
if (win.webContents.canGoBack()) {
win.webContents.goBack();
}
break;
default:
default:
console.log("Unknown action: " + action);
}
});

View File

@ -1,18 +0,0 @@
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,
},
};

View File

@ -1,33 +1,35 @@
const { nativeImage, Notification } = require("electron");
const { Notification } = require("electron");
const { listenAction } = require("../utils");
const { ACTIONS, CHANNEL } = require("./actions.js");
const getSongInfo = require("../../providers/song-info");
function notify(info) {
const notify = (info) => {
let notificationImage = "assets/youtube-music.png";
if (info.image) {
notificationImage = nativeImage.createFromDataURL(info.image);
notificationImage = info.image.resize({ height: 256, width: 256 });
}
// Fill the notification with content
const notification = {
title: info.title || "Playing",
body: info.artist,
icon: notificationImage,
silent: true,
};
// Send the notification
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 = (win) => {
const registerCallback = getSongInfo(win);
win.on("ready-to-show", () => {
// Register the callback for new song information
registerCallback((songInfo) => {
// If song is playing send notification
if (!songInfo.isPaused) {
notify(songInfo);
}
});
});
}
module.exports = listenAndNotify;
};

View File

@ -1,86 +0,0 @@
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;

View File

@ -1,12 +1,7 @@
const { globalShortcut } = require("electron");
const electronLocalshortcut = require("electron-localshortcut");
const {
playPause,
nextTrack,
previousTrack,
startSearch
} = require("./youtube.js");
const getSongControls = require("../../providers/song-controls");
function _registerGlobalShortcut(webContents, shortcut, action) {
globalShortcut.register(shortcut, () => {
@ -21,11 +16,13 @@ function _registerLocalShortcut(win, shortcut, action) {
}
function registerShortcuts(win) {
const { playPause, next, previous, search } = getSongControls(win);
_registerGlobalShortcut(win.webContents, "MediaPlayPause", playPause);
_registerGlobalShortcut(win.webContents, "MediaNextTrack", nextTrack);
_registerGlobalShortcut(win.webContents, "MediaPreviousTrack", previousTrack);
_registerLocalShortcut(win, "CommandOrControl+F", startSearch);
_registerLocalShortcut(win, "CommandOrControl+L", startSearch);
_registerGlobalShortcut(win.webContents, "MediaNextTrack", next);
_registerGlobalShortcut(win.webContents, "MediaPreviousTrack", previous);
_registerLocalShortcut(win, "CommandOrControl+F", search);
_registerLocalShortcut(win, "CommandOrControl+L", search);
}
module.exports = registerShortcuts;

View File

@ -1,29 +0,0 @@
function _keyboardInput(webContents, key) {
return webContents.sendInputEvent({
type : "keydown",
keyCode: key
});
}
function playPause(webContents) {
return _keyboardInput(webContents, "Space");
}
function nextTrack(webContents) {
return _keyboardInput(webContents, "j");
}
function previousTrack(webContents) {
return _keyboardInput(webContents, "k");
}
function startSearch(webContents) {
return _keyboardInput(webContents, "/");
}
module.exports = {
playPause : playPause,
nextTrack : nextTrack,
previousTrack: previousTrack,
startSearch : startSearch
};

87
plugins/touchbar/back.js Normal file
View File

@ -0,0 +1,87 @@
const { TouchBar } = require("electron");
const {
TouchBarButton,
TouchBarLabel,
TouchBarSpacer,
TouchBarSegmentedControl,
TouchBarScrubber,
} = TouchBar;
const getSongInfo = require("../../providers/song-info");
const getSongControls = require("../../providers/song-controls");
// Songtitle label
const songTitle = new TouchBarLabel({
label: "",
});
// This will store the song controls once available
let controls = [];
// This will store the song image once available
const songImage = {};
// Pause/play button
const pausePlayButton = new TouchBarButton();
// The song control buttons (control functions are in the same order)
const buttons = new TouchBarSegmentedControl({
mode: "buttons",
segments: [
new TouchBarButton({
label: "⏮",
}),
pausePlayButton,
new TouchBarButton({
label: "⏭",
}),
new TouchBarButton({
label: "👎",
}),
new TouchBarButton({
label: "👍",
}),
],
change: (i) => controls[i](),
});
// This is the touchbar object, this combines everything with proper layout
const touchBar = new TouchBar({
items: [
new TouchBarScrubber({
items: [songImage, songTitle],
continuous: false,
}),
new TouchBarSpacer({
size: "flexible",
}),
buttons,
],
});
module.exports = (win) => {
const registerCallback = getSongInfo(win);
const { playPause, next, previous, like, dislike } = getSongControls(win);
// If the page is ready, register the callback
win.on("ready-to-show", () => {
controls = [previous, playPause, next, like, dislike];
// Register the callback
registerCallback((songInfo) => {
// Song information changed, so lets update the touchBar
// Set the song title
songTitle.label = songInfo.title;
// Changes the pause button if paused
pausePlayButton.label = songInfo.isPaused ? "▶️" : "⏸";
// Get image source
songImage.icon = songInfo.image
? songInfo.image.resize({ height: 23 })
: null;
win.setTouchBar(touchBar);
});
});
};

View File

@ -4,15 +4,15 @@ const path = require("path");
const { ipcMain, ipcRenderer } = require("electron");
// Creates a DOM element from a HTML string
module.exports.ElementFromHtml = html => {
var template = document.createElement("template");
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
module.exports.ElementFromHtml = (html) => {
var template = document.createElement("template");
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
};
// Creates a DOM element from a HTML file
module.exports.ElementFromFile = filepath => {
module.exports.ElementFromFile = (filepath) => {
return module.exports.ElementFromHtml(fs.readFileSync(filepath, "utf8"));
};
@ -24,12 +24,16 @@ module.exports.triggerAction = (channel, action, ...args) => {
return ipcRenderer.send(channel, action, ...args);
};
module.exports.triggerActionSync = (channel, action, ...args) => {
return ipcRenderer.sendSync(channel, action, ...args);
};
module.exports.listenAction = (channel, callback) => {
return ipcMain.on(channel, callback);
};
module.exports.fileExists = (path, callbackIfExists) => {
fs.access(path, fs.F_OK, err => {
fs.access(path, fs.F_OK, (err) => {
if (err) {
return;
}
@ -45,10 +49,10 @@ module.exports.injectCSS = (webContents, filepath) => {
};
module.exports.getAllPlugins = () => {
const isDirectory = source => fs.lstatSync(source).isDirectory();
const isDirectory = (source) => fs.lstatSync(source).isDirectory();
return fs
.readdirSync(__dirname)
.map(name => path.join(__dirname, name))
.map((name) => path.join(__dirname, name))
.filter(isDirectory)
.map(name => path.basename(name));
.map((name) => path.basename(name));
};

View File

@ -1,32 +1,35 @@
const path = require("path");
const { remote } = require("electron");
const { contextBridge, remote } = require("electron");
const { getEnabledPlugins, store } = require("./store");
const { fileExists } = require("./plugins/utils");
const config = require("./config");
const { fileExists } = require("./plugins/utils");
const plugins = getEnabledPlugins();
const plugins = config.plugins.getEnabled();
plugins.forEach(plugin => {
plugins.forEach(([plugin, options]) => {
const pluginPath = path.join(__dirname, "plugins", plugin, "actions.js");
fileExists(pluginPath, () => {
const actions = require(pluginPath).global || {};
Object.keys(actions).forEach(actionName => {
const actions = require(pluginPath).actions || {};
// TODO: re-enable once contextIsolation is set to true
// contextBridge.exposeInMainWorld(plugin + "Actions", actions);
Object.keys(actions).forEach((actionName) => {
global[actionName] = actions[actionName];
});
});
});
document.addEventListener("DOMContentLoaded", () => {
plugins.forEach(plugin => {
plugins.forEach(([plugin, options]) => {
const pluginPath = path.join(__dirname, "plugins", plugin, "front.js");
fileExists(pluginPath, () => {
const run = require(pluginPath);
run();
run(options);
});
});
// Add action for reloading
global.reload = () =>
remote.getCurrentWindow().webContents.loadURL(store.get("url"));
remote.getCurrentWindow().webContents.loadURL(config.get("url"));
});

View File

@ -0,0 +1,18 @@
// This is used for to control the songs
const pressKey = (window, key) => {
window.webContents.sendInputEvent({
type: "keydown",
keyCode: key,
});
};
module.exports = (win) => {
return {
previous: () => pressKey(win, "k"),
next: () => pressKey(win, "j"),
playPause: () => pressKey(win, "space"),
like: () => pressKey(win, "_"),
dislike: () => pressKey(win, "+"),
search: () => pressKey(win, "/"),
};
};

137
providers/song-info.js Normal file
View File

@ -0,0 +1,137 @@
const { nativeImage } = require("electron");
const fetch = require("node-fetch");
// This selects the song title
const titleSelector = ".title.style-scope.ytmusic-player-bar";
// This selects the song image
const imageSelector =
"#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > img";
// This selects the song subinfo, this includes artist, views, likes
const subInfoSelector =
"#layout > ytmusic-player-bar > div.middle-controls.style-scope.ytmusic-player-bar > div.content-info-wrapper.style-scope.ytmusic-player-bar > span";
// This selects the progress bar, used for songlength and current progress
const progressSelector = "#progress-bar";
// Grab the title using the selector
const getTitle = (win) => {
return win.webContents
.executeJavaScript(
"document.querySelector('" + titleSelector + "').innerText"
)
.catch((error) => {
console.log(error);
});
};
// Grab the image src using the selector
const getImageSrc = (win) => {
return win.webContents
.executeJavaScript("document.querySelector('" + imageSelector + "').src")
.catch((error) => {
console.log(error);
});
};
// Grab the subinfo using the selector
const getSubInfo = async (win) => {
// Get innerText of subinfo element
const subInfoString = await win.webContents.executeJavaScript(
'document.querySelector("' + subInfoSelector + '").innerText'
);
// Split and clean the string
const splittedSubInfo = subInfoString.replaceAll("\n", "").split(" • ");
// Make sure we always return 3 elements in the aray
const subInfo = [];
for (let i = 0; i < 3; i++) {
// Fill array with empty string if not defined
subInfo.push(splittedSubInfo[i] || "");
}
return subInfo;
};
// Grab the progress using the selector
const getProgress = async (win) => {
// Get max value of the progressbar element
const songDuration = await win.webContents.executeJavaScript(
'document.querySelector("' + progressSelector + '").max'
);
// Get current value of the progressbar element
const elapsedSeconds = await win.webContents.executeJavaScript(
'document.querySelector("' + progressSelector + '").value'
);
return { songDuration, 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 getPausedStatus = async (win) => {
const title = await win.webContents.executeJavaScript("document.title");
return !title.includes("-");
};
// Fill songInfo with empty values
const songInfo = {
title: "",
artist: "",
views: "",
likes: "",
imageSrc: "",
image: null,
isPaused: true,
songDuration: 0,
elapsedSeconds: 0,
};
const registerProvider = (win) => {
// This variable will be filled with the callbacks once they register
const callbacks = [];
// This function will allow plugins to register callback that will be triggered when data changes
const registerCallback = (callback) => {
callbacks.push(callback);
};
win.on("page-title-updated", async () => {
// Save the old title temporarily
const oldTitle = songInfo.title;
// Get and set the new data
songInfo.title = await getTitle(win);
songInfo.isPaused = await getPausedStatus(win);
const { songDuration, elapsedSeconds } = await getProgress(win);
songInfo.songDuration = songDuration;
songInfo.elapsedSeconds = elapsedSeconds;
// If title changed then we do need to update other info
if (oldTitle !== songInfo.title) {
const subInfo = await getSubInfo(win);
songInfo.artist = subInfo[0];
songInfo.views = subInfo[1];
songInfo.likes = subInfo[2];
songInfo.imageSrc = await getImageSrc(win);
songInfo.image = await getImage(songInfo.imageSrc);
}
// Trigger the callbacks
callbacks.forEach((c) => {
c(songInfo);
});
});
return registerCallback;
};
module.exports = registerProvider;

View File

@ -1,13 +1,24 @@
# YouTube Music
[![GitHub release](https://img.shields.io/github/release/th-ch/youtube-music.svg)](https://GitHub.com/th-ch/youtube-music/releases/)
[![GitHub license](https://img.shields.io/github/license/th-ch/youtube-music.svg)](https://github.com/th-ch/youtube-music/blob/master/LICENSE)
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![Build status](https://github.com/th-ch/youtube-music/workflows/Build%20YouTube%20Music/badge.svg)](https://GitHub.com/th-ch/youtube-music/releases/)
[![Known Vulnerabilities](https://snyk.io/test/github/th-ch/youtube-music/badge.svg)](https://snyk.io/test/github/th-ch/youtube-music)
![GitHub All Releases](https://img.shields.io/github/downloads/th-ch/youtube-music/total)
<div align="center">
![Screenshot](screenshot.jpg "Screenshot")
[![GitHub release](https://img.shields.io/github/release/th-ch/youtube-music.svg?style=for-the-badge&logo=youtube-music)](https://github.com/th-ch/youtube-music/releases/)
[![GitHub license](https://img.shields.io/github/license/th-ch/youtube-music.svg?style=for-the-badge)](https://github.com/th-ch/youtube-music/blob/master/LICENSE)
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg?style=for-the-badge)](https://github.com/sindresorhus/xo)
[![Build status](https://img.shields.io/github/workflow/status/th-ch/youtube-music/Build%20YouTube%20Music?style=for-the-badge&logo=youtube-music)](https://GitHub.com/th-ch/youtube-music/releases/)
[![Known Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/th-ch/youtube-music?style=for-the-badge)](https://snyk.io/test/github/th-ch/youtube-music)
[![GitHub All Releases](https://img.shields.io/github/downloads/th-ch/youtube-music/total?style=for-the-badge&logo=youtube-music)](https://GitHub.com/th-ch/youtube-music/releases/)
[![AUR](https://img.shields.io/aur/version/youtube-music-bin?color=blueviolet&style=for-the-badge&logo=youtube-music)](https://aur.archlinux.org/packages/youtube-music-bin)
</div>
![Screenshot](web/screenshot.jpg "Screenshot")
<div align="center">
<a href="https://github.com/th-ch/youtube-music/releases/latest">
<img src="web/youtube-music.svg" width="400" height="100">
</a>
</div>
**Electron wrapper around YouTube Music featuring:**
@ -18,7 +29,7 @@
You can check out the [latest release](https://github.com/th-ch/youtube-music/releases/latest) to quickly find the latest version.
**Arch Linux**
### Arch Linux
Install the `youtube-music-bin` package from the AUR. For AUR installation instructions, take a look at this [wiki page](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages).
@ -32,6 +43,7 @@ Install the `youtube-music-bin` package from the AUR. For AUR installation instr
- **Auto confirm when paused**: when the "Continue Watching?" modal appears, automatically click "Yes"
- **Hide video player**: no video in the interface when playing music
- **Notifications**: display a notification when a song starts playing
- **Touchbar**: custom TouchBar layout for macOS
## Dev

View File

@ -1,40 +0,0 @@
const Store = require("electron-store");
const plugins = require("./plugins");
const store = new Store({
defaults: {
"window-size": {
width: 1100,
height: 550,
},
url: "https://music.youtube.com",
plugins: ["navigation", "shortcuts", "adblocker"],
options: {
tray: false,
appVisible: true,
autoUpdates: true,
hideMenu: false,
startAtLogin: false,
disableHardwareAcceleration: false,
},
},
});
module.exports = {
store: store,
// Plugins
isPluginEnabled: plugin => plugins.isEnabled(store, plugin),
getEnabledPlugins: () => plugins.getEnabledPlugins(store),
enablePlugin: plugin => plugins.enablePlugin(store, plugin),
disablePlugin: plugin => plugins.disablePlugin(store, plugin),
// Options
setOptions: options =>
store.set("options", { ...store.get("options"), ...options }),
isTrayEnabled: () => store.get("options.tray"),
isAppVisible: () => store.get("options.appVisible"),
autoUpdate: () => store.get("options.autoUpdates"),
hideMenu: () => store.get("options.hideMenu"),
startAtLogin: () => store.get("options.startAtLogin"),
disableHardwareAcceleration: () =>
store.get("options.disableHardwareAcceleration"),
};

View File

@ -1,31 +0,0 @@
function getEnabledPlugins(store) {
return store.get("plugins");
}
function isEnabled(store, plugin) {
return store.get("plugins").indexOf(plugin) > -1;
}
function enablePlugin(store, plugin) {
let plugins = getEnabledPlugins(store);
if (plugins.indexOf(plugin) === -1) {
plugins.push(plugin);
store.set("plugins", plugins);
}
}
function disablePlugin(store, plugin) {
let plugins = getEnabledPlugins(store);
let index = plugins.indexOf(plugin);
if (index > -1) {
plugins.splice(index, 1);
store.set("plugins", plugins);
}
}
module.exports = {
isEnabled : isEnabled,
getEnabledPlugins: getEnabledPlugins,
enablePlugin : enablePlugin,
disablePlugin : disablePlugin
};

28
tray.js
View File

@ -2,19 +2,20 @@ const path = require("path");
const { Menu, nativeImage, Tray } = require("electron");
const config = require("./config");
const { mainMenuTemplate } = require("./menu");
const { isTrayEnabled } = require("./store");
const { clickInYoutubeMusic } = require("./utils/youtube-music");
const getSongControls = require("./providers/song-controls");
// Prevent tray being garbage collected
let tray;
module.exports.setUpTray = (app, win) => {
if (!isTrayEnabled()) {
if (!config.get("options.tray")) {
tray = undefined;
return;
}
const { playPause, next, previous } = getSongControls(win);
const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png");
let trayIcon = nativeImage.createFromPath(iconPath).resize({
width: 16,
@ -24,35 +25,30 @@ module.exports.setUpTray = (app, win) => {
tray.setToolTip("Youtube Music");
tray.setIgnoreDoubleClickEvents(true);
tray.on("click", () => {
win.isVisible() ? win.hide() : win.show();
if (config.get("options.trayClickPlayPause")) {
playPause();
} else {
win.isVisible() ? win.hide() : win.show();
}
});
const trayMenu = Menu.buildFromTemplate([
{
label: "Play/Pause",
click: () => {
clickInYoutubeMusic(
win,
"#left-controls > div > paper-icon-button.play-pause-button.style-scope.ytmusic-player-bar"
);
playPause();
},
},
{
label: "Next",
click: () => {
clickInYoutubeMusic(
win,
"#left-controls > div > paper-icon-button.next-button.style-scope.ytmusic-player-bar"
);
next();
},
},
{
label: "Previous",
click: () => {
clickInYoutubeMusic(
win,
"#left-controls > div > paper-icon-button.previous-button.style-scope.ytmusic-player-bar"
);
previous();
},
},
{

View File

@ -1,8 +0,0 @@
const clickInYoutubeMusic = (win, selector) => {
win.webContents.executeJavaScript(
`document.querySelector("${selector}").click();`,
true
);
};
module.exports = { clickInYoutubeMusic };

View File

Before

Width:  |  Height:  |  Size: 816 KiB

After

Width:  |  Height:  |  Size: 816 KiB

379
web/youtube-music.svg Normal file
View File

@ -0,0 +1,379 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 800 300" width="800" height="300">
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml">
<style>
.container {
width: 100%;
position: relative;
overflow: hidden;
}
a {
text-decoration: none;
}
h1.main, p.demos {
-webkit-animation-delay: 18s;
-moz-animation-delay: 18s;
-ms-animation-delay: 18s;
animation-delay: 18s;
}
.container {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
z-index: 0;
}
.content {
position: absolute;
width: 100%;
height: 100%;
left: 0px;
top: 0px;
z-index: 1000;
}
.container h2 {
position: absolute;
top: 50%;
line-height: 100px;
height: 90px;
margin-top: -90px;
font-size: 90px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
width: 100%;
text-align: center;
color: transparent;
-webkit-animation: blurFadeInOut 3s ease-in backwards;
-moz-animation: blurFadeInOut 3s ease-in backwards;
-ms-animation: blurFadeInOut 3s ease-in backwards;
animation: blurFadeInOut 3s ease-in backwards;
}
.container h2.frame-1 {
-webkit-animation-delay: 0s;
-moz-animation-delay: 0s;
-ms-animation-delay: 0s;
animation-delay: 0s;
}
.container h2.frame-2 {
-webkit-animation-delay: 3s;
-moz-animation-delay: 3s;
-ms-animation-delay: 3s;
animation-delay: 3s;
}
.container h2.frame-3 {
-webkit-animation-delay: 6s;
-moz-animation-delay: 6s;
-ms-animation-delay: 6s;
animation-delay: 6s;
}
.container h2.frame-4 {
-webkit-animation-delay: 9s;
-moz-animation-delay: 9s;
-ms-animation-delay: 9s;
animation-delay: 9s;
}
.container h2.frame-5 {
-webkit-animation: none;
-moz-animation: none;
-ms-animation: none;
animation: none;
color: transparent;
text-shadow: 0px 0px 1px #fff;
}
.container h2.frame-5 span {
-webkit-animation: blurFadeIn 3s ease-in 12s backwards;
-moz-animation: blurFadeIn 1s ease-in 12s backwards;
-ms-animation: blurFadeIn 3s ease-in 12s backwards;
animation: blurFadeIn 3s ease-in 12s backwards;
color: transparent;
text-shadow: 0px 0px 1px #fff;
}
.container h2.frame-5 span:nth-child(2) {
-webkit-animation-delay: 13s;
-moz-animation-delay: 13s;
-ms-animation-delay: 13s;
animation-delay: 13s;
}
.container h2.frame-5 span:nth-child(3) {
-webkit-animation-delay: 14s;
-moz-animation-delay: 14s;
-ms-animation-delay: 14s;
animation-delay: 14s;
}
.circle-link {
position: absolute;
left: 50%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
bottom: 50px;
margin-left: -100px;
text-align: center;
line-height: 200px;
width: 200px;
height: 200px;
background: #cc0000;
color: #fff;
font-size: 25px;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%;
box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
-webkit-animation: fadeInRotate 0.8s ease 16s backwards;
-moz-animation: fadeInRotate 0.8s ease 16s backwards;
-ms-animation: fadeInRotate 0.8s ease 16s backwards;
animation: fadeInRotate 0.8s ease 16s backwards;
-webkit-transform: scale(1) rotate(0deg);
-moz-transform: scale(1) rotate(0deg);
-o-transform: scale(1) rotate(0deg);
-ms-transform: scale(1) rotate(0deg);
transform: scale(1) rotate(0deg);
background-repeat: no-repeat;
background-position: -250px -250px, 0 0;
background-image: -webkit-linear-gradient(
top left,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
background-image: -moz-linear-gradient(
0 0,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
background-image: -o-linear-gradient(
0 0,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
background-image: linear-gradient(
0 0,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
-moz-background-size: 250% 250%, 100% 100%;
background-size: 250% 250%, 100% 100%;
-webkit-transition: background-position 0s ease;
-moz-transition: background-position 0s ease;
-o-transition: background-position 0s ease;
transition: background-position 0s ease;
}
.circle-link:hover {
background-position: 0 0, 0 0;
-webkit-transition-duration: 0.5s;
-moz-transition-duration: 0.5s;
transition-duration: 0.5s;
}
@-webkit-keyframes blurFadeInOut {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-webkit-transform: scale(1.3);
}
20%, 75% {
opacity: 1;
color: #cc0000;
text-shadow: 0px 0px 1px #fff;
-webkit-transform: scale(1);
}
100% {
opacity: 0;
text-shadow: 0px 0px 50px #fff;
-webkit-transform: scale(0);
}
}
@-webkit-keyframes blurFadeIn {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-webkit-transform: scale(1.3);
}
50% {
opacity: 0.5;
color: #cc0000;
text-shadow: 0px 0px 10px #fff;
-webkit-transform: scale(1.1);
}
100% {
opacity: 1;
text-shadow: 0px 0px 1px #fff;
-webkit-transform: scale(1);
}
}
@-webkit-keyframes fadeInBack {
0% {
opacity: 0;
-webkit-transform: scale(0);
}
50% {
opacity: 0.4;
-webkit-transform: scale(2);
}
100% {
opacity: 0.2;
-webkit-transform: scale(5);
}
}
@-webkit-keyframes fadeInRotate {
0% {
opacity: 0;
-webkit-transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
-webkit-transform: scale(1) rotate(0deg);
}
}
@-moz-keyframes blurFadeInOut {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-moz-transform: scale(1.3);
}
20%, 75% {
opacity: 1;
color: #cc0000;
text-shadow: 0px 0px 1px #fff;
-moz-transform: scale(1);
}
100% {
opacity: 0;
text-shadow: 0px 0px 50px #fff;
-moz-transform: scale(0);
}
}
@-moz-keyframes blurFadeIn {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-moz-transform: scale(1.3);
}
100% {
opacity: 1;
text-shadow: 0px 0px 1px #fff;
-moz-transform: scale(1);
}
}
@-moz-keyframes fadeInBack {
0% {
opacity: 0;
-moz-transform: scale(0);
}
50% {
opacity: 0.4;
-moz-transform: scale(2);
}
100% {
opacity: 0.2;
-moz-transform: scale(5);
}
}
@-moz-keyframes fadeInRotate {
0% {
opacity: 0;
-moz-transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
-moz-transform: scale(1) rotate(0deg);
}
}
@keyframes blurFadeInOut {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
transform: scale(1.3);
}
20%, 75% {
opacity: 1;
color: #cc0000;
text-shadow: 0px 0px 1px #fff;
transform: scale(1);
}
100% {
opacity: 0;
text-shadow: 0px 0px 50px #fff;
transform: scale(0);
}
}
@keyframes blurFadeIn {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
transform: scale(1.3);
}
50% {
opacity: 0.5;
color: #cc0000;
text-shadow: 0px 0px 10px #fff;
transform: scale(1.1);
}
100% {
opacity: 1;
text-shadow: 0px 0px 1px #fff;
transform: scale(1);
}
}
@keyframes fadeInBack {
0% {
opacity: 0;
transform: scale(0);
}
50% {
opacity: 0.4;
transform: scale(2);
}
100% {
opacity: 0.2;
transform: scale(5);
}
}
@keyframes fadeInRotate {
0% {
opacity: 0;
transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
transform: scale(1) rotate(0deg);
}
}
</style>
<div class="container">
<div class="content">
<h2 class="frame-1">YouTube Music Desktop App</h2>
<h2 class="frame-2">With built-in ad blocker</h2>
<h2 class="frame-3">And built-in downloader</h2>
<h2 class="frame-4">Cross-platform</h2>
<h2 class="frame-5">
<span>Free,</span>
<span>Open source</span>
</h2>
<a class="circle-link" href="https://github.com/th-ch/youtube-music/releases/latest">
Download
</a>
</div>
</div>
</div>
</foreignObject>
</svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

372
yarn.lock
View File

@ -372,36 +372,36 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@cliqz/adblocker-content@^1.18.6":
version "1.18.6"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-content/-/adblocker-content-1.18.6.tgz#a65dd518f3e6d1f2e9fee36ca5ae5615ba7b4cfd"
integrity sha512-OXrca20n+cMn9Ase+6oeX3fTmkauQMSb//lMLs56pHyra4foxN5o1rNiBG7qNIypdGQBFiTtGG7Vbp7YO5RQMw==
"@cliqz/adblocker-content@^1.19.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-content/-/adblocker-content-1.19.0.tgz#c3e8ce50a719905cfcb66b114882c086b5074131"
integrity sha512-sciMicb+zmN5+iKCiDnWPegepgI32XzX4Snf1VxR+HAFwYGJmfk25vpL6ONd6hOlpmumxHkE/y5l7suH0ziP5g==
"@cliqz/adblocker-electron-preload@^1.18.6":
version "1.18.6"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron-preload/-/adblocker-electron-preload-1.18.6.tgz#57ec2dac09bbacb03b143609345638e98132f985"
integrity sha512-cOK6ZuN3j0qLCZUj8oCf2PmPY837VTxtZM6bZl1x5xWLy/31x7186Wk0DP3C9MXU7gUhlqYxxKpbJDLZgFJ7Qw==
"@cliqz/adblocker-electron-preload@^1.19.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron-preload/-/adblocker-electron-preload-1.19.0.tgz#960332fb3bc6e68e13596bc280f68aa9ee5e3170"
integrity sha512-ns+VCyb+H8U5rKC4lfvAbb1KxZiIKFs3F8HvivRkGBFqjEzaCUC4s/kf6P+tWCLC69lVgrZUDIzFHkKmGG9LPA==
dependencies:
"@cliqz/adblocker-content" "^1.18.6"
"@cliqz/adblocker-content" "^1.19.0"
"@cliqz/adblocker-electron@^1.18.6":
version "1.18.6"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron/-/adblocker-electron-1.18.6.tgz#e387a1dc6f3f4a4005d299b37723899be4f0967b"
integrity sha512-RGy003FHsvcLoGYaQIJVNWX8ZUQmK+Dbo0LeQAcsP96vOaTHHFOVj0Auhwkg7mZASiR9/XnoNepKIifO2zQVfw==
"@cliqz/adblocker-electron@^1.19.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker-electron/-/adblocker-electron-1.19.0.tgz#9be5e10e93cd0488b79c54e44a265687b3d07a87"
integrity sha512-UFhhHAQUI02HXWEX0Yjw+sCzJrdLbcGjCdjbaPjkYSv/w+uGaq7TahE1IXGALkS8vXHenA2iJdJglt2mh96Uuw==
dependencies:
"@cliqz/adblocker" "^1.18.6"
"@cliqz/adblocker-electron-preload" "^1.18.6"
"@cliqz/adblocker" "^1.19.0"
"@cliqz/adblocker-electron-preload" "^1.19.0"
tldts-experimental "^5.6.21"
"@cliqz/adblocker@^1.18.6":
version "1.18.6"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker/-/adblocker-1.18.6.tgz#07d075c45017db7cd2aff19afe466ad53217d318"
integrity sha512-+ro8DoqBaMt9nmfjJF+0Om03/9hdDhRx6NJKzwmW7Pfvd/XhqJ+NiDtdusABSERhCE3nUXCWdu5j09X9HiX6Vg==
"@cliqz/adblocker@^1.19.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@cliqz/adblocker/-/adblocker-1.19.0.tgz#1521673172998d0a691aeb1f2b2c1732c9b8c6ab"
integrity sha512-sy/aBjPoQsqLI5XYHAaZfgi+7HpddJmoHn/UMyX0dx0/jnnaM2/Z2LGylOsGwjW97GsNvvqSwXgxsEH2R0K9SQ==
dependencies:
"@remusao/guess-url-type" "^1.1.2"
"@remusao/small" "^1.1.2"
"@remusao/smaz" "^1.7.1"
"@types/chrome" "^0.0.126"
"@types/chrome" "^0.0.127"
"@types/firefox-webext-browser" "^82.0.0"
tldts-experimental "^5.6.21"
@ -469,15 +469,15 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@ffmpeg/core@^0.8.4":
version "0.8.4"
resolved "https://registry.yarnpkg.com/@ffmpeg/core/-/core-0.8.4.tgz#69062a9b257792a9a8445e1f01e68c3e5e7fe58b"
integrity sha512-gEr4qXZpShZpIVUO3hc5Vz7bkk/jLYuzVVQtHluUwrui5eAooQwExOGiEovzLVkRwjJ707/qqfmTrK3r80UkWw==
"@ffmpeg/core@^0.8.5":
version "0.8.5"
resolved "https://registry.yarnpkg.com/@ffmpeg/core/-/core-0.8.5.tgz#2d0b7d4409a4348fa6a1888c247de706ffc0e63f"
integrity sha512-hemVFmhVLbD/VZaCG2BvCzFglKytMIMJ5aJfc12eXN4O4cG0wXnGTMVzlK1KKW/6viHhJMPkc9h4UDnJW8Uivg==
"@ffmpeg/ffmpeg@^0.9.5":
version "0.9.5"
resolved "https://registry.yarnpkg.com/@ffmpeg/ffmpeg/-/ffmpeg-0.9.5.tgz#6624747dc331632bc7c581e8d4f2046abc933798"
integrity sha512-Vtxgi5C89n36pJ3I1/l6xd2qSwn+s1tAtLvFJ98N9P2ZorBvxXCEwTkt2yL7GuOUX9wpdG/vLFqp7iLso8LDwg==
"@ffmpeg/ffmpeg@^0.9.6":
version "0.9.6"
resolved "https://registry.yarnpkg.com/@ffmpeg/ffmpeg/-/ffmpeg-0.9.6.tgz#b44fa1ab34dd860785bb7c317dbfbe8b9ded7036"
integrity sha512-ov5FAV3dHRJ/+ZxQH9V5GvY/iczwq5vrKWeu4tpytxZewTSAhZ1aKD/sFBzd79nQNF+CTktxUp3LWuGECXBNeA==
dependencies:
is-url "^1.2.4"
node-fetch "^2.6.1"
@ -1100,10 +1100,10 @@
"@types/node" "*"
"@types/responselike" "*"
"@types/chrome@^0.0.126":
version "0.0.126"
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.126.tgz#f9f3436712f0c7c12ea9798abc9b95575ad7b23a"
integrity sha512-191z7uoyfbGU+z7/m45j9XbWugWqVHVPMM4hJV5cZ+3YzGCT9wFjMUHO3Wr3Xvo8aVodvRNu28u7lvEaAnfbzg==
"@types/chrome@^0.0.127":
version "0.0.127"
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.127.tgz#9e29f351d558f60e95326ceadc586fdcc824151b"
integrity sha512-hBB9EApLYKKn2GvklVkTxVP6vZvxsH9okyIRUinNtMzZHIgIKWQk/ESbX+O5g4Bihfy38+aFGn7Kl7Cxou5JUg==
dependencies:
"@types/filesystem" "*"
"@types/har-format" "*"
@ -1244,10 +1244,17 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.1.tgz#be148756d5480a84cde100324c03a86ae5739fb5"
integrity sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ==
"@types/puppeteer@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-3.0.2.tgz#20085220593b560c7332b6d46aecaf81ae263540"
integrity sha512-JRuHPSbHZBadOxxFwpyZPeRlpPTTeMbQneMdpFd8LXdyNfFSiX950CGewdm69g/ipzEAXAmMyFF1WOWJOL/nKw==
"@types/puppeteer-core@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@types/puppeteer-core/-/puppeteer-core-5.4.0.tgz#880a7917b4ede95cbfe2d5e81a558cfcb072c0fb"
integrity sha512-yqRPuv4EFcSkTyin6Yy17pN6Qz2vwVwTCJIDYMXbE3j8vTPhv0nCQlZOl5xfi0WHUkqvQsjAR8hAfjeMCoetwg==
dependencies:
"@types/puppeteer" "*"
"@types/puppeteer@*":
version "5.4.2"
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.2.tgz#80f3a1f54dedbbf750779716de81401549062072"
integrity sha512-yjbHoKjZFOGqA6bIEI2dfBE5UPqU0YGWzP+ipDVP1iGzmlhksVKTBVZfT3Aj3wnvmcJ2PQ9zcncwOwyavmafBw==
dependencies:
"@types/node" "*"
@ -1258,12 +1265,10 @@
dependencies:
"@types/node" "*"
"@types/semver@^7.3.1":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.1.tgz#7a9a5d595b6d873f338c867dcef64df289468cfa"
integrity sha512-ooD/FJ8EuwlDKOI6D9HWxgIgJjMg2cuziXm/42npDC8y4NjxplBUn9loewZiBNCt44450lHAU0OSb51/UqXeag==
dependencies:
"@types/node" "*"
"@types/semver@^7.3.4":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb"
integrity sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==
"@types/stack-utils@^1.0.1":
version "1.0.1"
@ -1356,43 +1361,43 @@
dependencies:
eslint-visitor-keys "^1.1.0"
"@wdio/config@6.4.7":
version "6.4.7"
resolved "https://registry.yarnpkg.com/@wdio/config/-/config-6.4.7.tgz#f2402a6067b15fdc61e9701458f60adc388ae75c"
integrity sha512-wtcj9yKm5+SivwhsgpusBrFR7a3rpDsN/WH6ekoqlZFs7oCpJeTLwawWnoX6MJQy2no5o00lGxDDJnqjaBdiiQ==
"@wdio/config@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@wdio/config/-/config-6.11.0.tgz#ba6b0ef72d9f40bd577da16602de84390c3dcdb4"
integrity sha512-aNkH5sPEybOf7ND1JrAlCsKZow6KMAaY3Wyf9yHralQ3xmclmswFKU/DseP7go17Ivc2KHLl7MMkNaqVY90siw==
dependencies:
"@wdio/logger" "6.4.7"
"@wdio/logger" "6.10.10"
deepmerge "^4.0.0"
glob "^7.1.2"
"@wdio/logger@6.4.7":
version "6.4.7"
resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-6.4.7.tgz#6d83033a0d7ef20f3b2cc2420956105903a6a4e7"
integrity sha512-Mm/rsRa/1u/l8/IrNKM2c9tkvLE90i83d3KZ0Ujh4cicYJv+lNi9whsCi+p3QNFCo64nJ6bfC+0Ho5VgD3MiKw==
"@wdio/logger@6.10.10":
version "6.10.10"
resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-6.10.10.tgz#1e07cf32a69606ddb94fa9fd4b0171cb839a5980"
integrity sha512-2nh0hJz9HeZE0VIEMI+oPgjr/Q37ohrR9iqsl7f7GW5ik+PnKYCT9Eab5mR1GNMG60askwbskgGC1S9ygtvrSw==
dependencies:
chalk "^4.0.0"
loglevel "^1.6.0"
loglevel-plugin-prefix "^0.8.4"
strip-ansi "^6.0.0"
"@wdio/protocols@6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-6.3.6.tgz#fc408b4441d9701bdd370b0981cf243862ce7e19"
integrity sha512-cocBRkv5sYUBxXResuxskQhIkKgDgE/yAtgMGR5wXLrtG/sMpZ2HVy6LOcOeARidAaRwbav80M2ZHjTCjPn53w==
"@wdio/protocols@6.10.6":
version "6.10.6"
resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-6.10.6.tgz#8d1deed6651a5ca0a185ea334fc1a371dc4c700c"
integrity sha512-CLLVdc82S+Zij7f9djL90JC1bE5gtaOn+EF2pY4n8XdypqPUa1orQip8stQtX/wXEX0Ak45MEcSU9nCY+CzNnQ==
"@wdio/repl@6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-6.5.0.tgz#1e3ef2dfbc1ead1280b21bb053ad509816722466"
integrity sha512-qKm2j0qY7mrZQipHv4PhKpAL7pkyxCzW1XDoEjp09OHLvmGvvCwY6aEBuLziD9BaiR30BXVNLIKPZfM4Xl2Zfg==
"@wdio/repl@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-6.11.0.tgz#5b1eab574b6b89f7f7c383e7295c06af23c3818e"
integrity sha512-FxrFKiTkFyELNGGVEH1uijyvNY7lUpmff6x+FGskFGZB4uSRs0rxkOMaEjxnxw7QP1zgQKr2xC7GyO03gIGRGg==
dependencies:
"@wdio/utils" "6.5.0"
"@wdio/utils" "6.11.0"
"@wdio/utils@6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-6.5.0.tgz#46398b08e6741eb97a57089b2c909622c480ea22"
integrity sha512-k5RxRj/re/BbK76SjWSmyhJFHWnXD74vl/doCAQNuOaKFBd2dqMCs3GiFjYCyLcU37XGMAnRvI3tKHflyLGJYw==
"@wdio/utils@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-6.11.0.tgz#878c2500efb1a325bf5a66d2ff3d08162f976e8c"
integrity sha512-vf0sOQzd28WbI26d6/ORrQ4XKWTzSlWLm9W/K/eJO0NASKPEzR+E+Q2kaa+MJ4FKXUpjbt+Lxfo+C26TzBk7tg==
dependencies:
"@wdio/logger" "6.4.7"
"@wdio/logger" "6.10.10"
"YoutubeNonStop@git://github.com/lawfx/YoutubeNonStop.git#v0.8.0":
version "0.0.0"
@ -2091,6 +2096,14 @@ builder-util-runtime@8.7.2:
debug "^4.1.1"
sax "^1.2.4"
builder-util-runtime@8.7.3:
version "8.7.3"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.3.tgz#0aaafa52d25295c939496f62231ca9ff06c30e40"
integrity sha512-1Q2ReBqFblimF5g/TLg2+0M5Xzv0Ih5LxJ/BMWXvEy/e6pQKeeEpbkPMGsN6OiQgkygaZo5VXCXIjOkOQG5EoQ==
dependencies:
debug "^4.3.2"
sax "^1.2.4"
builder-util@22.8.1:
version "22.8.1"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.8.1.tgz#efdfb327dbc22c59aa1e2f55adbe0e771086e839"
@ -2624,6 +2637,11 @@ crypto-random-string@^2.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
css-shorthand-properties@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935"
integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==
css-value@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea"
@ -2690,6 +2708,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "^2.1.1"
debug@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
decamelize-keys@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
@ -2850,16 +2875,17 @@ devtools-protocol@0.0.799653:
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.799653.tgz#86fc95ce5bf4fdf4b77a58047ba9d2301078f119"
integrity sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==
devtools@6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/devtools/-/devtools-6.5.0.tgz#48f9b72cb8f347c8658b46db940da75288f721c6"
integrity sha512-P/9+jSK+Jq4gWO5a79OLtDsZPcrNZN9JDCqWdCmKcbCCikV3fYic+0wmRzAPff8iYLCdmNXf/no4XMLwXR5LXQ==
devtools@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/devtools/-/devtools-6.11.0.tgz#d20f1d1bb42357d3f2e424d7103fdd3e0d339879"
integrity sha512-fqiRz77IIGkHfHdIkVYIpBNKz7qllJJvHP92NKDo5e41J+sSVKsjzHsPqvYLugRhSGsZKPrJ19Sj8kmPyFi18w==
dependencies:
"@wdio/config" "6.4.7"
"@wdio/logger" "6.4.7"
"@wdio/protocols" "6.3.6"
"@wdio/utils" "6.5.0"
"@wdio/config" "6.11.0"
"@wdio/logger" "6.10.10"
"@wdio/protocols" "6.10.6"
"@wdio/utils" "6.11.0"
chrome-launcher "^0.13.1"
edge-paths "^2.1.0"
puppeteer-core "^5.1.0"
ua-parser-js "^0.7.21"
uuid "^8.0.0"
@ -2885,6 +2911,14 @@ dir-glob@^2.2.2:
dependencies:
path-type "^3.0.0"
discord-rpc@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/discord-rpc/-/discord-rpc-3.1.4.tgz#6d449a682e6a0dec4f0444d5f36f9ebfabaccf91"
integrity sha512-QaBu+gHica2SzgRAmTpuJ4J8DX9+fDwAqhvaie3hcbkU9WPqewEPh21pWdd/7vTI/JNuapU7PFm2ZKg3BTkbGg==
dependencies:
node-fetch "^2.6.1"
ws "^7.3.1"
dmg-builder@22.8.1:
version "22.8.1"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.8.1.tgz#9b3bcbbc43e5fed232525d61a5567ea4b66085c3"
@ -2966,6 +3000,11 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
edge-paths@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/edge-paths/-/edge-paths-2.1.0.tgz#f273f3a0fe022422048bb78f83eb61aca29977ef"
integrity sha512-ZpIN1Vm5hlo9dkkST/1s8QqPNne2uwk3Plf6HcVUhnpfal0WnDRLdNj/wdQo3xRc+wnN3C25wPpPlV2E6aOunQ==
ejs@^3.1.3:
version "3.1.5"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.5.tgz#aed723844dc20acb4b170cd9ab1017e476a0d93b"
@ -2993,18 +3032,18 @@ electron-builder@^22.8.1:
update-notifier "^4.1.0"
yargs "^15.4.1"
electron-chromedriver@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/electron-chromedriver/-/electron-chromedriver-10.0.0.tgz#cea6ee9f2d67f794b7439074fd70060109a7ff56"
integrity sha512-6jvMnQNHsIFTnvSn8kYQk8dRXFjqtp7E4QVIP4Cc6xR7SM8QI0/EmAqfuysd8CGJOpa8wFkEYxCT2dbHGp3bDw==
electron-chromedriver@^11.0.0:
version "11.0.0"
resolved "https://registry.yarnpkg.com/electron-chromedriver/-/electron-chromedriver-11.0.0.tgz#49b034ed0ad12c12e3522862c7bb46875a0d85e1"
integrity sha512-ayMJPBbB4puU0SqYbcD9XvF3/7GWIhqKE1n5lG2/GQPRnrZkNoPIilsrS0rQcD50Xhl69KowatDqLhUznZWtbA==
dependencies:
"@electron/get" "^1.12.2"
extract-zip "^2.0.0"
electron-debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-3.1.0.tgz#0df17297487fa3c82344d810812853bf67f0bd69"
integrity sha512-SWEqLj4MgfV3tGuO5eBLQ5/Nr6M+KPxsnE0bUJZvQebGJus6RAcdmvd7L+l0Ji31h2mmrN23l2tHFtCa2FvurA==
electron-debug@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-3.2.0.tgz#46a15b555c3b11872218c65ea01d058aa0814920"
integrity sha512-7xZh+LfUvJ52M9rn6N+tPuDw6oRAjxUj9SoxAZfJ0hVCXhZCsdkrSt7TgXOiWiEOBgEV8qwUIO/ScxllsPS7ow==
dependencies:
electron-is-dev "^1.1.0"
electron-localshortcut "^3.1.0"
@ -3092,23 +3131,23 @@ electron-store@^6.0.1:
conf "^7.1.2"
type-fest "^0.16.0"
electron-updater@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.3.5.tgz#4fb36f593a031c87ea07ee141c9f064d5deffb15"
integrity sha512-5jjN7ebvfj1cLI0VZMdCnJk6aC4bP+dy7ryBf21vArR0JzpRVk0OZHA2QBD+H5rm6ZSeDYHOY6+8PrMEqJ4wlQ==
electron-updater@^4.3.6:
version "4.3.7"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.3.7.tgz#632434cf1dd856e37f5557a68b55867fae29c39e"
integrity sha512-F7l1ZdslA5Do9kABNhiYKMPzreLulFTv+rsbGUQJ3TnRKrEb3JAi/n/jco3mI8LOEG/pgS5f9ytQ+D+5r/PvQw==
dependencies:
"@types/semver" "^7.3.1"
builder-util-runtime "8.7.2"
"@types/semver" "^7.3.4"
builder-util-runtime "8.7.3"
fs-extra "^9.0.1"
js-yaml "^3.14.0"
js-yaml "^3.14.1"
lazy-val "^1.0.4"
lodash.isequal "^4.5.0"
semver "^7.3.2"
semver "^7.3.4"
electron@^10.1.3:
version "10.1.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-10.1.3.tgz#7e276e373bf30078bd4cb1184850a91268dc0e6c"
integrity sha512-CR8LrlG47MdAp317SQ3vGYa2o2cIMdMSMPYH46OVitFLk35dwE9fn3VqvhUIXhCHYcNWIAPzMhkVHpkoFdKWuw==
electron@^11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/electron/-/electron-11.1.1.tgz#188f036f8282798398dca9513e9bb3b10213e3aa"
integrity sha512-tlbex3xosJgfileN6BAQRotevPRXB/wQIq48QeQ08tUJJrXwE72c8smsM/hbHx5eDgnbfJ2G3a60PmRjHU2NhA==
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^12.0.12"
@ -4384,11 +4423,6 @@ html-encoding-sniffer@^2.0.1:
dependencies:
whatwg-encoding "^1.0.5"
html-entities@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44"
integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==
html-escaper@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
@ -4544,9 +4578,9 @@ inherits@2.0.3:
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
version "1.3.7"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
interpret@^1.2.0:
version "1.2.0"
@ -5412,6 +5446,14 @@ js-yaml@^3.14.0:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^3.14.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@ -6237,9 +6279,9 @@ node-modules-regexp@^1.0.0:
integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
node-notifier@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620"
integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==
version "8.0.1"
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.1.tgz#f86e89bbc925f2b068784b31f382afdc6ca56be1"
integrity sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==
dependencies:
growly "^1.3.0"
is-wsl "^2.2.0"
@ -7271,7 +7313,7 @@ request-promise-native@^1.0.8:
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
request@^2.81.0, request@^2.87.0, request@^2.88.2:
request@^2.81.0, request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@ -7372,10 +7414,10 @@ responselike@^2.0.0:
dependencies:
lowercase-keys "^2.0.0"
resq@^1.6.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/resq/-/resq-1.8.0.tgz#06c738e122bd024395cd729fb1e07248a1d2c340"
integrity sha512-VObcnfPcE6/EKfHqsi5qoJ0+BF9qfl5181CytP1su3HgzilqF03DrQ+Y7kZQrd+5myfmantl9W3/5uUcpwvKeg==
resq@^1.9.1:
version "1.10.0"
resolved "https://registry.yarnpkg.com/resq/-/resq-1.10.0.tgz#40b5e3515ff984668e6b6b7c2401f282b08042ea"
integrity sha512-hCUd0xMalqtPDz4jXIqs0M5Wnv/LZXN8h7unFOo4/nvExT9dDPbhwd3udRxLlp0HgBnHcV009UlduE9NZi7A6w==
dependencies:
fast-deep-equal "^2.0.1"
@ -7384,10 +7426,10 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
rgb2hex@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.0.tgz#801b4887127181d1e691f610df2cecdb77330265"
integrity sha512-cHdNTwmTMPu/TpP1bJfdApd6MbD+Kzi4GNnM6h35mdFChhQPSi9cAI8J7DMn5kQDKX8NuBaQXAyo360Oa7tOEA==
rgb2hex@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.3.tgz#8aa464c517b8a26c7a79d767dabaec2b49ee78ec"
integrity sha512-clEe0m1xv+Tva1B/TOepuIcvLAxP0U+sCDfgt1SX1HmI2Ahr5/Cd/nzJM1e78NKVtWdoo0s33YehpFA8UfIShQ==
rimraf@2.6.3, rimraf@^2.2.8:
version "2.6.3"
@ -7503,12 +7545,7 @@ semver-diff@^3.1.1:
dependencies:
semver "^6.3.0"
"semver@2 || 3 || 4 || 5", semver@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
semver@^5.4.1, semver@^5.7.1:
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@ -7518,15 +7555,12 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.1.2, semver@^7.2.1:
version "7.2.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.2.2.tgz#d01432d74ed3010a20ffaf909d63a691520521cd"
integrity sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==
semver@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
semver@^7.1.2, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
dependencies:
lru-cache "^6.0.0"
serialize-error@^5.0.0:
version "5.0.0"
@ -7742,16 +7776,16 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
spectron@^12.0.0:
version "12.0.0"
resolved "https://registry.yarnpkg.com/spectron/-/spectron-12.0.0.tgz#8a3454a1366cdb82fbb6be75be0104915b607340"
integrity sha512-ZyDFS7I+4dWa/YXSQ/trbC4s1Rd0Ks5oi4MQ6XSJHULPasJhx5q2bM93Ae7BNUvwrGrbhjk7O6f14JwqJimLyA==
spectron@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/spectron/-/spectron-13.0.0.tgz#16bdfcf9a2b26cb5ee6c3e29b4f08101e339aa4d"
integrity sha512-7RPa6Fp8gqL4V0DubobnqIRFHIijkpjg6MFHcJlxoerWyvLJd+cQvOh756XpB1Z/U3DyA9jPcS+HE2PvYRP5+A==
dependencies:
dev-null "^0.1.1"
electron-chromedriver "^10.0.0"
request "^2.87.0"
split "^1.0.0"
webdriverio "^6.1.20"
electron-chromedriver "^11.0.0"
request "^2.88.2"
split "^1.0.1"
webdriverio "^6.9.1"
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
@ -7760,7 +7794,7 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
split@^1.0.0:
split@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==
@ -8522,9 +8556,9 @@ uuid@^3.3.2:
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.0.0, uuid@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea"
integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache@^2.0.3:
version "2.1.0"
@ -8583,32 +8617,34 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
webdriver@6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-6.5.0.tgz#c2ab978570843c6fe8a778b274d9acf781cfcfd4"
integrity sha512-6iOll9TshD4+2J+em+bLshvM1uXtnotdZ+JaALqRLbkVswLRFU0pTVP1oug0e/IYwL7Me4Cafh9ugQ4PwPuOnA==
webdriver@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-6.11.0.tgz#2997bf4ff6c1dcb4e58b4ae9e2b83af8b938f643"
integrity sha512-31uD1Vi+9QAzDSpN3+0oFFRnzJP8IVp8wRjLVbIOaQGPXV1sKjAP7v6LJcxl1JjcmW8keAIh2eyAgbjZJbcyZw==
dependencies:
"@wdio/config" "6.4.7"
"@wdio/logger" "6.4.7"
"@wdio/protocols" "6.3.6"
"@wdio/utils" "6.5.0"
"@wdio/config" "6.11.0"
"@wdio/logger" "6.10.10"
"@wdio/protocols" "6.10.6"
"@wdio/utils" "6.11.0"
got "^11.0.2"
lodash.merge "^4.6.1"
webdriverio@^6.1.20:
version "6.5.2"
resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-6.5.2.tgz#0c733c293b88a0ee45082bfde9fc3d9a10308216"
integrity sha512-ChAV6RmF10mlyWnAL2y+PdnzAjpxL/UuyAHJsYSuirEeEAAqFWWePxniz67bUEVQPVClVj8Jh7oeoK6rhu4RAA==
webdriverio@^6.9.1:
version "6.11.3"
resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-6.11.3.tgz#043bc98514d7410264fbf14991bc0546fa119779"
integrity sha512-yHS01H0+oz59Y+JLj/u8piLzOhtTiQSeASwb3hHF1EDuIiiK6JgGYFAqCYr26BrlzqzUOQ/9VpJj11LtcKWF/A==
dependencies:
"@types/puppeteer" "^3.0.1"
"@wdio/config" "6.4.7"
"@wdio/logger" "6.4.7"
"@wdio/repl" "6.5.0"
"@wdio/utils" "6.5.0"
"@types/puppeteer-core" "^5.4.0"
"@wdio/config" "6.11.0"
"@wdio/logger" "6.10.10"
"@wdio/repl" "6.11.0"
"@wdio/utils" "6.11.0"
archiver "^5.0.0"
atob "^2.1.2"
css-shorthand-properties "^1.1.1"
css-value "^0.0.1"
devtools "6.5.0"
devtools "6.11.0"
fs-extra "^9.0.1"
get-port "^5.1.1"
grapheme-splitter "^1.0.2"
lodash.clonedeep "^4.5.0"
@ -8617,10 +8653,10 @@ webdriverio@^6.1.20:
lodash.zip "^4.2.0"
minimatch "^3.0.4"
puppeteer-core "^5.1.0"
resq "^1.6.0"
rgb2hex "^0.2.0"
resq "^1.9.1"
rgb2hex "0.2.3"
serialize-error "^7.0.0"
webdriver "6.5.0"
webdriver "6.11.0"
webidl-conversions@^5.0.0:
version "5.0.0"
@ -8745,6 +8781,11 @@ ws@^7.2.3:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
ws@^7.3.1:
version "7.4.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd"
integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==
xdg-basedir@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
@ -8936,12 +8977,11 @@ yauzl@^2.10.0:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
ytdl-core@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ytdl-core/-/ytdl-core-4.1.1.tgz#191fabf472c44f969fe3eca15cb4d1c094e46282"
integrity sha512-T2VIS64sHKdLLqvuTV7S4WyoUCZLdR7HOP/9jX1CyXKYUjKLFP9UpVIFH0ZUvFSmK48eNFErWLOO5dGouwqztQ==
ytdl-core@^4.1.2:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ytdl-core/-/ytdl-core-4.2.1.tgz#afcc1577a2a35701a5e1369f2ad3b0d1a7d2419d"
integrity sha512-7zAoJiWpaBGgiAUCQuvKBuWom7tmSCV0A70gRdrPxR96yQoJOrCZkW6Wg1CofvPtAeQVWTVWThC8bXRsE+SBeA==
dependencies:
html-entities "^1.3.1"
m3u8stream "^0.8.3"
miniget "^4.0.0"
sax "^1.1.3"