Compare commits

...

41 Commits

Author SHA1 Message Date
5710307ddc Bump version to 2.0.3 2023-10-10 00:51:44 +09:00
52ba2dc9ff remove: migration scripts 2023-10-10 00:51:16 +09:00
926b9fb5e6 feat: add migration script 2023-10-10 00:42:26 +09:00
a6c9b3381a fix(discord): apply hideGitHubButton 2023-10-10 00:42:10 +09:00
5dc13a4698 feat(discord): add Hide GitHub link Button (#1293) 2023-10-10 00:15:15 +09:00
a69085c591 fix: chore(deps): update dependency @jellybrick/mpris-service to 2.1.4 (fix #971) 2023-10-09 21:55:23 +09:00
a22f7fed21 feat(deps): bundle youtubei.js (temporary solution) (#1292) 2023-10-09 21:30:53 +09:00
8b7045fb1b chore(deps): update dependency @rollup/plugin-node-resolve to v15.2.3 2023-10-09 20:16:40 +09:00
efd1b92514 feat(defaults): change the default value of blocker back to WithBlocklists 2023-10-09 19:52:02 +09:00
969f6d7bba chore(deps): Bump @cliqz/adblocker-electron to 1.26.8 (fix #1269) 2023-10-09 19:50:27 +09:00
4f7c92d6a0 feat(plugins/utils): mediaIcons as const 2023-10-09 19:49:40 +09:00
24d4a50574 fix(mpris): fixed an issue where MPRIS information was incorrect (#1291) 2023-10-09 19:36:17 +09:00
7693a3ba4a fix(discord): fixed an issue where timeChanged was not being applied to Discord activities (#1290) 2023-10-09 19:36:06 +09:00
7ca4dc5c85 Fix: typo in README (#1286) 2023-10-08 23:54:58 +09:00
21ff09b605 Merge pull request #1283 from th-ch/fix/missing-taskbar-mediacontrol-icons 2023-10-08 19:57:50 +09:00
fbf4b3b8b5 fix: missing icons taskbar-mediacontrol 2023-10-08 19:41:39 +09:00
5812eb0147 Update changelog for v2.0.2 2023-10-08 08:51:28 +00:00
b5dbfaf686 Bump version to 2.0.2 2023-10-08 17:35:40 +09:00
6b7fd5ba63 Merge pull request #1272 from th-ch/feat/resolves-1265 2023-10-08 17:19:19 +09:00
73a049a7bc Merge pull request #1279 from th-ch/fix/1274 2023-10-08 17:18:54 +09:00
ef0c30e23a Merge pull request #1280 from th-ch/revert-scale-factor-patch 2023-10-08 17:18:33 +09:00
59ed2326d9 Revert "Fix for windows zoom (ScaleFactor) #1159"
This reverts commit d36fb592d0.
2023-10-08 17:06:30 +09:00
07a02c8c82 Revert "hotfix: fixed app launching offscreen"
This reverts commit ca92031e89.
2023-10-08 17:05:54 +09:00
f1050cb676 remove: remove useless CSS property 2023-10-08 15:35:08 +09:00
7131893f1c chore: update README
Added a guide to install YTM without a network connection.
2023-10-08 15:21:04 +09:00
e4dfb2ff33 feat(discord): remove hacky solution for calling callbacks 2023-10-08 15:01:26 +09:00
187fad6834 Merge branch 'master' into fix/1274 2023-10-08 15:00:30 +09:00
26df435db0 fix: fallback to DOM window controls on platforms without native support 2023-10-08 14:57:58 +09:00
0bee281d1d fix: discord-rpc (#1278) 2023-10-08 14:44:48 +09:00
26de5802a0 fix: prevent multiple RPCs from being registered 2023-10-08 13:55:40 +09:00
c258a4855e Merge pull request #1277 from th-ch/hotfix/1273 2023-10-08 12:56:16 +09:00
b7b6d50ba2 Merge pull request #1276 from jkrei0/master 2023-10-08 12:56:03 +09:00
0376a30fbb fix: prevent name shadowing 2023-10-08 12:39:24 +09:00
ca92031e89 hotfix: fixed app launching offscreen 2023-10-08 12:29:09 +09:00
986d2ad5b1 chore: add window control icons 2023-10-08 12:12:44 +09:00
d9b8d8c48d Fix in-app-menu squishing sub-menu items 2023-10-07 22:50:32 -04:00
0ef34d7c71 feat: use nsis-web instead of nsis 2023-10-08 03:04:53 +09:00
f87607d25d Merge pull request #1271 from th-ch/fix/lastfm 2023-10-08 02:54:13 +09:00
cc0bfae067 fix(last-fm): fix last-fm plugin 2023-10-08 02:41:06 +09:00
e7d2d04f5a fix(test): Add a test to check the title
It failed because of @cliqz/adblocker-electron.
Check see this issue: https://github.com/ghostery/adblocker/issues/3462
2023-10-08 01:16:11 +09:00
f4319ebc6b Update changelog for v2.0.1 2023-10-07 15:57:46 +00:00
28 changed files with 716 additions and 241 deletions

View File

@ -2,7 +2,24 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v2.0.0](https://github.com/th-ch/youtube-music/compare/v1.20.0...v2.0.0)
#### [v2.0.2](https://github.com/th-ch/youtube-music/compare/v2.0.1...v2.0.2)
- 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)
- Merge pull request #1272 from th-ch/feat/resolves-1265 [`6b7fd5b`](https://github.com/th-ch/youtube-music/commit/6b7fd5ba630888de08004105179c059c6d93e028)
- Merge pull request #1279 from th-ch/fix/1274 [`73a049a`](https://github.com/th-ch/youtube-music/commit/73a049a7bc5161f0d53c252cf510f1e2a6f6eeb3)
#### [v2.0.1](https://github.com/th-ch/youtube-music/compare/v2.0.0...v2.0.1)
> 8 October 2023
- Update changelog for v2.0.0 [`2d69dfd`](https://github.com/th-ch/youtube-music/commit/2d69dfd333c3223ecc7de13a0abc98fd99aa3a2b)
- hotfix: hotfix for #1267 [`c002263`](https://github.com/th-ch/youtube-music/commit/c002263c3bdd51890b8ffb431283afb60405d8fe)
- Bump version to 2.0.1 [`a1f025e`](https://github.com/th-ch/youtube-music/commit/a1f025e23c599fe5eb63b32ea38ee81200d232d6)
### [v2.0.0](https://github.com/th-ch/youtube-music/compare/v1.20.0...v2.0.0)
> 7 October 2023
- Bump version to 2.0.0 [`#1257`](https://github.com/th-ch/youtube-music/pull/1257)
- feat(GitHub): add issue template [`#1264`](https://github.com/th-ch/youtube-music/pull/1264)

View File

@ -74,7 +74,7 @@ const defaultConfig = {
'adblocker': {
enabled: true,
cache: true,
blocker: blockers.InPlayer as string,
blocker: blockers.WithBlocklists as string,
additionalBlockLists: [], // Additional list of filters, e.g "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"
disableDefaultLists: false,
},
@ -106,6 +106,7 @@ const defaultConfig = {
activityTimoutEnabled: true, // If enabled, the discord rich presence gets cleared when music paused after the time specified below
activityTimoutTime: 10 * 60 * 1000, // 10 minutes
listenAlong: true, // Add a "listen along" button to rich presence
hideGitHubButton: false, // Disable the "View App On GitHub" button
hideDurationLeft: false, // Hides the start and end time of the song to rich presence
},
'downloader': {

View File

@ -226,15 +226,15 @@ function createMainWindow() {
loadPlugins(win);
if (windowPosition) {
const { x, y } = windowPosition;
const { x: windowX, y: windowY } = windowPosition;
const winSize = win.getSize();
const displaySize
= screen.getDisplayNearestPoint(windowPosition).bounds;
if (
x + winSize[0] < displaySize.x - 8
|| x - winSize[0] > displaySize.x + displaySize.width
|| y < displaySize.y - 8
|| y > displaySize.y + displaySize.height
windowX + winSize[0] < displaySize.x - 8
|| windowX - winSize[0] > displaySize.x + displaySize.width
|| windowY < displaySize.y - 8
|| windowY > displaySize.y + displaySize.height
) {
// Window is offscreen
if (is.dev()) {
@ -243,7 +243,7 @@ function createMainWindow() {
);
}
} else {
win.setPosition(x, y);
win.setPosition(windowX, windowY);
}
}
@ -261,26 +261,6 @@ function createMainWindow() {
win.webContents.loadURL(urlToLoad);
win.on('closed', onClosed);
const scaleFactor = screen.getAllDisplays().length > 1 ? screen.getPrimaryDisplay().scaleFactor : 1;
const size = config.get('window-size');
const position = config.get('window-position');
if (size && size.width && size.height) {
const scaledSize = {
width: size.width / scaleFactor,
height: size.height / scaleFactor,
};
win.setSize(scaledSize.width, scaledSize.height);
}
if (position && position.x && position.y) {
const scaledPosition = {
x: position.x / scaleFactor,
y: position.y / scaleFactor,
};
win.setPosition(scaledPosition.x, scaledPosition.y);
}
type PiPOptions = typeof config.defaultConfig.plugins['picture-in-picture'];
const setPiPOptions = config.plugins.isEnabled('picture-in-picture')
// eslint-disable-next-line @typescript-eslint/no-var-requires

460
package-lock.json generated
View File

@ -1,19 +1,20 @@
{
"name": "youtube-music",
"version": "2.0.1",
"version": "2.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "youtube-music",
"version": "2.0.1",
"version": "2.0.3",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@cliqz/adblocker-electron": "1.26.7",
"@cliqz/adblocker-electron": "1.26.8",
"@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/main": "0.12.0",
"@foobar404/wave": "2.0.4",
"@jellybrick/mpris-service": "2.1.4",
"@xhayper/discord-rpc": "1.0.23",
"async-mutex": "0.4.0",
"butterchurn": "2.6.7",
@ -33,7 +34,6 @@
"html-to-text": "9.0.5",
"keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2",
"mpris-service": "2.1.2",
"node-id3": "0.2.6",
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5",
"vudio": "2.1.1",
@ -46,7 +46,7 @@
"@rollup/plugin-commonjs": "25.0.5",
"@rollup/plugin-image": "3.0.3",
"@rollup/plugin-json": "6.0.1",
"@rollup/plugin-node-resolve": "15.2.2",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.5",
"@rollup/plugin-wasm": "6.2.2",
@ -64,6 +64,7 @@
"eslint-plugin-import": "2.28.1",
"eslint-plugin-prettier": "5.0.0",
"node-gyp": "9.4.0",
"patch-package": "8.0.0",
"playwright": "1.38.1",
"rollup": "4.0.2",
"rollup-plugin-copy": "3.5.0",
@ -279,12 +280,12 @@
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
},
"node_modules/@cliqz/adblocker": {
"version": "1.26.7",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker/-/adblocker-1.26.7.tgz",
"integrity": "sha512-UGjsDoAcmSoG2uGrpZVTWOxAxQJ2Ws9fDxmYrzR0yS/vtxd0t+Q8G8vTLKWkdU7BN53xTTCZMjgCALwdqZhycw==",
"version": "1.26.8",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker/-/adblocker-1.26.8.tgz",
"integrity": "sha512-/dhojt5S1gdcd1D4cGeLIJ9KLpglk9rVFBS0P78ErZUDOUfLfpb1i+n1LQhF8CiWlw4N6r5iscWMiMRBx9K0QQ==",
"dependencies": {
"@cliqz/adblocker-content": "^1.26.7",
"@cliqz/adblocker-extended-selectors": "^1.26.7",
"@cliqz/adblocker-content": "^1.26.8",
"@cliqz/adblocker-extended-selectors": "^1.26.8",
"@remusao/guess-url-type": "^1.2.1",
"@remusao/small": "^1.2.1",
"@remusao/smaz": "^1.9.1",
@ -294,20 +295,20 @@
}
},
"node_modules/@cliqz/adblocker-content": {
"version": "1.26.7",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-content/-/adblocker-content-1.26.7.tgz",
"integrity": "sha512-9nCII7NcidGb16Tf49HV6QFbdDE6ZSTqhLS3vZFImunDRSOHfyCimHOsYEAUURpDi3mnDuK2Kq1yt1m+U4KyKw==",
"version": "1.26.8",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-content/-/adblocker-content-1.26.8.tgz",
"integrity": "sha512-tDg1K0fTdNtQjO+XCg19sONEy26dcndPJ9pXoPtNdw+PO8vhIHy8U9QnIme63dAGd8VGHUtmZ/Vxpw3jtwoZxw==",
"dependencies": {
"@cliqz/adblocker-extended-selectors": "^1.26.7"
"@cliqz/adblocker-extended-selectors": "^1.26.8"
}
},
"node_modules/@cliqz/adblocker-electron": {
"version": "1.26.7",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-electron/-/adblocker-electron-1.26.7.tgz",
"integrity": "sha512-L11NgAGwFI4VnFRq6OrHnvzwVP2r6wyr5uNzj/L+bIAkUjGVqHgcJw4bzF6OdJkeye2trwol2gqONkJKMFlXPA==",
"version": "1.26.8",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-electron/-/adblocker-electron-1.26.8.tgz",
"integrity": "sha512-8vuqiPMCOzGPfUehfvv7eDwQRw3sm3sdR3eZYU2wNGPNbUHf6gZbSBcsx12n3QNcbAxbdydtc/a3X4Eu5VOlxg==",
"dependencies": {
"@cliqz/adblocker": "^1.26.7",
"@cliqz/adblocker-electron-preload": "^1.26.7",
"@cliqz/adblocker": "^1.26.8",
"@cliqz/adblocker-electron-preload": "^1.26.8",
"tldts-experimental": "^6.0.14"
},
"peerDependencies": {
@ -315,20 +316,20 @@
}
},
"node_modules/@cliqz/adblocker-electron-preload": {
"version": "1.26.7",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-electron-preload/-/adblocker-electron-preload-1.26.7.tgz",
"integrity": "sha512-/A9HmfZDxo+4wgQuj80YwrTSzm2tb99K7uJ0ymN4/QUKwrSeNIjEeXztbXakS2QqZE5gDWV0opCkGDpV0FuCcg==",
"version": "1.26.8",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-electron-preload/-/adblocker-electron-preload-1.26.8.tgz",
"integrity": "sha512-sWhND23IrP6f7wUtUt3+lAbNdmMYucwKVsMcMtInAk6vKz5bxpECsp+QBHXE7Z3MXLwa0dATGRVcC4RTLZBb/A==",
"dependencies": {
"@cliqz/adblocker-content": "^1.26.7"
"@cliqz/adblocker-content": "^1.26.8"
},
"peerDependencies": {
"electron": ">11"
}
},
"node_modules/@cliqz/adblocker-extended-selectors": {
"version": "1.26.7",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-extended-selectors/-/adblocker-extended-selectors-1.26.7.tgz",
"integrity": "sha512-J6IYdrWHvTy2U/iikMBB/nz0IbYHYomUgmwpOgDbzaesacrNShzr1evU06ncx7QFexKkUqYMqqt/JjUFnYn5qA=="
"version": "1.26.8",
"resolved": "https://registry.npmjs.org/@cliqz/adblocker-extended-selectors/-/adblocker-extended-selectors-1.26.8.tgz",
"integrity": "sha512-aeOfwr10svwZJROHWhBolAWdKu66oTGiE0GRmoKDbS+ZPVZJRY+N2PZO7spzjjRbYyqKXl08Rgzb3ussJwJlfA=="
},
"node_modules/@develar/schema-utils": {
"version": "2.6.5",
@ -881,6 +882,44 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/@jellybrick/mpris-service": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@jellybrick/mpris-service/-/mpris-service-2.1.4.tgz",
"integrity": "sha512-OwSxYeRRss7+ZhZs/n6D0LjUMWp1QIrAfzBZA6zGs62x80QIQlpeMXO2GKxC6UNyi87wJTiSWsUGDM1jO4eCtQ==",
"dependencies": {
"dbus-next": "^0.10.2",
"deep-equal": "^2.2.2",
"source-map-support": "^0.5.12"
}
},
"node_modules/@jellybrick/mpris-service/node_modules/deep-equal": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz",
"integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==",
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
"call-bind": "^1.0.2",
"es-get-iterator": "^1.1.3",
"get-intrinsic": "^1.2.1",
"is-arguments": "^1.1.1",
"is-array-buffer": "^3.0.2",
"is-date-object": "^1.0.5",
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
"isarray": "^2.0.5",
"object-is": "^1.1.5",
"object-keys": "^1.1.1",
"object.assign": "^4.1.4",
"regexp.prototype.flags": "^1.5.0",
"side-channel": "^1.0.4",
"which-boxed-primitive": "^1.0.2",
"which-collection": "^1.0.1",
"which-typed-array": "^1.1.9"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@ -1216,9 +1255,9 @@
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.2.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.2.tgz",
"integrity": "sha512-f64bU4OKqV0yihtxFemmuf0oj37pToCFMISCA+sJbbIAl5wcpbRO9XgWNWb1tDiWQJUcPxo6V0l59hcuZOQ3kw==",
"version": "15.2.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
"integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@ -1946,6 +1985,12 @@
"node": ">=10.0.0"
}
},
"node_modules/@yarnpkg/lockfile": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
"dev": true
},
"node_modules/7zip-bin": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz",
@ -2252,7 +2297,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
"integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"is-array-buffer": "^3.0.1"
@ -2482,7 +2526,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@ -3391,22 +3434,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"dependencies": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -4282,6 +4309,25 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/es-get-iterator": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
"integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.3",
"has-symbols": "^1.0.3",
"is-arguments": "^1.1.1",
"is-map": "^2.0.2",
"is-set": "^2.0.2",
"is-string": "^1.0.7",
"isarray": "^2.0.5",
"stop-iteration-iterator": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
@ -4939,6 +4985,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/find-yarn-workspace-root": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
"integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
"dev": true,
"dependencies": {
"micromatch": "^4.0.2"
}
},
"node_modules/flat-cache": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
@ -4982,7 +5037,6 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"dependencies": {
"is-callable": "^1.1.3"
}
@ -5448,7 +5502,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
"integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -5757,7 +5810,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
"integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.0",
"has": "^1.0.3",
@ -5792,7 +5844,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
"integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.2.0",
@ -5812,7 +5863,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
"integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
"dev": true,
"dependencies": {
"has-bigints": "^1.0.1"
},
@ -5824,7 +5874,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
"integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
@ -5855,7 +5904,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@ -5970,6 +6018,14 @@
"integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
"devOptional": true
},
"node_modules/is-map": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
@ -6001,7 +6057,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
"integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
"dev": true,
"dependencies": {
"has-tostringtag": "^1.0.0"
},
@ -6086,11 +6141,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-set": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-shared-array-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
"integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2"
},
@ -6114,7 +6176,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
"integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
"dev": true,
"dependencies": {
"has-tostringtag": "^1.0.0"
},
@ -6129,7 +6190,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
"integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
"dev": true,
"dependencies": {
"has-symbols": "^1.0.2"
},
@ -6144,7 +6204,6 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
"integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
"dev": true,
"dependencies": {
"which-typed-array": "^1.1.11"
},
@ -6160,6 +6219,14 @@
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
},
"node_modules/is-weakmap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakref": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
@ -6172,6 +6239,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakset": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
"integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@ -6199,6 +6278,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
},
"node_modules/isbinaryfile": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz",
@ -6307,6 +6391,18 @@
"resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz",
"integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A=="
},
"node_modules/json-stable-stringify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz",
"integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==",
"dev": true,
"dependencies": {
"jsonify": "^0.0.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
@ -6339,6 +6435,15 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
"integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@ -6414,6 +6519,15 @@
"node": ">=0.10.0"
}
},
"node_modules/klaw-sync": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.1.11"
}
},
"node_modules/lazy-val": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
@ -6942,16 +7056,6 @@
"node": ">=10"
}
},
"node_modules/mpris-service": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/mpris-service/-/mpris-service-2.1.2.tgz",
"integrity": "sha512-AC6WepCnFWwOME9OWplHZ8ps/BB+g9QrEpUKCv7wX82fDPzR3nPrypOFmL/Fm0JloEAu6QTWSfDLLc6mM/jinw==",
"dependencies": {
"dbus-next": "^0.9.2",
"deep-equal": "^1.0.1",
"source-map-support": "^0.5.11"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -7142,7 +7246,6 @@
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -7174,7 +7277,6 @@
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
"integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@ -7307,6 +7409,15 @@
"node": ">=0.10.0"
}
},
"node_modules/os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/p-cancelable": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
@ -7428,6 +7539,156 @@
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/patch-package": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
"integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
"dev": true,
"dependencies": {
"@yarnpkg/lockfile": "^1.1.0",
"chalk": "^4.1.2",
"ci-info": "^3.7.0",
"cross-spawn": "^7.0.3",
"find-yarn-workspace-root": "^2.0.0",
"fs-extra": "^9.0.0",
"json-stable-stringify": "^1.0.2",
"klaw-sync": "^6.0.0",
"minimist": "^1.2.6",
"open": "^7.4.2",
"rimraf": "^2.6.3",
"semver": "^7.5.3",
"slash": "^2.0.0",
"tmp": "^0.0.33",
"yaml": "^2.2.2"
},
"bin": {
"patch-package": "index.js"
},
"engines": {
"node": ">=14",
"npm": ">5"
}
},
"node_modules/patch-package/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/patch-package/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/patch-package/node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/patch-package/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/patch-package/node_modules/open": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
"integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
"dev": true,
"dependencies": {
"is-docker": "^2.0.0",
"is-wsl": "^2.1.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/patch-package/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/patch-package/node_modules/slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/patch-package/node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dev": true,
"dependencies": {
"os-tmpdir": "~1.0.2"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/patch-package/node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true,
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -8287,12 +8548,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safe-array-concat/node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -8460,7 +8715,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
@ -8667,6 +8921,17 @@
"node": ">= 6"
}
},
"node_modules/stop-iteration-iterator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
"integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
"dependencies": {
"internal-slot": "^1.0.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/stream-combiner": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
@ -9012,16 +9277,16 @@
}
},
"node_modules/tldts-core": {
"version": "6.0.15",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.0.15.tgz",
"integrity": "sha512-8RuyZ9S3kY2NaSx5zfEiURuyM1kipN6xXp/1guWWKwiSqvb/7dNKKjfvF88H3A3wX1pagaNlewK21kpxZt/DPg=="
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.0.16.tgz",
"integrity": "sha512-/ypKV6FdiDpXnVEUmPy9s5xfoSAyNPvj1r6V/3FqQBWi0ay3asObLX4Hn8eLwkHB0+VEr2bneu3CnX4uT3fO0w=="
},
"node_modules/tldts-experimental": {
"version": "6.0.15",
"resolved": "https://registry.npmjs.org/tldts-experimental/-/tldts-experimental-6.0.15.tgz",
"integrity": "sha512-JiH9jnr9Y+CUjgymKZkdAxIed8KKnsAgoz+zq6JxLb85K92rhJHPbRnA8iDWbqZ7MCAcIFMiTaFWXiYns0CZ9A==",
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/tldts-experimental/-/tldts-experimental-6.0.16.tgz",
"integrity": "sha512-4gmlrxa1Iwt983Xefsp0AJUu/9YdKzd+RU8irW4T5ZHQYNnE2bxTK7BGq1A4mUiSI7aOanXLQojLs1HUKUGjuA==",
"dependencies": {
"tldts-core": "^6.0.15"
"tldts-core": "^6.0.16"
}
},
"node_modules/tmp": {
@ -9447,7 +9712,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
"integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
"dev": true,
"dependencies": {
"is-bigint": "^1.0.1",
"is-boolean-object": "^1.1.0",
@ -9459,11 +9723,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/which-collection": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
"integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
"dependencies": {
"is-map": "^2.0.1",
"is-set": "^2.0.1",
"is-weakmap": "^2.0.1",
"is-weakset": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/which-typed-array": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
"integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
"dev": true,
"dependencies": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@ -9613,6 +9890,15 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yaml": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
"integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==",
"dev": true,
"engines": {
"node": ">= 14"
}
},
"node_modules/yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",

View File

@ -1,7 +1,7 @@
{
"name": "youtube-music",
"productName": "YouTube Music",
"version": "2.0.1",
"version": "2.0.3",
"description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/index.js",
"license": "MIT",
@ -20,21 +20,6 @@
"license",
"!node_modules",
"node_modules/custom-electron-prompt/**",
"node_modules/youtubei.js/**",
"node_modules/undici/**",
"node_modules/@fastify/busboy/**",
"node_modules/jintr/**",
"node_modules/acorn/**",
"node_modules/tslib/**",
"node_modules/semver/**",
"node_modules/lru-cache/**",
"node_modules/detect-libc/**",
"node_modules/color/**",
"node_modules/color-convert/**",
"node_modules/color-string/**",
"node_modules/color-name/**",
"node_modules/simple-swizzle/**",
"node_modules/is-arrayish/**",
"node_modules/@cliqz/adblocker-electron-preload/**",
"node_modules/@cliqz/adblocker-content/**",
"node_modules/@cliqz/adblocker-extended-selectors/**",
@ -59,7 +44,7 @@
"icon": "assets/generated/icons/win/icon.ico",
"target": [
{
"target": "nsis",
"target": "nsis-web",
"arch": [
"x64",
"ia32",
@ -76,7 +61,7 @@
}
]
},
"nsis": {
"nsisWeb": {
"runAfterFinish": false
},
"linux": {
@ -112,14 +97,14 @@
"start": "npm run build && electron ./dist/index.js",
"start:debug": "ELECTRON_ENABLE_LOGGING=1 npm run start",
"generate:package": "node utils/generate-package-json.js",
"postinstall": "npm run plugins && npm run clean",
"postinstall": "patch-package && npm run plugins && npm run clean",
"clean": "del-cli dist && del-cli pack",
"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:mac": "npm run clean && npm run build && electron-builder --mac dmg:x64 -p never",
"dist:mac:arm64": "npm run clean && npm run build && electron-builder --mac dmg:arm64 -p never",
"dist:win": "npm run clean && npm run build && electron-builder --win -p never",
"dist:win:x64": "npm run clean && npm run build && electron-builder --win nsis:x64 -p never",
"dist:win:x64": "npm run clean && npm run build && electron-builder --win nsis-web:x64 -p never",
"lint": "eslint .",
"changelog": "auto-changelog",
"plugins": "npm run plugin:bypass-age-restrictions",
@ -133,10 +118,11 @@
"node": ">=16.0.0"
},
"dependencies": {
"@cliqz/adblocker-electron": "1.26.7",
"@cliqz/adblocker-electron": "1.26.8",
"@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/main": "0.12.0",
"@foobar404/wave": "2.0.4",
"@jellybrick/mpris-service": "2.1.4",
"@xhayper/discord-rpc": "1.0.23",
"async-mutex": "0.4.0",
"butterchurn": "2.6.7",
@ -156,7 +142,6 @@
"html-to-text": "9.0.5",
"keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2",
"mpris-service": "2.1.2",
"node-id3": "0.2.6",
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5",
"vudio": "2.1.1",
@ -168,7 +153,6 @@
"rollup": "4.0.2",
"node-gyp": "9.4.0",
"xml2js": "0.6.2",
"dbus-next": "0.10.2",
"node-fetch": "2.7.0",
"@electron/universal": "1.4.2",
"electron": "27.0.0-beta.9"
@ -178,7 +162,7 @@
"@rollup/plugin-commonjs": "25.0.5",
"@rollup/plugin-image": "3.0.3",
"@rollup/plugin-json": "6.0.1",
"@rollup/plugin-node-resolve": "15.2.2",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.5",
"@rollup/plugin-wasm": "6.2.2",
@ -196,6 +180,7 @@
"eslint-plugin-import": "2.28.1",
"eslint-plugin-prettier": "5.0.0",
"node-gyp": "9.4.0",
"patch-package": "8.0.0",
"playwright": "1.38.1",
"rollup": "4.0.2",
"rollup-plugin-copy": "3.5.0",

View File

@ -0,0 +1,38 @@
diff --git a/node_modules/youtubei.js/bundle/node.cjs b/node_modules/youtubei.js/bundle/node.cjs
index 7e3072e..bf5be6a 100644
--- a/node_modules/youtubei.js/bundle/node.cjs
+++ b/node_modules/youtubei.js/bundle/node.cjs
@@ -16969,7 +16969,13 @@ var _Cache_createCache;
var meta_url = import_meta.url;
var is_cjs = !meta_url;
var __dirname__ = is_cjs ? __dirname : import_path.default.dirname((0, import_url.fileURLToPath)(meta_url));
-var package_json = JSON.parse((0, import_fs.readFileSync)(import_path.default.resolve(__dirname__, is_cjs ? "../package.json" : "../../package.json"), "utf-8"));
+var package_json = {
+ homepage: "https://github.com/LuanRT/YouTube.js#readme",
+ version: "6.4.1",
+ bugs: {
+ url: "https://github.com/LuanRT/YouTube.js/issues"
+ }
+};
var repo_url = (_a3 = package_json.homepage) === null || _a3 === void 0 ? void 0 : _a3.split("#")[0];
var Cache = class {
constructor(persistent = false, persistent_directory) {
diff --git a/node_modules/youtubei.js/dist/src/platform/node.js b/node_modules/youtubei.js/dist/src/platform/node.js
index 0e1b2ca..17b437c 100644
--- a/node_modules/youtubei.js/dist/src/platform/node.js
+++ b/node_modules/youtubei.js/dist/src/platform/node.js
@@ -16,7 +16,13 @@ import evaluate from './jsruntime/jinter.js';
const meta_url = import.meta.url;
const is_cjs = !meta_url;
const __dirname__ = is_cjs ? __dirname : path.dirname(fileURLToPath(meta_url));
-const package_json = JSON.parse(readFileSync(path.resolve(__dirname__, is_cjs ? '../package.json' : '../../package.json'), 'utf-8'));
+const package_json = {
+ homepage: "https://github.com/LuanRT/YouTube.js#readme",
+ version: "6.4.1",
+ bugs: {
+ url: "https://github.com/LuanRT/YouTube.js/issues"
+ }
+};
const repo_url = (_a = package_json.homepage) === null || _a === void 0 ? void 0 : _a.split('#')[0];
class Cache {
constructor(persistent = false, persistent_directory) {

View File

@ -1,10 +1,10 @@
import { app, dialog } from 'electron';
import { app, dialog, ipcMain } from 'electron';
import { Client as DiscordClient } from '@xhayper/discord-rpc';
import { dev } from 'electron-is';
import { SetActivity } from '@xhayper/discord-rpc/dist/structures/ClientUser';
import registerCallback from '../../providers/song-info';
import registerCallback, { type SongInfoCallback, type SongInfo } from '../../providers/song-info';
import type { ConfigType } from '../../config/dynamic';
@ -15,7 +15,7 @@ export interface Info {
rpc: DiscordClient;
ready: boolean;
autoReconnect: boolean;
lastSongInfo?: import('../../providers/song-info').SongInfo;
lastSongInfo?: SongInfo;
}
const info: Info = {
@ -44,31 +44,6 @@ const resetInfo = () => {
}
};
info.rpc.on('connected', () => {
if (dev()) {
console.log('discord connected');
}
for (const cb of refreshCallbacks) {
cb();
}
});
info.rpc.on('ready', () => {
info.ready = true;
if (info.lastSongInfo) {
updateActivity(info.lastSongInfo);
}
});
info.rpc.on('disconnected', () => {
resetInfo();
if (info.autoReconnect) {
connectTimeout();
}
});
const connectTimeout = () => new Promise((resolve, reject) => setTimeout(() => {
if (!info.autoReconnect || info.rpc.isConnected) {
return;
@ -117,7 +92,7 @@ export const connect = (showError = false) => {
};
let clearActivity: NodeJS.Timeout | undefined;
let updateActivity: import('../../providers/song-info').SongInfoCallback;
let updateActivity: SongInfoCallback;
type DiscordOptions = ConfigType<'discord'>;
@ -125,6 +100,31 @@ export default (
win: Electron.BrowserWindow,
options: DiscordOptions,
) => {
info.rpc.on('connected', () => {
if (dev()) {
console.log('discord connected');
}
for (const cb of refreshCallbacks) {
cb();
}
});
info.rpc.on('ready', () => {
info.ready = true;
if (info.lastSongInfo) {
updateActivity(info.lastSongInfo);
}
});
info.rpc.on('disconnected', () => {
resetInfo();
if (info.autoReconnect) {
connectTimeout();
}
});
info.autoReconnect = options.autoReconnect;
window = win;
@ -163,7 +163,7 @@ export default (
largeImageText: songInfo.album ?? '',
buttons: [
...(options.listenAlong ? [{ label: 'Listen Along', url: songInfo.url ?? '' }] : []),
{ label: 'View App On GitHub', url: 'https://github.com/th-ch/youtube-music' },
...(options.hideGitHubButton ? [] : [{ label: 'View App On GitHub', url: 'https://github.com/th-ch/youtube-music' }]),
],
};
@ -188,8 +188,22 @@ export default (
// If the page is ready, register the callback
win.once('ready-to-show', () => {
registerCallback(updateActivity);
let lastSongInfo: SongInfo;
registerCallback((songInfo) => {
lastSongInfo = songInfo;
updateActivity(songInfo);
});
connect();
let lastSent = Date.now();
ipcMain.on('timeChanged', (_, t: number) => {
const currentTime = Date.now();
// if lastSent is more than 5 seconds ago, send the new time
if (currentTime - lastSent > 5000) {
lastSent = currentTime;
lastSongInfo.elapsedSeconds = t;
updateActivity(lastSongInfo);
}
});
});
app.on('window-all-closed', clear);
};

View File

@ -55,6 +55,15 @@ export default (win: Electron.BrowserWindow, options: DiscordOptions, refreshMen
setMenuOptions('discord', options);
},
},
{
label: 'Hide GitHub link Button',
type: 'checkbox',
checked: options.hideGitHubButton,
click(item: Electron.MenuItem) {
options.hideGitHubButton = item.checked;
setMenuOptions('discord', options);
},
},
{
label: 'Hide duration left',
type: 'checkbox',

View File

@ -0,0 +1,3 @@
<svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="m4.21 4.387.083-.094a1 1 0 0 1 1.32-.083l.094.083L12 10.585l6.293-6.292a1 1 0 1 1 1.414 1.414L13.415 12l6.292 6.293a1 1 0 0 1 .083 1.32l-.083.094a1 1 0 0 1-1.32.083l-.094-.083L12 13.415l-6.293 6.292a1 1 0 0 1-1.414-1.414L10.585 12 4.293 5.707a1 1 0 0 1-.083-1.32l.083-.094-.083.094Z"/>
</svg>

After

Width:  |  Height:  |  Size: 392 B

View File

@ -0,0 +1,3 @@
<svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M6 3h12a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3Zm0 2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H6Z"/>
</svg>

After

Width:  |  Height:  |  Size: 252 B

View File

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1,3 @@
<svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M3.755 12.5h16.492a.75.75 0 0 0 0-1.5H3.755a.75.75 0 0 0 0 1.5Z" />
</svg>

After

Width:  |  Height:  |  Size: 174 B

View File

@ -0,0 +1,3 @@
<svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M7.518 5H6.009a3.25 3.25 0 0 1 3.24-3h8.001A4.75 4.75 0 0 1 22 6.75v8a3.25 3.25 0 0 1-3 3.24v-1.508a1.75 1.75 0 0 0 1.5-1.732v-8a3.25 3.25 0 0 0-3.25-3.25h-8A1.75 1.75 0 0 0 7.518 5ZM5.25 6A3.25 3.25 0 0 0 2 9.25v9.5A3.25 3.25 0 0 0 5.25 22h9.5A3.25 3.25 0 0 0 18 18.75v-9.5A3.25 3.25 0 0 0 14.75 6h-9.5ZM3.5 9.25c0-.966.784-1.75 1.75-1.75h9.5c.967 0 1.75.784 1.75 1.75v9.5a1.75 1.75 0 0 1-1.75 1.75h-9.5a1.75 1.75 0 0 1-1.75-1.75v-9.5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@ -1,5 +1,3 @@
import path from 'node:path';
import { register } from 'electron-localshortcut';
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';
@ -25,7 +23,7 @@ export default (win: BrowserWindow) => {
(key: string, value: unknown) => (key !== 'commandsMap' && key !== 'menu') ? value : undefined),
),
);
const getMenuItemById = (commandId: number): MenuItem | null => {
const menu = Menu.getApplicationMenu();
@ -40,7 +38,7 @@ export default (win: BrowserWindow) => {
break;
}
}
return target;
};
@ -57,4 +55,11 @@ export default (win: BrowserWindow) => {
(key: string, value: unknown) => (key !== 'commandsMap' && key !== 'menu') ? value : undefined),
);
});
ipcMain.handle('window-is-maximized', () => win.isMaximized());
ipcMain.handle('window-close', () => win.close());
ipcMain.handle('window-minimize', () => win.minimize());
ipcMain.handle('window-maximize', () => win.maximize());
ipcMain.handle('window-unmaximize', () => win.unmaximize());
};

View File

@ -2,7 +2,12 @@ import { ipcRenderer, Menu } from 'electron';
import { createPanel } from './menu/panel';
import logo from '../../assets/menu.svg';
import logo from './assets/menu.svg';
import close from './assets/close.svg';
import minimize from './assets/minimize.svg';
import maximize from './assets/maximize.svg';
import unmaximize from './assets/unmaximize.svg';
import { isEnabled } from '../../config/plugins';
import config from '../../config';
@ -11,8 +16,9 @@ function $<E extends Element = Element>(selector: string) {
}
const isMacOS = navigator.userAgent.includes('Macintosh');
const isNotWindowsOrMacOS = !navigator.userAgent.includes('Windows') && !isMacOS;
export default () => {
export default async () => {
let hideMenu = config.get('options.hideMenu');
const titleBar = document.createElement('title-bar');
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
@ -39,6 +45,60 @@ export default () => {
if (!isMacOS) titleBar.appendChild(logo);
document.body.appendChild(titleBar);
titleBar.appendChild(logo);
const addWindowControls = async () => {
// Create window control buttons
const minimizeButton = document.createElement('button');
minimizeButton.classList.add('window-control');
minimizeButton.appendChild(minimize);
minimizeButton.onclick = () => ipcRenderer.invoke('window-minimize');
const maximizeButton = document.createElement('button');
if (await ipcRenderer.invoke('window-is-maximized')) {
maximizeButton.classList.add('window-control');
maximizeButton.appendChild(unmaximize);
} else {
maximizeButton.classList.add('window-control');
maximizeButton.appendChild(maximize);
}
maximizeButton.onclick = async () => {
if (await ipcRenderer.invoke('window-is-maximized')) {
// change icon to maximize
maximizeButton.removeChild(maximizeButton.firstChild!);
maximizeButton.appendChild(maximize);
// call unmaximize
await ipcRenderer.invoke('window-unmaximize');
} else {
// change icon to unmaximize
maximizeButton.removeChild(maximizeButton.firstChild!);
maximizeButton.appendChild(unmaximize);
// call maximize
await ipcRenderer.invoke('window-maximize');
}
};
const closeButton = document.createElement('button');
closeButton.classList.add('window-control');
closeButton.appendChild(close);
closeButton.onclick = () => ipcRenderer.invoke('window-close');
// Create a container div for the window control buttons
const windowControlsContainer = document.createElement('div');
windowControlsContainer.classList.add('window-controls-container');
windowControlsContainer.appendChild(minimizeButton);
windowControlsContainer.appendChild(maximizeButton);
windowControlsContainer.appendChild(closeButton);
// Add window control buttons to the title bar
titleBar.appendChild(windowControlsContainer);
};
if (isNotWindowsOrMacOS) await addWindowControls();
if (navBar) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(() => {
@ -69,8 +129,9 @@ export default () => {
menu.style.visibility = 'hidden';
}
});
if (isNotWindowsOrMacOS) await addWindowControls();
};
updateMenu();
await updateMenu();
document.title = 'Youtube Music';

View File

@ -101,6 +101,15 @@ export const createPanel = (
}
panel.setAttribute('open', 'true');
// Children are placed below their parent item, which can cause
// long lists to squeeze their children at the bottom of the screen
// (This needs to be done *after* setAttribute)
panel.classList.remove('position-by-bottom');
if (options.placement === 'right' && panel.scrollHeight > panel.clientHeight ) {
panel.style.setProperty('--y', `${rect.y + rect.height}px`);
panel.classList.add('position-by-bottom');
}
};
anchor.addEventListener('click', () => {

View File

@ -80,6 +80,11 @@ menu-panel[open="true"] {
opacity: 1;
transform: scale(1);
}
menu-panel.position-by-bottom {
top: unset;
bottom: calc(100vh - var(--y, 100%));
max-height: calc(var(--y, 0) - var(--menu-bar-height, 36px) - 16px);
}
menu-item {
-webkit-app-region: none;
@ -121,6 +126,33 @@ menu-separator {
margin-left: -4px;
}
/* Window control container */
.window-controls-container {
-webkit-app-region: no-drag;
display: flex;
justify-content: flex-end; /* Align to the right end of the title-bar */
align-items: center;
gap: 4px; /* Add spacing between the window control buttons */
position: absolute; /* Position it absolutely within title-bar */
right: 4px; /* Adjust the right position as needed */
}
/* Window control buttons */
.window-control {
width: 24px;
height: 24px;
background: none;
border: none;
cursor: pointer;
display: flex;
align-items: center;
color: #f1f1f1;
font-size: 14px;
padding: 0;
}
/* youtube-music style */
ytmusic-app-layout {

View File

@ -15,11 +15,24 @@ interface LastFmData {
timestamp?: number,
}
const createFormData = (parameters: Record<string, unknown>) => {
interface LastFmSongData {
track?: string,
duration?: number,
artist?: string,
album?: string,
api_key: string,
sk?: string,
format: string,
method: string,
timestamp?: number,
api_sig?: string,
}
const createFormData = (parameters: LastFmSongData) => {
// Creates the body for in the post request
const formData = new URLSearchParams();
for (const key in parameters) {
formData.append(key, String(parameters[key]));
formData.append(key, String(parameters[key as keyof LastFmSongData]));
}
return formData;
@ -36,7 +49,7 @@ const createQueryString = (parameters: Record<string, unknown>, apiSignature: st
return '?' + queryData.join('&');
};
const createApiSig = (parameters: Record<string, unknown>, secret: string) => {
const createApiSig = (parameters: LastFmSongData, secret: string) => {
// This function creates the api signature, see: https://www.last.fm/api/authspec
const keys = Object.keys(parameters);
@ -47,7 +60,7 @@ const createApiSig = (parameters: Record<string, unknown>, secret: string) => {
continue;
}
sig += `${key}${String(parameters[key])}`;
sig += `${key}${parameters[key as keyof LastFmSongData]}`;
}
sig += secret;
@ -59,7 +72,7 @@ const createToken = async ({ api_key: apiKey, api_root: apiRoot, secret }: LastF
// Creates and stores the auth token
const data = {
method: 'auth.gettoken',
apiKey,
api_key: apiKey,
format: 'json',
};
const apiSigature = createApiSig(data, secret);
@ -68,10 +81,9 @@ const createToken = async ({ api_key: apiKey, api_root: apiRoot, secret }: LastF
return json?.token;
};
const authenticateAndGetToken = async (config: LastFMOptions) => {
const authenticate = async (config: LastFMOptions) => {
// Asks the user for authentication
await shell.openExternal(`https://www.last.fm/api/auth/?api_key=${config.api_key}&token=${config.token}`);
return await createToken(config);
};
const getAndSetSessionKey = async (config: LastFMOptions) => {
@ -86,18 +98,19 @@ const getAndSetSessionKey = async (config: LastFMOptions) => {
const response = await net.fetch(`${config.api_root}${createQueryString(data, apiSignature)}`);
const json = await response.json() as {
error?: string,
session?: {
key: string,
}
session?: {
key: string,
}
};
if (json.error) {
config.token = await authenticateAndGetToken(config);
config.token = await createToken(config);
await authenticate(config);
setOptions('last-fm', config);
}
if (json.session) {
config.session_key = json?.session?.key;
setOptions('last-fm', config);
config.session_key = json.session.key;
}
setOptions('last-fm', config);
return config;
};
@ -107,20 +120,20 @@ const postSongDataToAPI = async (songInfo: SongInfo, config: LastFMOptions, data
await getAndSetSessionKey(config);
}
const postData = {
const postData: LastFmSongData = {
track: songInfo.title,
duration: songInfo.songDuration,
artist: songInfo.artist,
...(songInfo.album ? { album: songInfo.album } : undefined), // Will be undefined if current song is a video
api_key: config.api_key,
api_sig: '',
sk: config.session_key,
format: 'json',
...data,
};
postData.api_sig = createApiSig(postData, config.secret);
net.fetch('https://ws.audioscrobbler.com/2.0/', { method: 'POST', body: createFormData(postData) })
const formData = createFormData(postData);
net.fetch('https://ws.audioscrobbler.com/2.0/', { method: 'POST', body: formData })
.catch(async (error: {
response?: {
data?: {
@ -131,7 +144,8 @@ const postSongDataToAPI = async (songInfo: SongInfo, config: LastFMOptions, data
if (error?.response?.data?.error === 9) {
// Session key is invalid, so remove it from the config and reauthenticate
config.session_key = undefined;
config.token = await authenticateAndGetToken(config);
config.token = await createToken(config);
await authenticate(config);
setOptions('last-fm', config);
}
});

View File

@ -2,14 +2,14 @@ import path from 'node:path';
import { app, BrowserWindow, ipcMain, Notification } from 'electron';
import { icons, notificationImage, saveTempIcon, secondsToMinutes, ToastStyles } from './utils';
import { notificationImage, secondsToMinutes, ToastStyles } from './utils';
import config from './config';
import getSongControls from '../../providers/song-controls';
import registerCallback, { SongInfo } from '../../providers/song-info';
import { changeProtocolHandler } from '../../providers/protocol-handler';
import { setTrayOnClick, setTrayOnDoubleClick } from '../../tray';
import { getMediaIconLocation } from '../utils';
import { getMediaIconLocation, mediaIcons, saveMediaIcon } from '../utils';
let songControls: ReturnType<typeof getSongControls>;
let savedNotification: Notification | undefined;
@ -23,7 +23,7 @@ export default (win: BrowserWindow) => {
ipcMain.on('timeChanged', (_, t: number) => currentSeconds = t);
if (app.isPackaged) {
saveTempIcon();
saveMediaIcon();
}
let savedSongInfo: SongInfo;
@ -152,9 +152,9 @@ const getXml = (songInfo: SongInfo, iconSrc: string) => {
}
}
};
const display = (kind: keyof typeof icons) => {
const display = (kind: keyof typeof mediaIcons) => {
if (config.get('toastStyle') === ToastStyles.legacy) {
return `content="${icons[kind]}"`;
return `content="${mediaIcons[kind]}"`;
}
return `\
@ -163,7 +163,7 @@ const display = (kind: keyof typeof icons) => {
`;
};
const getButton = (kind: keyof typeof icons) =>
const getButton = (kind: keyof typeof mediaIcons) =>
`<action ${display(kind)} activationType="protocol" arguments="youtubemusic://${kind}"/>`;
const getButtons = (isPaused: boolean) => `\

View File

@ -9,7 +9,7 @@ import { cache } from '../../providers/decorators';
import { SongInfo } from '../../providers/song-info';
import { getAssetsDirectoryLocation } from '../utils';
const icon = 'assets/youtube-music.png';
const defaultIcon = path.join(getAssetsDirectoryLocation(), 'youtube-music.png');
const userData = app.getPath('userData');
const temporaryIcon = path.join(userData, 'tempIcon.png');
const temporaryBanner = path.join(userData, 'tempBanner.png');
@ -25,13 +25,6 @@ export const ToastStyles = {
legacy: 7,
};
export const icons = {
play: '\u{1405}', // ᐅ
pause: '\u{2016}', // ‖
next: '\u{1433}', //
previous: '\u{1438}', //
};
export const urgencyLevels = [
{ name: 'Low', value: 'low' },
{ name: 'Normal', value: 'normal' },
@ -52,7 +45,7 @@ const nativeImageToLogo = cache((nativeImage: NativeImage) => {
export const notificationImage = (songInfo: SongInfo) => {
if (!songInfo.image) {
return icon;
return defaultIcon;
}
if (!config.get('interactive')) {
@ -76,25 +69,12 @@ export const saveImage = cache((img: NativeImage, savePath: string) => {
fs.writeFileSync(savePath, img.toPNG());
} catch (error: unknown) {
console.log(`Error writing song icon to disk:\n${String(error)}`);
return icon;
return defaultIcon;
}
return savePath;
});
export const saveTempIcon = () => {
for (const kind of Object.keys(icons)) {
const destinationPath = path.join(userData, 'icons', `${kind}.png`);
if (fs.existsSync(destinationPath)) {
continue;
}
const iconPath = path.resolve(path.resolve(getAssetsDirectoryLocation(), 'media-icons-black'), `${kind}.png`);
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
fs.copyFile(iconPath, destinationPath, () => {});
}
};
export const snakeToCamel = (string_: string) => string_.replaceAll(/([-_][a-z]|^[a-z])/g, (group) =>
group.toUpperCase()
.replace('-', ' ')

View File

@ -1,4 +1,4 @@
declare module 'mpris-service' {
declare module '@jellybrick/mpris-service' {
import { EventEmitter } from 'events';
import dbus from 'dbus-next';
@ -75,6 +75,8 @@ declare module 'mpris-service' {
objectPath(subpath?: string): string;
getPosition(): number;
seeked(position: number): void;
getTrackIndex(trackId: string): number;

View File

@ -1,6 +1,6 @@
import { BrowserWindow, ipcMain } from 'electron';
import mpris, { Track } from 'mpris-service';
import mpris, { Track } from '@jellybrick/mpris-service';
import registerCallback from '../../providers/song-info';
import getSongControls from '../../providers/song-controls';
@ -50,18 +50,13 @@ function registerMPRIS(win: BrowserWindow) {
player.loopStatus = mpris.LOOP_STATUS_NONE;
break;
}
case 'ONE': {
player.loopStatus = mpris.LOOP_STATUS_PLAYLIST;
break;
}
case 'ALL': {
{
player.loopStatus = mpris.LOOP_STATUS_TRACK;
// No default
}
player.loopStatus = mpris.LOOP_STATUS_TRACK;
// No default
break;
}
}
@ -76,6 +71,7 @@ function registerMPRIS(win: BrowserWindow) {
const delta = (targetIndex - currentIndex + 3) % 3;
songControls.switchRepeat(delta);
});
player.getPosition = () => secToMicro(currentSeconds);
player.on('raise', () => {
win.setSkipTaskbar(false);
@ -110,7 +106,7 @@ function registerMPRIS(win: BrowserWindow) {
shuffle();
}
});
player.on('open', (args: { uri: string}) => { win.loadURL(args.uri); });
player.on('open', (args: { uri: string }) => { win.loadURL(args.uri); });
let mprisVolNewer = false;
let autoUpdate = false;

View File

@ -1,16 +1,20 @@
import path from 'node:path';
import { BrowserWindow, nativeImage } from 'electron';
import { app, BrowserWindow, nativeImage } from 'electron';
import getSongControls from '../../providers/song-controls';
import registerCallback, { SongInfo } from '../../providers/song-info';
import { getMediaIconLocation } from '../utils';
import { getMediaIconLocation, saveMediaIcon } from '../utils';
export default (win: BrowserWindow) => {
let currentSongInfo: SongInfo;
const { playPause, next, previous } = getSongControls(win);
if (app.isPackaged) {
saveMediaIcon();
}
const setThumbar = (win: BrowserWindow, songInfo: SongInfo) => {
// Wait for song to start before setting thumbar
if (!songInfo?.title) {

View File

@ -2,7 +2,6 @@ import fs from 'node:fs';
import path from 'node:path';
import { app, ipcMain, ipcRenderer } from 'electron';
import is from 'electron-is';
import { ValueOf } from '../utils/type-utils';
@ -15,6 +14,26 @@ export const getMediaIconLocation = () =>
? path.resolve(app.getPath('userData'), 'icons')
: path.resolve(getAssetsDirectoryLocation(), 'media-icons-black');
export const mediaIcons = {
play: '\u{1405}', // ᐅ
pause: '\u{2016}', // ‖
next: '\u{1433}', //
previous: '\u{1438}', //
} as const;
export const saveMediaIcon = () => {
for (const kind of Object.keys(mediaIcons)) {
const destinationPath = path.join(getMediaIconLocation(), `${kind}.png`);
if (fs.existsSync(destinationPath)) {
continue;
}
const iconPath = path.resolve(path.resolve(getAssetsDirectoryLocation(), 'media-icons-black'), `${kind}.png`);
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
fs.copyFile(iconPath, destinationPath, () => {});
}
};
// Creates a DOM element from an HTML string
export const ElementFromHtml = (html: string): HTMLElement => {
const template = document.createElement('template');

View File

@ -62,8 +62,8 @@ export const setupRepeatChangedListener = singleton(() => {
ipcRenderer.send(
'repeatChanged',
$<HTMLElement & {
GetState: () => GetState;
}>('ytmusic-player-bar')?.GetState().queue.repeatMode,
getState: () => GetState;
}>('ytmusic-player-bar')?.getState().queue.repeatMode,
);
});

View File

@ -65,6 +65,16 @@ file).*
winget install th-ch.YouTubeMusic
```
#### How to install without a network connection? (in Windows)
- Download the `*.nsis.7z` file for _your device architecture_ in [release page](https://github.com/th-ch/youtube-music/releases/latest).
- `x64` for 64-bit Windows
- `ia32` for 32-bit Windows
- `arm64` for ARM64 Windows
- Download installer in release page. (`*-Setup.exe`)
- Place them in the **same directory**.
- Run the installer.
## Available plugins:
- **Ad Blocker**: Block all ads and tracking out of the box
@ -94,8 +104,7 @@ winget install th-ch.YouTubeMusic
slider [exponential](https://greasyfork.org/en/scripts/397686-youtube-music-fix-volume-ratio/) so it's easier to
select lower volumes.
- **In-App Menu
**: [gives bars a fancy, dark look](https://user-images.githubusercontent.com/78568641/112215894-923dbf00-8c29-11eb-95c3-3ce15db27eca.png)
- **In-App Menu**: [gives bars a fancy, dark look](https://user-images.githubusercontent.com/78568641/112215894-923dbf00-8c29-11eb-95c3-3ce15db27eca.png)
> (see [this post](https://github.com/th-ch/youtube-music/issues/410#issuecomment-952060709) if you have problem
accessing the menu after enabling this plugin and hide-menu option)

View File

@ -59,7 +59,6 @@ export default defineConfig({
external: [
'electron',
'custom-electron-prompt',
'youtubei.js', // https://github.com/LuanRT/YouTube.js/pull/509
...builtinModules,
],
});

View File

@ -30,6 +30,9 @@ test('YouTube Music App - With default settings, app is launched and visible', a
await consentForm.click('button');
}
const title = await window.title();
expect(title.replaceAll(/\s/g, ' ')).toEqual('YouTube Music');
const url = window.url();
expect(url.startsWith('https://music.youtube.com')).toBe(true);