Compare commits

..

19 Commits

Author SHA1 Message Date
6901713036 Bump version to 2.0.4 2023-10-12 00:46:04 +09:00
1d5b2997bd fix(downloader): private playlist download 2023-10-12 00:41:58 +09:00
572a023aaa fix: fixed an issue with the initial launch in certain regions, such as South Korea 2023-10-11 23:09:05 +09:00
9187f1e240 Revert "fix: set default adblocker as InPlayer"
This reverts commit 85228fd7d2.
2023-10-11 22:47:56 +09:00
df13d7d0f3 Merge pull request #1304 from th-ch/fix/deps 2023-10-11 22:37:16 +09:00
85228fd7d2 fix: set default adblocker as InPlayer
Fixed an issue with the initial launch in certain regions, such as South Korea.
2023-10-11 22:12:54 +09:00
17ba071057 fix: crash before window loaded 2023-10-11 21:59:03 +09:00
d7df4d7d10 fix: fix It Just Works
Fixed an issue that caused inconsistent execution results.
2023-10-11 19:28:01 +09:00
7aa970cebc fix: bump dependencies 2023-10-11 18:24:11 +09:00
f08f003cf4 Merge pull request #1301 from th-ch/fix/1300
hotfix(adblocker): fix `ipcRenderer.sendSync() with ...`
2023-10-11 08:53:22 +09:00
9f99eded9e chore(readme): update build instruction 2023-10-11 08:48:36 +09:00
c512f13009 hotfix(adblocker): fix ipcRenderer.sendSync() with ...
This issue is caused by the renderer's adblocker being loaded before the main process's adblocker.
2023-10-11 02:01:44 +09:00
b475f780ff Merge pull request #1296 from Lucasamiel0406/master 2023-10-11 00:23:11 +09:00
2294102006 Merge pull request #1297 from nnnlog/master
fix(downloader): Korean filename is broken on non-macOS devices
2023-10-10 16:48:43 +09:00
d69a07d025 fix(downloader): normalize filename depending on OS 2023-10-10 16:05:09 +09:00
4f4995c20c fix: typo in readme.md 2023-10-10 15:54:55 +09:00
b6894dca29 chore(deps): bump deps 2023-10-10 14:10:33 +09:00
73f14e581d Fix Library removed for Premium users
As by now, the code removes the last child of the YT's buttons sidebar. It's good for non-premium users but affects premium users, as it removes the "Library" button.

This small fix targets the 4th child (usually the Upgrade button location) instead of last child.

A bad move/practice, but does its job and remove the Upgrade button while not removing the Library one.
2023-10-09 20:56:08 -03:00
2f2e64af4a Update changelog for v2.0.3 2023-10-09 16:06:41 +00:00
18 changed files with 446 additions and 263 deletions

View File

@ -2,8 +2,23 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v2.0.3](https://github.com/th-ch/youtube-music/compare/v2.0.2...v2.0.3)
- feat(discord): add `Hide GitHub link Button` [`#1293`](https://github.com/th-ch/youtube-music/pull/1293)
- feat(deps): bundle `youtubei.js` (temporary solution) [`#1292`](https://github.com/th-ch/youtube-music/pull/1292)
- fix(mpris): fixed an issue where MPRIS information was incorrect [`#1291`](https://github.com/th-ch/youtube-music/pull/1291)
- fix(discord): fixed an issue where `timeChanged` was not being applied to Discord activities [`#1290`](https://github.com/th-ch/youtube-music/pull/1290)
- Fix: typo in README [`#1286`](https://github.com/th-ch/youtube-music/pull/1286)
- fix: chore(deps): update dependency @jellybrick/mpris-service to 2.1.4 (fix #971) [`#971`](https://github.com/th-ch/youtube-music/issues/971)
- chore(deps): Bump `@cliqz/adblocker-electron` to 1.26.8 (fix #1269) [`#1269`](https://github.com/th-ch/youtube-music/issues/1269)
- fix: missing icons taskbar-mediacontrol [`fbf4b3b`](https://github.com/th-ch/youtube-music/commit/fbf4b3b8b5e39c61975e67efc990c45f62de76d8)
- remove: migration scripts [`52ba2dc`](https://github.com/th-ch/youtube-music/commit/52ba2dc9ffd8e235251d1279686f55e33b3fa3bb)
- feat: add migration script [`926b9fb`](https://github.com/th-ch/youtube-music/commit/926b9fb5e6db69b69935ec5d7be9a76a84e54ceb)
#### [v2.0.2](https://github.com/th-ch/youtube-music/compare/v2.0.1...v2.0.2) #### [v2.0.2](https://github.com/th-ch/youtube-music/compare/v2.0.1...v2.0.2)
> 8 October 2023
- fix: discord-rpc [`#1278`](https://github.com/th-ch/youtube-music/pull/1278) - fix: discord-rpc [`#1278`](https://github.com/th-ch/youtube-music/pull/1278)
- Bump version to 2.0.2 [`b5dbfaf`](https://github.com/th-ch/youtube-music/commit/b5dbfaf68691a546d72f5c1818fd3a44802eb0fa) - Bump version to 2.0.2 [`b5dbfaf`](https://github.com/th-ch/youtube-music/commit/b5dbfaf68691a546d72f5c1818fd3a44802eb0fa)
- Merge pull request #1272 from th-ch/feat/resolves-1265 [`6b7fd5b`](https://github.com/th-ch/youtube-music/commit/6b7fd5ba630888de08004105179c059c6d93e028) - Merge pull request #1272 from th-ch/feat/resolves-1265 [`6b7fd5b`](https://github.com/th-ch/youtube-music/commit/6b7fd5ba630888de08004105179c059c6d93e028)

View File

@ -1,16 +1,14 @@
import path from 'node:path'; import path from 'node:path';
import { BrowserWindow, app, screen, globalShortcut, session, shell, dialog, ipcMain } from 'electron'; import { BrowserWindow, app, screen, globalShortcut, session, shell, dialog, ipcMain } from 'electron';
import enhanceWebRequest from 'electron-better-web-request'; import enhanceWebRequest, { BetterSession } from '@jellybrick/electron-better-web-request';
import is from 'electron-is'; import is from 'electron-is';
import unhandled from 'electron-unhandled'; import unhandled from 'electron-unhandled';
import { autoUpdater } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
import electronDebug from 'electron-debug'; import electronDebug from 'electron-debug';
import { BetterWebRequest } from 'electron-better-web-request/lib/electron-better-web-request';
import config from './config'; import config from './config';
import { setApplicationMenu } from './menu'; import { refreshMenu, setApplicationMenu } from './menu';
import { fileExists, injectCSS, injectCSSAsFile } from './plugins/utils'; import { fileExists, injectCSS, injectCSSAsFile } from './plugins/utils';
import { isTesting } from './utils/testing'; import { isTesting } from './utils/testing';
import { setUpTray } from './tray'; import { setUpTray } from './tray';
@ -144,7 +142,7 @@ if (is.windows()) {
ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins)); ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins));
function loadPlugins(win: BrowserWindow) { async function loadPlugins(win: BrowserWindow) {
injectCSS(win.webContents, youtubeMusicCSS); injectCSS(win.webContents, youtubeMusicCSS);
// Load user CSS // Load user CSS
const themes: string[] = config.get('options.themes'); const themes: string[] = config.get('options.themes');
@ -175,7 +173,7 @@ function loadPlugins(win: BrowserWindow) {
console.log('Loaded plugin - ' + plugin); console.log('Loaded plugin - ' + plugin);
const handler = mainPlugins[plugin as keyof typeof mainPlugins]; const handler = mainPlugins[plugin as keyof typeof mainPlugins];
if (handler) { if (handler) {
handler(win, options as never); await handler(win, options as never);
} }
} }
} catch (e) { } catch (e) {
@ -184,7 +182,7 @@ function loadPlugins(win: BrowserWindow) {
} }
} }
function createMainWindow() { async function createMainWindow() {
const windowSize = config.get('window-size'); const windowSize = config.get('window-size');
const windowMaximized = config.get('window-maximized'); const windowMaximized = config.get('window-maximized');
const windowPosition: Electron.Point = config.get('window-position'); const windowPosition: Electron.Point = config.get('window-position');
@ -223,7 +221,7 @@ function createMainWindow() {
: 'default'), : 'default'),
autoHideMenuBar: config.get('options.hideMenu'), autoHideMenuBar: config.get('options.hideMenu'),
}); });
loadPlugins(win); await loadPlugins(win);
if (windowPosition) { if (windowPosition) {
const { x: windowX, y: windowY } = windowPosition; const { x: windowX, y: windowY } = windowPosition;
@ -258,7 +256,6 @@ function createMainWindow() {
const urlToLoad = config.get('options.resumeOnStart') const urlToLoad = config.get('options.resumeOnStart')
? config.get('url') ? config.get('url')
: config.defaultConfig.url; : config.defaultConfig.url;
win.webContents.loadURL(urlToLoad);
win.on('closed', onClosed); win.on('closed', onClosed);
type PiPOptions = typeof config.defaultConfig.plugins['picture-in-picture']; type PiPOptions = typeof config.defaultConfig.plugins['picture-in-picture'];
@ -338,6 +335,8 @@ function createMainWindow() {
removeContentSecurityPolicy(); removeContentSecurityPolicy();
win.webContents.loadURL(urlToLoad);
return win; return win;
} }
@ -394,7 +393,7 @@ app.once('browser-window-created', (event, win) => {
console.log(log); console.log(log);
} }
if (!(config.plugins.isEnabled('in-app-menu') && errorCode === -3)) { // -3 is a false positive with in-app-menu if (errorCode !== -3) { // -3 is a false positive
win.webContents.send('log', log); win.webContents.send('log', log);
win.webContents.loadFile(path.join(__dirname, 'error.html')); win.webContents.loadFile(path.join(__dirname, 'error.html'));
} }
@ -414,17 +413,17 @@ app.on('window-all-closed', () => {
globalShortcut.unregisterAll(); globalShortcut.unregisterAll();
}); });
app.on('activate', () => { app.on('activate', async () => {
// On OS X it's common to re-create a window in the app when the // On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
mainWindow = createMainWindow(); mainWindow = await createMainWindow();
} else if (!mainWindow.isVisible()) { } else if (!mainWindow.isVisible()) {
mainWindow.show(); mainWindow.show();
} }
}); });
app.on('ready', () => { app.on('ready', async () => {
if (config.get('options.autoResetAppCache')) { if (config.get('options.autoResetAppCache')) {
// Clear cache after 20s // Clear cache after 20s
const clearCacheTimeout = setTimeout(() => { const clearCacheTimeout = setTimeout(() => {
@ -469,8 +468,9 @@ app.on('ready', () => {
} }
} }
mainWindow = createMainWindow(); mainWindow = await createMainWindow();
setApplicationMenu(mainWindow); setApplicationMenu(mainWindow);
refreshMenu(mainWindow);
setUpTray(app, mainWindow); setUpTray(app, mainWindow);
setupProtocolHandler(mainWindow); setupProtocolHandler(mainWindow);
@ -602,8 +602,6 @@ function showUnresponsiveDialog(win: BrowserWindow, details: Electron.RenderProc
}); });
} }
// HACK: electron-better-web-request's typing is wrong
type BetterSession = Omit<Electron.Session, 'webRequest'> & { webRequest: BetterWebRequest & Electron.WebRequest };
function removeContentSecurityPolicy( function removeContentSecurityPolicy(
betterSession: BetterSession = session.defaultSession as BetterSession, betterSession: BetterSession = session.defaultSession as BetterSession,
) { ) {
@ -623,11 +621,10 @@ function removeContentSecurityPolicy(
callback({ cancel: false, responseHeaders: details.responseHeaders }); callback({ cancel: false, responseHeaders: details.responseHeaders });
}); });
type ResolverListener = { apply: () => Promise<Record<string, unknown>>; context: unknown };
// When multiple listeners are defined, apply them all // When multiple listeners are defined, apply them all
betterSession.webRequest.setResolver('onHeadersReceived', async (listeners: ResolverListener[]) => { betterSession.webRequest.setResolver('onHeadersReceived', async (listeners) => {
return listeners.reduce<Promise<Record<string, unknown>>>( return listeners.reduce(
async (accumulator: Promise<Record<string, unknown>>, listener: ResolverListener) => { async (accumulator, listener) => {
const acc = await accumulator; const acc = await accumulator;
if (acc.cancel) { if (acc.cancel) {
return acc; return acc;

20
menu.ts
View File

@ -62,13 +62,15 @@ const pluginEnabledMenu = (plugin: string, label = '', hasSubmenu = false, refre
}, },
}); });
export const refreshMenu = (win: BrowserWindow) => {
setApplicationMenu(win);
if (inAppMenuActive) {
win.webContents.send('refreshMenu');
}
};
export const mainMenuTemplate = (win: BrowserWindow): MenuTemplate => { export const mainMenuTemplate = (win: BrowserWindow): MenuTemplate => {
const refreshMenu = () => { const innerRefreshMenu = () => refreshMenu(win);
setApplicationMenu(win);
if (inAppMenuActive) {
win.webContents.send('refreshMenu');
}
};
return [ return [
{ {
@ -84,15 +86,15 @@ export const mainMenuTemplate = (win: BrowserWindow): MenuTemplate => {
const getPluginMenu = pluginMenus[pluginName as keyof typeof pluginMenus]; const getPluginMenu = pluginMenus[pluginName as keyof typeof pluginMenus];
if (!config.plugins.isEnabled(pluginName)) { if (!config.plugins.isEnabled(pluginName)) {
return pluginEnabledMenu(pluginName, pluginLabel, true, refreshMenu); return pluginEnabledMenu(pluginName, pluginLabel, true, innerRefreshMenu);
} }
return { return {
label: pluginLabel, label: pluginLabel,
submenu: [ submenu: [
pluginEnabledMenu(pluginName, 'Enabled', true, refreshMenu), pluginEnabledMenu(pluginName, 'Enabled', true, innerRefreshMenu),
{ type: 'separator' }, { type: 'separator' },
...getPluginMenu(win, config.plugins.getOptions(pluginName), refreshMenu), ...getPluginMenu(win, config.plugins.getOptions(pluginName), innerRefreshMenu),
], ],
} satisfies Electron.MenuItemConstructorOptions; } satisfies Electron.MenuItemConstructorOptions;
} }

366
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "youtube-music", "name": "youtube-music",
"version": "2.0.3", "version": "2.0.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "youtube-music", "name": "youtube-music",
"version": "2.0.3", "version": "2.0.4",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -14,14 +14,14 @@
"@ffmpeg.wasm/core-mt": "0.12.0", "@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/main": "0.12.0", "@ffmpeg.wasm/main": "0.12.0",
"@foobar404/wave": "2.0.4", "@foobar404/wave": "2.0.4",
"@jellybrick/electron-better-web-request": "1.0.4",
"@jellybrick/mpris-service": "2.1.4", "@jellybrick/mpris-service": "2.1.4",
"@xhayper/discord-rpc": "1.0.23", "@xhayper/discord-rpc": "1.0.23",
"async-mutex": "0.4.0", "async-mutex": "0.4.0",
"butterchurn": "2.6.7", "butterchurn": "3.0.0-beta.4",
"butterchurn-presets": "2.4.7", "butterchurn-presets": "3.0.0-beta.4",
"conf": "10.2.0", "conf": "10.2.0",
"custom-electron-prompt": "1.5.7", "custom-electron-prompt": "1.5.7",
"electron-better-web-request": "1.0.1",
"electron-debug": "3.2.0", "electron-debug": "3.2.0",
"electron-is": "3.0.0", "electron-is": "3.0.0",
"electron-localshortcut": "3.2.1", "electron-localshortcut": "3.2.1",
@ -35,7 +35,7 @@
"keyboardevent-from-electron-accelerator": "2.0.0", "keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2", "keyboardevents-areequal": "0.2.2",
"node-id3": "0.2.6", "node-id3": "0.2.6",
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5", "simple-youtube-age-restriction-bypass": "git+https://github.com/organization/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.8",
"vudio": "2.1.1", "vudio": "2.1.1",
"x11": "2.3.0", "x11": "2.3.0",
"youtubei.js": "6.4.1", "youtubei.js": "6.4.1",
@ -54,15 +54,15 @@
"@types/electron-localshortcut": "3.1.1", "@types/electron-localshortcut": "3.1.1",
"@types/howler": "2.2.9", "@types/howler": "2.2.9",
"@types/html-to-text": "9.0.2", "@types/html-to-text": "9.0.2",
"@typescript-eslint/eslint-plugin": "6.7.4", "@typescript-eslint/eslint-plugin": "6.7.5",
"auto-changelog": "2.4.0", "auto-changelog": "2.4.0",
"del-cli": "5.1.0", "del-cli": "5.1.0",
"electron": "27.0.0-beta.9", "electron": "27.0.0",
"electron-builder": "24.6.4", "electron-builder": "24.6.4",
"electron-devtools-installer": "3.2.0", "electron-devtools-installer": "3.2.0",
"eslint": "8.51.0", "eslint": "8.51.0",
"eslint-plugin-import": "2.28.1", "eslint-plugin-import": "2.28.1",
"eslint-plugin-prettier": "5.0.0", "eslint-plugin-prettier": "5.0.1",
"node-gyp": "9.4.0", "node-gyp": "9.4.0",
"patch-package": "8.0.0", "patch-package": "8.0.0",
"playwright": "1.38.1", "playwright": "1.38.1",
@ -85,6 +85,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@assemblyscript/loader": {
"version": "0.17.14",
"resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.17.14.tgz",
"integrity": "sha512-+PVTOfla/0XMLRTQLJFPg4u40XcdTfon6GGea70hBGi8Pd7ZymIXyVUR+vK8wt5Jb4MVKTKPIz43Myyebw5mZA=="
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.22.13", "version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
@ -882,6 +887,27 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1" "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
} }
}, },
"node_modules/@jellybrick/electron-better-web-request": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@jellybrick/electron-better-web-request/-/electron-better-web-request-1.0.4.tgz",
"integrity": "sha512-vL2lv7Gz8BWgCpwXb3ha17oaEmJqG5ZLdVWssAkA/0PGPMCWH2lLWq7vDymyTswmZ+zKpfOdzwTomvMqn9nElg==",
"dependencies": {
"browser-extension-url-match": "^1.0.0",
"uuid": "^9.0.1"
}
},
"node_modules/@jellybrick/electron-better-web-request/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/@jellybrick/mpris-service": { "node_modules/@jellybrick/mpris-service": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@jellybrick/mpris-service/-/mpris-service-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@jellybrick/mpris-service/-/mpris-service-2.1.4.tgz",
@ -1774,16 +1800,16 @@
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.7.4", "version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.5.tgz",
"integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", "integrity": "sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.5.1", "@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.7.4", "@typescript-eslint/scope-manager": "6.7.5",
"@typescript-eslint/type-utils": "6.7.4", "@typescript-eslint/type-utils": "6.7.5",
"@typescript-eslint/utils": "6.7.4", "@typescript-eslint/utils": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.4", "@typescript-eslint/visitor-keys": "6.7.5",
"debug": "^4.3.4", "debug": "^4.3.4",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.2.4", "ignore": "^5.2.4",
@ -1808,6 +1834,53 @@
} }
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz",
"integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz",
"integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz",
"integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "6.7.4", "version": "6.7.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz",
@ -1842,6 +1915,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz",
"integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "6.7.4", "@typescript-eslint/types": "6.7.4",
"@typescript-eslint/visitor-keys": "6.7.4" "@typescript-eslint/visitor-keys": "6.7.4"
@ -1855,13 +1929,13 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "6.7.4", "version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.5.tgz",
"integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", "integrity": "sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "6.7.4", "@typescript-eslint/typescript-estree": "6.7.5",
"@typescript-eslint/utils": "6.7.4", "@typescript-eslint/utils": "6.7.5",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^1.0.1" "ts-api-utils": "^1.0.1"
}, },
@ -1881,11 +1955,69 @@
} }
} }
}, },
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz",
"integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz",
"integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz",
"integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "6.7.4", "version": "6.7.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz",
"integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": "^16.0.0 || >=18.0.0" "node": "^16.0.0 || >=18.0.0"
}, },
@ -1899,6 +2031,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz",
"integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "6.7.4", "@typescript-eslint/types": "6.7.4",
"@typescript-eslint/visitor-keys": "6.7.4", "@typescript-eslint/visitor-keys": "6.7.4",
@ -1922,17 +2055,17 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "6.7.4", "version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.5.tgz",
"integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", "integrity": "sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12", "@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0", "@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.7.4", "@typescript-eslint/scope-manager": "6.7.5",
"@typescript-eslint/types": "6.7.4", "@typescript-eslint/types": "6.7.5",
"@typescript-eslint/typescript-estree": "6.7.4", "@typescript-eslint/typescript-estree": "6.7.5",
"semver": "^7.5.4" "semver": "^7.5.4"
}, },
"engines": { "engines": {
@ -1946,11 +2079,86 @@
"eslint": "^7.0.0 || ^8.0.0" "eslint": "^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz",
"integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz",
"integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz",
"integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz",
"integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.4", "version": "6.7.4",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz",
"integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "6.7.4", "@typescript-eslint/types": "6.7.4",
"eslint-visitor-keys": "^3.4.1" "eslint-visitor-keys": "^3.4.1"
@ -2543,20 +2751,6 @@
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }
}, },
"node_modules/babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
"dependencies": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -2656,6 +2850,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/browser-extension-url-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/browser-extension-url-match/-/browser-extension-url-match-1.0.0.tgz",
"integrity": "sha512-LfIs9SYgPjYksjxkgOVYZhxMIroR56isQB3YHTAmzunWuT9qrH6Fxt7TD9/s9MoKo7GP37JZbLlZhL9vwQAk3w==",
"dependencies": {
"fancy-regex": "^0.5.4"
}
},
"node_modules/buffer": { "node_modules/buffer": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@ -2805,22 +3007,22 @@
} }
}, },
"node_modules/butterchurn": { "node_modules/butterchurn": {
"version": "2.6.7", "version": "3.0.0-beta.4",
"resolved": "https://registry.npmjs.org/butterchurn/-/butterchurn-2.6.7.tgz", "resolved": "https://registry.npmjs.org/butterchurn/-/butterchurn-3.0.0-beta.4.tgz",
"integrity": "sha512-BJiRA8L0L2+84uoG2SSfkp0kclBuN+vQKf217pK7pMlwEO2ZEg3MtO2/o+l8Qpr8Nbejg8tmL1ZHD1jmhiaaqg==", "integrity": "sha512-hiY1ktHYHQ8MT65nnZi7GjrgZZ6sl/ipT5rBqEfaYJd90L4SvOtB6lVxtKadtzAyJo2TQJc4gJfEca4cpZo0DA==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.0.0", "@assemblyscript/loader": "^0.17.11",
"ecma-proposal-math-extensions": "0.0.2" "@babel/runtime": "^7.11.2",
"ecma-proposal-math-extensions": "0.0.2",
"eel-wasm": "^0.0.15"
} }
}, },
"node_modules/butterchurn-presets": { "node_modules/butterchurn-presets": {
"version": "2.4.7", "version": "3.0.0-beta.4",
"resolved": "https://registry.npmjs.org/butterchurn-presets/-/butterchurn-presets-2.4.7.tgz", "resolved": "https://registry.npmjs.org/butterchurn-presets/-/butterchurn-presets-3.0.0-beta.4.tgz",
"integrity": "sha512-4MdM8ripz/VfH1BCldrIKdAc/1ryJFBDvqlyow6Ivo1frwj0H3duzvSMFC7/wIjAjxb1QpwVHVqGqS9uAFKhpg==", "integrity": "sha512-TbQLUPvGOYMZAtWKoCmBtludh9aQZ6NaMGQU4lvPeadBPy3Du3yNmwBjlTMLP5c5mRWElxQPjTL1PtR7FZK3OQ==",
"dependencies": { "dependencies": {
"babel-runtime": "^6.26.0", "@babel/runtime": "^7.12.5"
"ecma-proposal-math-extensions": "0.0.2",
"lodash": "^4.17.4"
} }
}, },
"node_modules/cacache": { "node_modules/cacache": {
@ -3263,13 +3465,6 @@
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
"devOptional": true "devOptional": true
}, },
"node_modules/core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true
},
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@ -3849,6 +4044,11 @@
"resolved": "https://registry.npmjs.org/ecma-proposal-math-extensions/-/ecma-proposal-math-extensions-0.0.2.tgz", "resolved": "https://registry.npmjs.org/ecma-proposal-math-extensions/-/ecma-proposal-math-extensions-0.0.2.tgz",
"integrity": "sha512-80BnDp2Fn7RxXlEr5HHZblniY4aQ97MOAicdWWpSo0vkQiISSE9wLR4SqxKsu4gCtXFBIPPzy8JMhay4NWRg/Q==" "integrity": "sha512-80BnDp2Fn7RxXlEr5HHZblniY4aQ97MOAicdWWpSo0vkQiISSE9wLR4SqxKsu4gCtXFBIPPzy8JMhay4NWRg/Q=="
}, },
"node_modules/eel-wasm": {
"version": "0.0.15",
"resolved": "https://registry.npmjs.org/eel-wasm/-/eel-wasm-0.0.15.tgz",
"integrity": "sha512-FSTWf6lwGn7Zc3QiV+KxWTznIqq4j9eST/aXmyN/cC39+1Arqs13YOMosHQ7tqUt+OjQmG79Vd41f9gu+w1lvA=="
},
"node_modules/ejs": { "node_modules/ejs": {
"version": "3.1.9", "version": "3.1.9",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
@ -3865,9 +4065,9 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "27.0.0-beta.9", "version": "27.0.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-27.0.0-beta.9.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.0.tgz",
"integrity": "sha512-4h78B843eYU2oh2yMjrwYtl5aVMiFdpurIwtoZvXpfTwyuKiEOQaylmLbi4+sPg1rCmqW/VLv9hfVlOqHDNj/Q==", "integrity": "sha512-mr3Zoy82l8XKK/TgguE5FeNeHZ9KHXIGIpUMjbjZWIREfAv+X2Q3vdX6RG0Pmi1K23AFAxANXQezIHBA2Eypwg==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
@ -3881,15 +4081,6 @@
"node": ">= 12.20.55" "node": ">= 12.20.55"
} }
}, },
"node_modules/electron-better-web-request": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/electron-better-web-request/-/electron-better-web-request-1.0.1.tgz",
"integrity": "sha512-euwLeL82k6fbVODfH5Uz9c4BN047/XyYKfsZcaFhdWfqx05JPu2J0xE7nciJ/1Bb0sTClU1FDLW5H2zQWBB5Gw==",
"dependencies": {
"url-match-patterns": "^0.2.0",
"uuid": "^3.3.2"
}
},
"node_modules/electron-builder": { "node_modules/electron-builder": {
"version": "24.6.4", "version": "24.6.4",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz", "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz",
@ -4557,9 +4748,9 @@
} }
}, },
"node_modules/eslint-plugin-prettier": { "node_modules/eslint-plugin-prettier": {
"version": "5.0.0", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
"integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"prettier-linter-helpers": "^1.0.0", "prettier-linter-helpers": "^1.0.0",
@ -4808,6 +4999,11 @@
], ],
"optional": true "optional": true
}, },
"node_modules/fancy-regex": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/fancy-regex/-/fancy-regex-0.5.4.tgz",
"integrity": "sha512-O6qfjtMnrPRs+3XOavCxGQDFaMS9K1vEsQMhPowqx2P/h1fDCvK5RUyeWeyDusMH2FkSHAsRE3IbSBMMg53fmw=="
},
"node_modules/fast-average-color": { "node_modules/fast-average-color": {
"version": "9.4.0", "version": "9.4.0",
"resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.4.0.tgz", "resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.4.0.tgz",
@ -6587,7 +6783,8 @@
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
}, },
"node_modules/lodash.debounce": { "node_modules/lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
@ -8744,7 +8941,7 @@
}, },
"node_modules/simple-youtube-age-restriction-bypass": { "node_modules/simple-youtube-age-restriction-bypass": {
"version": "2.5.9", "version": "2.5.9",
"resolved": "git+ssh://git@github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#79b9456c290df42f35f081413e18dc336d340724", "resolved": "git+ssh://git@github.com/organization/Simple-YouTube-Age-Restriction-Bypass.git#816a882c68fcfe6cdd9410a6877b88093ed15b28",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"account-proxy" "account-proxy"
@ -9601,14 +9798,6 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/url-match-patterns": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/url-match-patterns/-/url-match-patterns-0.2.0.tgz",
"integrity": "sha512-vtaWyxq+CyrQP4/dapGddkSGwGypQOD2qjHcsqp9ahsjRWzGtjqm+ANxApH46OfWQfpkL6cuyPwsm80386jdjQ==",
"dependencies": {
"lodash": "^4.3.0"
}
},
"node_modules/usocket": { "node_modules/usocket": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/usocket/-/usocket-0.3.0.tgz", "resolved": "https://registry.npmjs.org/usocket/-/usocket-0.3.0.tgz",
@ -9633,15 +9822,6 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"devOptional": true "devOptional": true
}, },
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/validate-npm-package-license": { "node_modules/validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",

View File

@ -1,7 +1,7 @@
{ {
"name": "youtube-music", "name": "youtube-music",
"productName": "YouTube Music", "productName": "YouTube Music",
"version": "2.0.3", "version": "2.0.4",
"description": "YouTube Music Desktop App - including custom plugins", "description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/index.js", "main": "./dist/index.js",
"license": "MIT", "license": "MIT",
@ -21,8 +21,6 @@
"!node_modules", "!node_modules",
"node_modules/custom-electron-prompt/**", "node_modules/custom-electron-prompt/**",
"node_modules/@cliqz/adblocker-electron-preload/**", "node_modules/@cliqz/adblocker-electron-preload/**",
"node_modules/@cliqz/adblocker-content/**",
"node_modules/@cliqz/adblocker-extended-selectors/**",
"node_modules/@ffmpeg.wasm/core-mt/**", "node_modules/@ffmpeg.wasm/core-mt/**",
"!node_modules/**/*.map", "!node_modules/**/*.map",
"!node_modules/**/*.ts" "!node_modules/**/*.ts"
@ -96,8 +94,7 @@
"build": "npm run rollup:preload && npm run rollup:main", "build": "npm run rollup:preload && npm run rollup:main",
"start": "npm run build && electron ./dist/index.js", "start": "npm run build && electron ./dist/index.js",
"start:debug": "ELECTRON_ENABLE_LOGGING=1 npm run start", "start:debug": "ELECTRON_ENABLE_LOGGING=1 npm run start",
"generate:package": "node utils/generate-package-json.js", "postinstall": "patch-package",
"postinstall": "patch-package && npm run plugins && npm run clean",
"clean": "del-cli dist && del-cli pack", "clean": "del-cli dist && del-cli pack",
"dist": "npm run clean && npm run build && electron-builder --win --mac --linux -p never", "dist": "npm run clean && npm run build && electron-builder --win --mac --linux -p never",
"dist:linux": "npm run clean && npm run build && electron-builder --linux -p never", "dist:linux": "npm run clean && npm run build && electron-builder --linux -p never",
@ -107,8 +104,6 @@
"dist:win:x64": "npm run clean && npm run build && electron-builder --win nsis-web:x64 -p never", "dist:win:x64": "npm run clean && npm run build && electron-builder --win nsis-web:x64 -p never",
"lint": "eslint .", "lint": "eslint .",
"changelog": "auto-changelog", "changelog": "auto-changelog",
"plugins": "npm run plugin:bypass-age-restrictions",
"plugin:bypass-age-restrictions": "del-cli node_modules/simple-youtube-age-restriction-bypass/package.json && npm run generate:package simple-youtube-age-restriction-bypass",
"release:linux": "npm run clean && npm run build && electron-builder --linux -p always -c.snap.publish=github", "release:linux": "npm run clean && npm run build && electron-builder --linux -p always -c.snap.publish=github",
"release:mac": "npm run clean && npm run build && electron-builder --mac -p always", "release:mac": "npm run clean && npm run build && electron-builder --mac -p always",
"release:win": "npm run clean && npm run build && electron-builder --win -p always", "release:win": "npm run clean && npm run build && electron-builder --win -p always",
@ -122,14 +117,14 @@
"@ffmpeg.wasm/core-mt": "0.12.0", "@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/main": "0.12.0", "@ffmpeg.wasm/main": "0.12.0",
"@foobar404/wave": "2.0.4", "@foobar404/wave": "2.0.4",
"@jellybrick/electron-better-web-request": "1.0.4",
"@jellybrick/mpris-service": "2.1.4", "@jellybrick/mpris-service": "2.1.4",
"@xhayper/discord-rpc": "1.0.23", "@xhayper/discord-rpc": "1.0.23",
"async-mutex": "0.4.0", "async-mutex": "0.4.0",
"butterchurn": "2.6.7", "butterchurn": "3.0.0-beta.4",
"butterchurn-presets": "2.4.7", "butterchurn-presets": "3.0.0-beta.4",
"conf": "10.2.0", "conf": "10.2.0",
"custom-electron-prompt": "1.5.7", "custom-electron-prompt": "1.5.7",
"electron-better-web-request": "1.0.1",
"electron-debug": "3.2.0", "electron-debug": "3.2.0",
"electron-is": "3.0.0", "electron-is": "3.0.0",
"electron-localshortcut": "3.2.1", "electron-localshortcut": "3.2.1",
@ -143,7 +138,7 @@
"keyboardevent-from-electron-accelerator": "2.0.0", "keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2", "keyboardevents-areequal": "0.2.2",
"node-id3": "0.2.6", "node-id3": "0.2.6",
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5", "simple-youtube-age-restriction-bypass": "git+https://github.com/organization/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.8",
"vudio": "2.1.1", "vudio": "2.1.1",
"x11": "2.3.0", "x11": "2.3.0",
"youtubei.js": "6.4.1", "youtubei.js": "6.4.1",
@ -155,7 +150,7 @@
"xml2js": "0.6.2", "xml2js": "0.6.2",
"node-fetch": "2.7.0", "node-fetch": "2.7.0",
"@electron/universal": "1.4.2", "@electron/universal": "1.4.2",
"electron": "27.0.0-beta.9" "@babel/runtime": "7.23.1"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.38.1", "@playwright/test": "1.38.1",
@ -170,15 +165,15 @@
"@types/electron-localshortcut": "3.1.1", "@types/electron-localshortcut": "3.1.1",
"@types/howler": "2.2.9", "@types/howler": "2.2.9",
"@types/html-to-text": "9.0.2", "@types/html-to-text": "9.0.2",
"@typescript-eslint/eslint-plugin": "6.7.4", "@typescript-eslint/eslint-plugin": "6.7.5",
"auto-changelog": "2.4.0", "auto-changelog": "2.4.0",
"del-cli": "5.1.0", "del-cli": "5.1.0",
"electron": "27.0.0-beta.9", "electron": "27.0.0",
"electron-builder": "24.6.4", "electron-builder": "24.6.4",
"electron-devtools-installer": "3.2.0", "electron-devtools-installer": "3.2.0",
"eslint": "8.51.0", "eslint": "8.51.0",
"eslint-plugin-import": "2.28.1", "eslint-plugin-import": "2.28.1",
"eslint-plugin-prettier": "5.0.0", "eslint-plugin-prettier": "5.0.1",
"node-gyp": "9.4.0", "node-gyp": "9.4.0",
"patch-package": "8.0.0", "patch-package": "8.0.0",
"playwright": "1.38.1", "playwright": "1.38.1",

View File

@ -8,8 +8,8 @@ import type { ConfigType } from '../../config/dynamic';
type AdBlockOptions = ConfigType<'adblocker'>; type AdBlockOptions = ConfigType<'adblocker'>;
export default async (win: BrowserWindow, options: AdBlockOptions) => { export default async (win: BrowserWindow, options: AdBlockOptions) => {
if (await shouldUseBlocklists()) { if (shouldUseBlocklists()) {
loadAdBlockerEngine( await loadAdBlockerEngine(
win.webContents.session, win.webContents.session,
options.cache, options.cache,
options.additionalBlockLists, options.additionalBlockLists,

View File

@ -3,7 +3,7 @@ import path from 'node:path';
import fs, { promises } from 'node:fs'; import fs, { promises } from 'node:fs';
import { ElectronBlocker } from '@cliqz/adblocker-electron'; import { ElectronBlocker } from '@cliqz/adblocker-electron';
import { app } from 'electron'; import { app, net } from 'electron';
const SOURCES = [ const SOURCES = [
'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt', 'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt',
@ -17,7 +17,7 @@ const SOURCES = [
'https://secure.fanboy.co.nz/fanboy-annoyance_ubo.txt', 'https://secure.fanboy.co.nz/fanboy-annoyance_ubo.txt',
]; ];
export const loadAdBlockerEngine = ( export const loadAdBlockerEngine = async (
session: Electron.Session | undefined = undefined, session: Electron.Session | undefined = undefined,
cache = true, cache = true,
additionalBlockLists = [], additionalBlockLists = [],
@ -49,25 +49,24 @@ export const loadAdBlockerEngine = (
...additionalBlockLists, ...additionalBlockLists,
]; ];
ElectronBlocker.fromLists( try {
fetch, const blocker = await ElectronBlocker.fromLists(
lists, (url: string) => net.fetch(url),
{ lists,
// When generating the engine for caching, do not load network filters {
// So that enhancing the session works as expected // When generating the engine for caching, do not load network filters
// Allowing to define multiple webRequest listeners // So that enhancing the session works as expected
loadNetworkFilters: session !== undefined, // Allowing to define multiple webRequest listeners
}, loadNetworkFilters: session !== undefined,
cachingOptions, },
) cachingOptions,
.then((blocker) => { );
if (session) { if (session) {
blocker.enableBlockingInSession(session); blocker.enableBlockingInSession(session);
} else { }
console.log('Successfully generated adBlocker engine.'); } catch (error) {
} console.log('Error loading adBlocker engine', error);
}) }
.catch((error) => console.log('Error loading adBlocker engine', error));
}; };
export default { loadAdBlockerEngine }; export default { loadAdBlockerEngine };

View File

@ -7,7 +7,7 @@ import { PluginConfig } from '../../config/dynamic';
const config = new PluginConfig('adblocker', { enableFront: true }); const config = new PluginConfig('adblocker', { enableFront: true });
export const shouldUseBlocklists = async () => await config.get('blocker') !== blockers.InPlayer; export const shouldUseBlocklists = () => config.get('blocker') !== blockers.InPlayer;
export default Object.assign(config, { export default Object.assign(config, {
shouldUseBlocklists, shouldUseBlocklists,

View File

@ -1,4 +1,3 @@
export default () => { export default async () => {
const path = '@cliqz/adblocker-electron-preload'; // prevent require hoisting await import('@cliqz/adblocker-electron-preload');
require(path);
}; };

View File

@ -1,15 +1,15 @@
import config from './config'; import config, { shouldUseBlocklists } from './config';
import inject from './inject'; import inject from './inject';
import injectCliqzPreload from './inject-cliqz-preload'; import injectCliqzPreload from './inject-cliqz-preload';
import { blockers } from './blocker-types'; import { blockers } from './blocker-types';
export default async () => { export default async () => {
if (await config.shouldUseBlocklists()) { if (shouldUseBlocklists()) {
// Preload adblocker to inject scripts/styles // Preload adblocker to inject scripts/styles
injectCliqzPreload(); await injectCliqzPreload();
// eslint-disable-next-line @typescript-eslint/await-thenable // eslint-disable-next-line @typescript-eslint/await-thenable
} else if ((await config.get('blocker')) === blockers.InPlayer) { } else if ((config.get('blocker')) === blockers.InPlayer) {
inject(); inject();
} }
}; };

View File

@ -1,4 +1,4 @@
export default () => { export default async () => {
// See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass#userscript // See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass#userscript
require('simple-youtube-age-restriction-bypass/dist/Simple-YouTube-Age-Restriction-Bypass.user.js'); await import('simple-youtube-age-restriction-bypass');
}; };

View File

@ -0,0 +1,4 @@
declare module 'simple-youtube-age-restriction-bypass' {
const nothing: never;
export default nothing;
}

View File

@ -3,23 +3,14 @@ import { join } from 'node:path';
import { randomBytes } from 'node:crypto'; import { randomBytes } from 'node:crypto';
import { app, BrowserWindow, dialog, ipcMain, net } from 'electron'; import { app, BrowserWindow, dialog, ipcMain, net } from 'electron';
import { ClientType, Innertube, UniversalCache, Utils } from 'youtubei.js'; import { ClientType, Innertube, UniversalCache, Utils, YTNodes } from 'youtubei.js';
import is from 'electron-is'; import is from 'electron-is';
import ytpl from 'ytpl';
// REPLACE with youtubei getplaylist https://github.com/LuanRT/YouTube.js#getplaylistid
import filenamify from 'filenamify'; import filenamify from 'filenamify';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { createFFmpeg } from '@ffmpeg.wasm/main'; import { createFFmpeg } from '@ffmpeg.wasm/main';
import NodeID3, { TagConstants } from 'node-id3'; import NodeID3, { TagConstants } from 'node-id3';
import PlayerErrorMessage from 'youtubei.js/dist/src/parser/classes/PlayerErrorMessage';
import { FormatOptions } from 'youtubei.js/dist/src/types/FormatUtils';
import TrackInfo from 'youtubei.js/dist/src/parser/ytmusic/TrackInfo';
import { VideoInfo } from 'youtubei.js/dist/src/parser/youtube';
import { cropMaxWidth, getFolder, presets, sendFeedback as sendFeedback_, setBadge } from './utils'; import { cropMaxWidth, getFolder, presets, sendFeedback as sendFeedback_, setBadge } from './utils';
import config from './config'; import config from './config';
@ -32,8 +23,13 @@ import { cleanupName, getImage, SongInfo } from '../../providers/song-info';
import { injectCSS } from '../utils'; import { injectCSS } from '../utils';
import { cache } from '../../providers/decorators'; import { cache } from '../../providers/decorators';
import type { GetPlayerResponse } from '../../types/get-player-response'; import type { FormatOptions } from 'youtubei.js/dist/src/types/FormatUtils';
import type PlayerErrorMessage from 'youtubei.js/dist/src/parser/classes/PlayerErrorMessage';
import type { Playlist } from 'youtubei.js/dist/src/parser/ytmusic';
import type { VideoInfo } from 'youtubei.js/dist/src/parser/youtube';
import type TrackInfo from 'youtubei.js/dist/src/parser/ytmusic/TrackInfo';
import type { GetPlayerResponse } from '../../types/get-player-response';
type CustomSongInfo = SongInfo & { trackId?: string }; type CustomSongInfo = SongInfo & { trackId?: string };
@ -69,16 +65,19 @@ const sendError = (error: Error, source?: string) => {
}); });
}; };
export const getCookieFromWindow = async (win: BrowserWindow) => {
return (await win.webContents.session.cookies.get({ url: 'https://music.youtube.com' })).map((it) =>
it.name + '=' + it.value + ';'
).join('');
};
export default async (win_: BrowserWindow) => { export default async (win_: BrowserWindow) => {
win = win_; win = win_;
injectCSS(win.webContents, style); injectCSS(win.webContents, style);
const cookie = (await win.webContents.session.cookies.get({ url: 'https://music.youtube.com' })).map((it) =>
it.name + '=' + it.value + ';'
).join('');
yt = await Innertube.create({ yt = await Innertube.create({
cache: new UniversalCache(false), cache: new UniversalCache(false),
cookie, cookie: await getCookieFromWindow(win),
generate_session_locally: true, generate_session_locally: true,
fetch: async (input: RequestInfo | URL, init?: RequestInit) => { fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
const url = const url =
@ -118,6 +117,7 @@ export async function downloadSong(
let resolvedName; let resolvedName;
try { try {
await downloadSongUnsafe( await downloadSongUnsafe(
false,
url, url,
(name: string) => resolvedName = name, (name: string) => resolvedName = name,
playlistFolder, playlistFolder,
@ -129,8 +129,31 @@ export async function downloadSong(
} }
} }
export async function downloadSongFromId(
id: string,
playlistFolder: string | undefined = undefined,
trackId: string | undefined = undefined,
increasePlaylistProgress: (value: number) => void = () => {
},
) {
let resolvedName;
try {
await downloadSongUnsafe(
true,
id,
(name: string) => resolvedName = name,
playlistFolder,
trackId,
increasePlaylistProgress,
);
} catch (error: unknown) {
sendError(error as Error, resolvedName || id);
}
}
async function downloadSongUnsafe( async function downloadSongUnsafe(
url: string, isId: boolean,
idOrUrl: string,
setName: (name: string) => void, setName: (name: string) => void,
playlistFolder: string | undefined = undefined, playlistFolder: string | undefined = undefined,
trackId: string | undefined = undefined, trackId: string | undefined = undefined,
@ -147,8 +170,13 @@ async function downloadSongUnsafe(
sendFeedback('Downloading...', 2); sendFeedback('Downloading...', 2);
const id = getVideoId(url); let id: string | null;
if (typeof id !== 'string') throw new Error('Video not found'); if (isId) {
id = idOrUrl;
} else {
id = getVideoId(idOrUrl);
if (typeof id !== 'string') throw new Error('Video not found');
}
let info: TrackInfo | VideoInfo = await yt.music.getInfo(id); let info: TrackInfo | VideoInfo = await yt.music.getInfo(id);
@ -199,10 +227,13 @@ async function downloadSongUnsafe(
presetSetting = presets[preset]; presetSetting = presets[preset];
} }
const filename = filenamify(`${name}.${presetSetting?.extension ?? 'mp3'}`, { let filename = filenamify(`${name}.${presetSetting?.extension ?? 'mp3'}`, {
replacement: '_', replacement: '_',
maxLength: 255, maxLength: 255,
}); });
if (!is.macOS()) {
filename = filename.normalize('NFC');
}
const filePath = join(dir, filename); const filePath = join(dir, filename);
if (config.get('skipExisting') && existsSync(filePath)) { if (config.get('skipExisting') && existsSync(filePath)) {
@ -414,11 +445,9 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
console.log(`trying to get playlist ID: '${playlistId}'`); console.log(`trying to get playlist ID: '${playlistId}'`);
sendFeedback('Getting playlist info…'); sendFeedback('Getting playlist info…');
let playlist: ytpl.Result; let playlist: Playlist;
try { try {
playlist = await ytpl(playlistId, { playlist = await yt.music.getPlaylist(playlistId);
limit: config.get('playlistMaxItems') || Number.POSITIVE_INFINITY,
});
} catch (error: unknown) { } catch (error: unknown) {
sendError( sendError(
Error(`Error getting playlist info: make sure it isn't a private or "Mixed for you" playlist\n\n${String(error)}`), Error(`Error getting playlist info: make sure it isn't a private or "Mixed for you" playlist\n\n${String(error)}`),
@ -426,22 +455,27 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
return; return;
} }
if (playlist.items.length === 0) { if (!playlist || !playlist.items || playlist.items.length === 0) {
sendError(new Error('Playlist is empty')); sendError(new Error('Playlist is empty'));
} }
if (playlist.items.length === 1) { const items = playlist.items!.as(YTNodes.MusicResponsiveListItem);
if (items.length === 1) {
sendFeedback('Playlist has only one item, downloading it directly'); sendFeedback('Playlist has only one item, downloading it directly');
await downloadSong(playlist.items[0].url); await downloadSongFromId(items.at(0)!.id!);
return; return;
} }
const isAlbum = playlist.title.startsWith('Album - '); let playlistTitle = playlist.header?.title?.text ?? '';
const isAlbum = playlistTitle?.startsWith('Album - ');
if (isAlbum) { if (isAlbum) {
playlist.title = playlist.title.slice(8); playlistTitle = playlistTitle.slice(8);
} }
const safePlaylistTitle = filenamify(playlist.title, { replacement: ' ' }); let safePlaylistTitle = filenamify(playlistTitle, { replacement: ' ' });
if (!is.macOS()) {
safePlaylistTitle = safePlaylistTitle.normalize('NFC');
}
const folder = getFolder(config.get('downloadFolder') ?? ''); const folder = getFolder(config.get('downloadFolder') ?? '');
const playlistFolder = join(folder, safePlaylistTitle); const playlistFolder = join(folder, safePlaylistTitle);
@ -458,47 +492,47 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
type: 'info', type: 'info',
buttons: ['OK'], buttons: ['OK'],
title: 'Started Download', title: 'Started Download',
message: `Downloading Playlist "${playlist.title}"`, message: `Downloading Playlist "${playlistTitle}"`,
detail: `(${playlist.items.length} songs)`, detail: `(${items.length} songs)`,
}); });
if (is.dev()) { if (is.dev()) {
console.log( console.log(
`Downloading playlist "${playlist.title}" - ${playlist.items.length} songs (${playlistId})`, `Downloading playlist "${playlistTitle}" - ${items.length} songs (${playlistId})`,
); );
} }
win.setProgressBar(2); // Starts with indefinite bar win.setProgressBar(2); // Starts with indefinite bar
setBadge(playlist.items.length); setBadge(items.length);
let counter = 1; let counter = 1;
const progressStep = 1 / playlist.items.length; const progressStep = 1 / items.length;
const increaseProgress = (itemPercentage: number) => { const increaseProgress = (itemPercentage: number) => {
const currentProgress = (counter - 1) / (playlist.items.length ?? 1); const currentProgress = (counter - 1) / (items.length ?? 1);
const newProgress = currentProgress + (progressStep * itemPercentage); const newProgress = currentProgress + (progressStep * itemPercentage);
win.setProgressBar(newProgress); win.setProgressBar(newProgress);
}; };
try { try {
for (const song of playlist.items) { for (const song of items) {
sendFeedback(`Downloading ${counter}/${playlist.items.length}...`); sendFeedback(`Downloading ${counter}/${items.length}...`);
const trackId = isAlbum ? counter : undefined; const trackId = isAlbum ? counter : undefined;
await downloadSong( await downloadSongFromId(
song.url, song.id!,
playlistFolder, playlistFolder,
trackId?.toString(), trackId?.toString(),
increaseProgress, increaseProgress,
).catch((error) => ).catch((error) =>
sendError( sendError(
new Error(`Error downloading "${song.author.name} - ${song.title}":\n ${error}`) new Error(`Error downloading "${song.author!.name} - ${song.title!}":\n ${error}`)
), ),
); );
win.setProgressBar(counter / playlist.items.length); win.setProgressBar(counter / items.length);
setBadge(playlist.items.length - counter); setBadge(items.length - counter);
counter++; counter++;
} }
} catch (error: unknown) { } catch (error: unknown) {

View File

@ -49,5 +49,7 @@ declare module 'butterchurn' {
} }
declare module 'butterchurn-presets' { declare module 'butterchurn-presets' {
export function getPresets(): Record<string, unknown>; const presets: Record<string, unknown>;
export default presets;
} }

View File

@ -5,8 +5,6 @@ import { Visualizer } from './visualizer';
import { ConfigType } from '../../../config/dynamic'; import { ConfigType } from '../../../config/dynamic';
const presets = ButterchurnPresets.getPresets();
class ButterchurnVisualizer extends Visualizer<Butterchurn> { class ButterchurnVisualizer extends Visualizer<Butterchurn> {
name = 'butterchurn'; name = 'butterchurn';
@ -41,7 +39,7 @@ class ButterchurnVisualizer extends Visualizer<Butterchurn> {
} }
); );
const preset = presets[options.butterchurn.preset]; const preset = ButterchurnPresets[options.butterchurn.preset];
this.visualizer.loadPreset(preset, options.butterchurn.blendTimeInSeconds); this.visualizer.loadPreset(preset, options.butterchurn.blendTimeInSeconds);
this.visualizer.connectAudio(audioNode); this.visualizer.connectAudio(audioNode);

View File

@ -187,7 +187,7 @@ function onApiLoaded() {
// Remove upgrade button // Remove upgrade button
if (config.get('options.removeUpgradeButton')) { if (config.get('options.removeUpgradeButton')) {
const styles = document.createElement('style'); const styles = document.createElement('style');
styles.innerHTML = `ytmusic-guide-section-renderer #items ytmusic-guide-entry-renderer:last-child { styles.innerHTML = `ytmusic-guide-section-renderer #items ytmusic-guide-entry-renderer:nth-child(4) {
display: none; display: none;
}`; }`;
document.head.appendChild(styles); document.head.appendChild(styles);

View File

@ -178,7 +178,7 @@ Some predefined themes are available in https://github.com/kerichdev/themes-for-
```bash ```bash
git clone https://github.com/th-ch/youtube-music git clone https://github.com/th-ch/youtube-music
cd youtube-music cd youtube-music
npm npm ci
npm run start npm run start
``` ```
@ -269,9 +269,9 @@ export default () => {
2. Run `npm i` to install dependencies 2. Run `npm i` to install dependencies
3. Run `npm run build:OS` 3. Run `npm run build:OS`
- `npm run build:win` - Windows - `npm run dist:win` - Windows
- `npm run build:linux` - Linux - `npm run dist:linux` - Linux
- `npm run build:mac` - MacOS - `npm run dist:mac` - MacOS
Builds the app for macOS, Linux, and Windows, Builds the app for macOS, Linux, and Windows,
using [electron-builder](https://github.com/electron-userland/electron-builder). using [electron-builder](https://github.com/electron-userland/electron-builder).

View File

@ -1,42 +0,0 @@
#!/usr/bin/env node
const { existsSync, writeFile } = require('node:fs');
const { join } = require('node:path');
const { promisify } = require('node:util');
/**
* Generates a fake package.json for given packages that don't have any.
* Allows electron-builder to resolve them
*/
const generatePackageJson = async (packageName) => {
const packageFolder = join('node_modules', packageName);
if (!existsSync(packageFolder)) {
console.log(
`${packageName} module not found, exiting…`,
);
return;
}
const filepath = join(packageFolder, 'package.json');
if (!existsSync(filepath)) {
console.log(
`No package.json found for ${packageName} module, generating one…`,
);
let pkg = {
name: packageName,
version: '0.0.0',
description: '-',
repository: { type: 'git', url: '-' },
readme: '-',
};
const writeFileAsync = promisify(writeFile);
await writeFileAsync(filepath, JSON.stringify(pkg, null, 2));
}
};
if (require.main === module) {
process.argv.slice(2).forEach(async (packageName) => {
await generatePackageJson(packageName);
});
}