Compare commits

...

37 Commits

Author SHA1 Message Date
d67f62c0dc Merge branch 'master' of github.com:Mastermindzh/tidal-hifi into develop 2024-10-27 23:06:20 +01:00
ae699887b2
Merge pull request #494 from Mastermindzh/develop
Develop
2024-10-27 21:51:50 +01:00
b2afd44dd6 prettier :) 2024-10-27 21:31:31 +01:00
a8c635932f Merge branch 'develop' of github.com:Mastermindzh/tidal-hifi into develop 2024-10-27 21:27:38 +01:00
66d0d004bf discord listening to 2024-10-27 21:27:23 +01:00
d1e0321058
Merge pull request #488 from 3top1a/master
"Listening to" Discord RPC
2024-10-27 21:24:08 +01:00
2ab5a556ab Removed Songwhip (they shut down) and replaced it with TIDAL's universal link system 2024-10-27 21:01:37 +01:00
baf719fc60 Merge branch 'develop' of github.com:Mastermindzh/tidal-hifi into develop 2024-10-27 20:24:38 +01:00
15c8f6a418 prepping 5.17 2024-10-27 20:24:32 +01:00
f73521e2e5
Merge pull request #491 from darkiox/master
feat: Added ability to keep a static window title.
2024-10-27 20:20:23 +01:00
3top1a
0f5e00c4df Revert "Removed redundant RPC settings" 2024-10-23 19:14:36 +02:00
Darío Marmie
974877ea4f staticWindowTitle(fix): Order alphabetically on all appearances, change if to a ternary 2024-10-23 09:39:13 -03:00
3top1a
6be5774001 Added RPC connection retrying 2024-10-22 22:29:56 +02:00
3top1a
f96cf2e8da Removed redundant RPC settings 2024-10-22 22:27:29 +02:00
3top1a
f832bd2712 replaced discord rpc library 2024-10-22 17:20:06 +02:00
Darío Marmie
cb8667bd41 feat: Added ability to keep a static window title. 2024-10-21 18:28:03 -03:00
91156e5936
Merge pull request #490 from Mastermindzh/snyk-upgrade-aca3a223a3167341470045eeca25ec6e
[Snyk] Upgrade sass from 1.78.0 to 1.79.4
2024-10-21 09:12:32 +02:00
snyk-bot
57e9a2dcb8
fix: upgrade sass from 1.78.0 to 1.79.4
Snyk has created this PR to upgrade sass from 1.78.0 to 1.79.4.

See this package in npm:
https://www.npmjs.com/package/sass

See this project in Snyk:
https://app.snyk.io/org/mastermindzh/project/dade8f03-2064-49a3-8957-edbacec3887c?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-10-19 06:05:50 +00:00
3top1a
21edcd6ad5 reverted more ugly/unnecessary changes 2024-10-17 20:51:23 +02:00
3top1a
3dc42eceb0 Reverted bad changes 2024-10-17 20:47:40 +02:00
3top1a
b1830f5684 Fixed impl 2024-10-17 20:16:22 +02:00
3top1a
1a0c15e17f Maybe? Can't build 2024-10-16 20:37:47 +02:00
6e59d59a1d
Merge pull request #484 from Mastermindzh/snyk-fix-a313b5a6ce537f9549a8ae3a45f1033f
[Snyk] Security upgrade express from 4.21.0 to 4.21.1
2024-10-16 09:51:56 +02:00
snyk-bot
fab7497311
fix: package.json & package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-COOKIE-8163060
2024-10-12 06:26:33 +00:00
fccbcc77ea
Merge pull request #478 from Mastermindzh/snyk-upgrade-28ef48ed574610f887d2ae8544baaf7d
[Snyk] Upgrade axios from 1.7.5 to 1.7.7
2024-09-26 11:09:22 +02:00
snyk-bot
041c19fb52
fix: upgrade axios from 1.7.5 to 1.7.7
Snyk has created this PR to upgrade axios from 1.7.5 to 1.7.7.

See this package in npm:
axios

See this project in Snyk:
https://app.snyk.io/org/mastermindzh/project/dade8f03-2064-49a3-8957-edbacec3887c?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-09-26 08:54:14 +00:00
e36c562afa
Merge pull request #472 from Mastermindzh/snyk-upgrade-6ce11eb8281ab57e87d8cb068d3cf64d
[Snyk] Upgrade axios from 1.7.4 to 1.7.5
2024-09-25 16:36:18 +02:00
1589aa5251
Merge pull request #473 from Mastermindzh/dependabot/npm_and_yarn/multi-cf87d80143
chore(deps): bump send and express
2024-09-25 16:35:39 +02:00
8c672dd1eb
Merge pull request #476 from Mastermindzh/snyk-upgrade-3215721a01f05f1d0592d13bf3fb865f
[Snyk] Upgrade sass from 1.77.8 to 1.78.0
2024-09-25 16:34:49 +02:00
snyk-bot
3769550f24
fix: upgrade sass from 1.77.8 to 1.78.0
Snyk has created this PR to upgrade sass from 1.77.8 to 1.78.0.

See this package in npm:
sass

See this project in Snyk:
https://app.snyk.io/org/mastermindzh/project/dade8f03-2064-49a3-8957-edbacec3887c?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-09-25 09:23:53 +00:00
dependabot[bot]
5a06b6c53f
chore(deps): bump send and express
Bumps [send](https://github.com/pillarjs/send) to 0.19.0 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.


Updates `send` from 0.18.0 to 0.19.0
- [Release notes](https://github.com/pillarjs/send/releases)
- [Changelog](https://github.com/pillarjs/send/blob/master/HISTORY.md)
- [Commits](https://github.com/pillarjs/send/compare/0.18.0...0.19.0)

Updates `express` from 4.20.0 to 4.21.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.0)

---
updated-dependencies:
- dependency-name: send
  dependency-type: indirect
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 08:12:34 +00:00
snyk-bot
1d4ef66d27
fix: upgrade axios from 1.7.4 to 1.7.5
Snyk has created this PR to upgrade axios from 1.7.4 to 1.7.5.

See this package in npm:
axios

See this project in Snyk:
https://app.snyk.io/org/mastermindzh/project/dade8f03-2064-49a3-8957-edbacec3887c?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-09-14 08:18:21 +00:00
e02f07401b
Merge pull request #459 from Mastermindzh/snyk-fix-0bde81baefef700af2dc83cf83bf83d4
[Snyk] Security upgrade axios from 1.7.2 to 1.7.4
2024-09-10 15:27:26 +02:00
118f92e75a
Merge pull request #469 from Mastermindzh/dependabot/npm_and_yarn/multi-ceff1a497b
chore(deps): bump path-to-regexp and express
2024-09-10 15:05:56 +02:00
dependabot[bot]
788d302ce8
chore(deps): bump path-to-regexp and express
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) to 0.1.10 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.


Updates `path-to-regexp` from 0.1.7 to 0.1.10
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v0.1.7...v0.1.10)

Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 10:21:44 +00:00
snyk-bot
cc09f35b49
fix: package.json & package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-7361793
2024-08-14 22:44:20 +00:00
ef13933c66
Merge pull request #456 from Mastermindzh/develop
Develop
2024-08-10 14:49:42 +02:00
22 changed files with 929 additions and 796 deletions

View File

@ -1,13 +1,7 @@
{
"plugins": [
"stylelint-prettier"
],
"extends": [
"stylelint-config-standard-scss"
],
"ignoreFiles": [
"src/themes/**.scss"
],
"plugins": ["stylelint-prettier"],
"extends": ["stylelint-config-standard-scss"],
"ignoreFiles": ["src/themes/**.scss"],
"rules": {
"prettier/prettier": true,
"scss/at-extend-no-missing-placeholder": null,

View File

@ -14,7 +14,6 @@
"rescrobbler",
"scrobble",
"scrobbling",
"Songwhip",
"trackid",
"tracklist",
"widevine",

View File

@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [5.17.0]
- Added an option to disable the dynamic title and set it to a static one, [#491](https://github.com/Mastermindzh/tidal-hifi/pull/491)
- Discord integration now says "Listening to" instead of "playing" [#488](https://github.com/Mastermindzh/tidal-hifi/pull/488) && [#454](https://github.com/Mastermindzh/tidal-hifi/pull/454)
- Fixed several element names in the dom scraper
- Removed the Songwhip (they shut down) integration and replaced it with TIDAL's universal link system
## [5.16.0]
- Fix issue #449 Discord RPC stuck on "Browsing Tidal".
@ -14,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added all missing swagger/openApi info with the help of [Times-Z](https://github.com/Times-Z)
- Updated most dependency versions
- This includes Electron 31!
- Added a channel selector so we can now use Tidal's staging environment directly from the app
@ -105,10 +113,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated Electron to 28.1.1 (fixes [325](https://github.com/Mastermindzh/tidal-hifi/issues/325))
- Updated dependencies to latest
- added theme files to stylelint ignore
- fixed other stylelint errors
- Added functionality to favorite a song (fixes [#323](https://github.com/Mastermindzh/tidal-hifi/issues/323))
- Added a hotkey to favorite ("Add to collection") songs: Control+a
- Added the "favorite" field in the `mediaInfo` and the API `/current` endpoint
- Added an endpoint to toggle favoriting a song: `http://localhost:47836/favorite/toggle`

View File

@ -48,7 +48,6 @@ The web version of [listen.tidal.com](https://listen.tidal.com) running in elect
- AlbumArt in integrations ([best-effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847))
- Custom [integrations](#integrations)
- [ListenBrainz](https://listenbrainz.org/?redirect=false) integration
- Songwhip.com integration (hotkey `ctrl + w`)
- Discord RPC integration (showing "now listening", "Browsing", etc)
- Flatpak version only works if both Discord and Tidal-HiFi are flatpaks
- MPRIS integration

View File

@ -8,4 +8,3 @@ Only the very latest 😄.
If you find a vulnerability just add it as an issue.
If there's an especially bad vulnerability that you don't want to make public just send me a private message (email, discord, wherever).

431
package-lock.json generated
View File

@ -1,31 +1,30 @@
{
"name": "tidal-hifi",
"version": "5.16.0",
"version": "5.17.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tidal-hifi",
"version": "5.16.0",
"version": "5.17.0",
"license": "MIT",
"dependencies": {
"@electron/remote": "^2.1.2",
"@types/swagger-jsdoc": "^6.0.4",
"axios": "^1.7.2",
"@xhayper/discord-rpc": "^1.2.0",
"axios": "^1.7.7",
"cors": "^2.8.5",
"discord-rpc": "^4.0.1",
"electron-store": "^8.2.0",
"express": "^4.19.2",
"express": "^4.21.1",
"hotkeys-js": "^3.13.7",
"mpris-service": "^2.1.2",
"request": "^2.88.2",
"sass": "^1.77.8",
"sass": "^1.79.4",
"swagger-ui-express": "^5.0.1"
},
"devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0",
"@types/cors": "^2.8.17",
"@types/discord-rpc": "^4.0.8",
"@types/express": "^4.17.21",
"@types/node": "^20.14.10",
"@types/request": "^2.48.12",
@ -402,6 +401,59 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.0.tgz",
"integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@discordjs/util": "^1.1.1",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "0.37.97",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"node_modules/@discordjs/util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@dual-bundle/import-meta-resolve": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
@ -1115,6 +1167,26 @@
"node": ">=14"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.3.tgz",
"integrity": "sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
@ -1230,21 +1302,6 @@
"@types/ms": "*"
}
},
"node_modules/@types/discord-rpc": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-4.0.8.tgz",
"integrity": "sha512-1tZf217Natkj+TziNXRRLwNmdm5GNa1bnrQr8VWowquo/Su5hMjdhobj8URxW1COMk2da28XCU1ahsYCAlxirA==",
"dev": true,
"dependencies": {
"@types/events": "*"
}
},
"node_modules/@types/events": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
"dev": true
},
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
@ -1818,6 +1875,52 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@xhayper/discord-rpc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xhayper/discord-rpc/-/discord-rpc-1.2.0.tgz",
"integrity": "sha512-cKjs9TKzN/7JoozijjszQjUEK1qnLHpEvcJQ2OGFBZjymUzIOH7l14KUu7TQtaIEk0Aw9Bx2w7TfQ0O6tp5mCw==",
"license": "ISC",
"dependencies": {
"@discordjs/rest": "^2.3.0",
"@vladfrangu/async_event_emitter": "^2.4.4",
"discord-api-types": "^0.37.93",
"ws": "^8.18.0"
},
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@xhayper/discord-rpc/node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.10",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
@ -2024,6 +2127,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@ -2260,9 +2364,9 @@
"integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
},
"node_modules/axios": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@ -2322,6 +2426,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -2353,9 +2458,9 @@
}
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
@ -2365,7 +2470,7 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
@ -2419,6 +2524,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
"fill-range": "^7.1.1"
},
@ -2430,6 +2536,7 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@ -2657,6 +2764,7 @@
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
@ -2683,6 +2791,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@ -2970,9 +3079,10 @@
}
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
@ -3379,17 +3489,11 @@
"node": ">=8"
}
},
"node_modules/discord-rpc": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-4.0.1.tgz",
"integrity": "sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==",
"dependencies": {
"node-fetch": "^2.6.1",
"ws": "^7.3.1"
},
"optionalDependencies": {
"register-scheme": "github:devsnek/node-register-scheme"
}
"node_modules/discord-api-types": {
"version": "0.37.103",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.103.tgz",
"integrity": "sha512-r+qitxXKe2l6KFw5odPdZSSqdEou+7eNC7BfbZ7mny5Me/K06wCTeKUMVeH/YsI9+4QQudskeQ307kr/7ppQ1A==",
"license": "MIT"
},
"node_modules/dmg-builder": {
"version": "24.9.4",
@ -4063,36 +4167,37 @@
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@ -4111,6 +4216,14 @@
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -4294,12 +4407,12 @@
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
@ -4318,6 +4431,14 @@
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -4497,6 +4618,7 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@ -5216,6 +5338,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@ -5269,6 +5392,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -5286,6 +5410,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@ -5756,6 +5881,12 @@
"node": ">=10"
}
},
"node_modules/magic-bytes.js": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==",
"license": "MIT"
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -5859,9 +5990,12 @@
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge2": {
"version": "1.4.1",
@ -6119,6 +6253,7 @@
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
"dev": true,
"optional": true
},
"node_modules/node-cleanup": {
@ -6127,25 +6262,6 @@
"integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==",
"dev": true
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/nodemon": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz",
@ -6271,6 +6387,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -6303,9 +6420,12 @@
}
},
"node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -6565,9 +6685,9 @@
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
},
"node_modules/path-type": {
"version": "4.0.0",
@ -6607,6 +6727,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
@ -6935,11 +7056,11 @@
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
"side-channel": "^1.0.4"
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@ -7087,6 +7208,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@ -7110,17 +7232,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/register-scheme": {
"version": "0.0.2",
"resolved": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"bindings": "^1.3.0",
"node-addon-api": "^1.3.0"
}
},
"node_modules/remarkable": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz",
@ -7374,12 +7485,12 @@
}
},
"node_modules/sass": {
"version": "1.77.8",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz",
"integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==",
"version": "1.79.4",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz",
"integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==",
"license": "MIT",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"chokidar": "^4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
@ -7390,6 +7501,34 @@
"node": ">=14.0.0"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
"license": "MIT",
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sax": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
@ -7414,9 +7553,9 @@
"optional": true
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@ -7493,19 +7632,27 @@
}
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dependencies": {
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/serve-static/node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@ -8444,6 +8591,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
@ -8455,6 +8603,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
@ -8497,11 +8646,6 @@
"node": ">=0.8"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/truncate-utf8-bytes": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
@ -8591,6 +8735,12 @@
"typescript": "*"
}
},
"node_modules/tslib": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
"integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
"license": "0BSD"
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@ -8668,6 +8818,15 @@
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
"node_modules/undici": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",
"integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
@ -8784,20 +8943,6 @@
"license": "MIT",
"optional": true
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@ -8867,26 +9012,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/ws": {
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "tidal-hifi",
"version": "5.16.0",
"version": "5.17.0",
"description": "Tidal on Electron with widevine(hifi) support",
"main": "ts-dist/main.js",
"scripts": {
@ -23,6 +23,7 @@
"build-mac": "npm run builder -- -c ./build/electron-builder.yml -m",
"build-base": "npm run builder -- -c ./build/electron-builder.base.yml",
"prebuilder": "npm run compile",
"prettier": "prettier . --write",
"builder": "electron-builder --publish=never",
"sass": "sass ./src/pages/settings/settings.scss ./src/pages/settings/settings.css && sass --no-source-map src/themes:themes",
"style-lint": "npx stylelint **/*.scss",
@ -42,21 +43,20 @@
"dependencies": {
"@electron/remote": "^2.1.2",
"@types/swagger-jsdoc": "^6.0.4",
"axios": "^1.7.2",
"@xhayper/discord-rpc": "^1.2.0",
"axios": "^1.7.7",
"cors": "^2.8.5",
"discord-rpc": "^4.0.1",
"electron-store": "^8.2.0",
"express": "^4.19.2",
"express": "^4.21.1",
"hotkeys-js": "^3.13.7",
"mpris-service": "^2.1.2",
"request": "^2.88.2",
"sass": "^1.77.8",
"sass": "^1.79.4",
"swagger-ui-express": "^5.0.1"
},
"devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0",
"@types/cors": "^2.8.17",
"@types/discord-rpc": "^4.0.8",
"@types/express": "^4.17.21",
"@types/node": "^20.14.10",
"@types/request": "^2.48.12",

View File

@ -16,8 +16,9 @@
search: '[class^="searchField"]',
shuffle: '*[data-test="shuffle"]',
repeat: '*[data-test="repeat"]',
account: '*[class^="profileOptions"]',
settings: '*[data-test^="open-settings"]',
account: '*[data-test^="profile-image-button"]',
settings: '*[data-test^="sidebar-menu-button"]',
openSettings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]',
image: "img",
current: '*[data-test="current-time"]',

View File

@ -10,7 +10,7 @@ export const globalEvents = {
showSettings: "showSettings",
storeChanged: "storeChanged",
error: "error",
whip: "whip",
getUniversalLink: "getUniversalLink",
log: "log",
toggleFavorite: "toggleFavorite",
toggleShuffle: "toggleShuffle",

View File

@ -55,6 +55,7 @@ export const settings = {
singleInstance: "singleInstance",
skipArtists: "skipArtists",
skippedArtists: "skippedArtists",
staticWindowTitle: "staticWindowTitle",
theme: "theme",
trayIcon: "trayIcon",
updateFrequency: "updateFrequency",

View File

@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "TIDAL Hi-Fi API",
"version": "5.16.0",
"version": "5.17.0",
"description": "",
"license": {
"name": "MIT",
@ -21,9 +21,7 @@
"/current": {
"get": {
"summary": "Get current media info",
"tags": [
"current"
],
"tags": ["current"],
"responses": {
"200": {
"description": "Current media info",
@ -41,9 +39,7 @@
"/current/image": {
"get": {
"summary": "Get current media image",
"tags": [
"current"
],
"tags": ["current"],
"responses": {
"200": {
"description": "Current media image",
@ -65,9 +61,7 @@
"/player/play": {
"post": {
"summary": "Play the current media",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -85,9 +79,7 @@
"/player/favorite/toggle": {
"post": {
"summary": "Add the current media to your favorites, or remove it if its already added to your favorites",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -105,9 +97,7 @@
"/player/pause": {
"post": {
"summary": "Pause the current media",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -125,9 +115,7 @@
"/player/next": {
"post": {
"summary": "Play the next song",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -145,9 +133,7 @@
"/player/previous": {
"post": {
"summary": "Play the previous song",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -165,9 +151,7 @@
"/player/shuffle/toggle": {
"post": {
"summary": "Play the previous song",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -185,9 +169,7 @@
"/player/repeat/toggle": {
"post": {
"summary": "Toggle the repeat status, toggles between \"off\" , \"single\" and \"all\"",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -205,9 +187,7 @@
"/player/playpause": {
"post": {
"summary": "Start playing the media if paused, or pause the media if playing",
"tags": [
"player"
],
"tags": ["player"],
"responses": {
"200": {
"description": "Ok",
@ -225,9 +205,7 @@
"/settings/skipped-artists": {
"get": {
"summary": "get a list of artists that TIDAL Hi-Fi will skip if skipping is enabled",
"tags": [
"settings"
],
"tags": ["settings"],
"responses": {
"200": {
"description": "The list book.",
@ -243,9 +221,7 @@
},
"post": {
"summary": "Add new artists to the list of skipped artists",
"tags": [
"settings"
],
"tags": ["settings"],
"requestBody": {
"required": true,
"content": {
@ -266,9 +242,7 @@
"/settings/skipped-artists/delete": {
"post": {
"summary": "Remove artists from the list of skipped artists",
"tags": [
"settings"
],
"tags": ["settings"],
"requestBody": {
"required": true,
"content": {
@ -289,9 +263,7 @@
"/settings/skipped-artists/current": {
"post": {
"summary": "Add the current artist to the list of skipped artists",
"tags": [
"settings"
],
"tags": ["settings"],
"responses": {
"200": {
"description": "Ok"
@ -300,9 +272,7 @@
},
"delete": {
"summary": "Remove the current artist from the list of skipped artists",
"tags": [
"settings"
],
"tags": ["settings"],
"responses": {
"200": {
"description": "Ok"
@ -313,9 +283,7 @@
"/image": {
"get": {
"summary": "Get current image",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -338,9 +306,7 @@
"/play": {
"get": {
"summary": "Play the current media",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -359,9 +325,7 @@
"/favorite/toggle": {
"get": {
"summary": "Add the current media to your favorites, or remove it if its already added to your favorites",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -380,9 +344,7 @@
"/pause": {
"get": {
"summary": "Pause the current media",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -401,9 +363,7 @@
"/next": {
"get": {
"summary": "Play the next song",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -422,9 +382,7 @@
"/previous": {
"get": {
"summary": "Play the previous song",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -443,9 +401,7 @@
"/playpause": {
"get": {
"summary": "Toggle play/pause",
"tags": [
"legacy"
],
"tags": ["legacy"],
"deprecated": true,
"responses": {
"200": {
@ -558,10 +514,7 @@
"items": {
"type": "string"
},
"example": [
"Artist1",
"Artist2"
]
"example": ["Artist1", "Artist2"]
}
}
},

View File

@ -0,0 +1,10 @@
export class SharingService {
/**
* Retrieve the universal link given a regular track link
* @param currentUrl
* @returns
*/
public static getUniversalLink(currentUrl: string): string {
return `${currentUrl}?u`;
}
}

View File

@ -1,21 +0,0 @@
import { ServiceLinks } from "./ServiceLinks";
export interface Artist {
type: string;
id: number;
path: string;
name: string;
sourceUrl: string;
sourceCountry: string;
url: string;
image: string;
createdAt: string;
updatedAt: string;
refreshedAt: string;
serviceIds: { [key: string]: string };
orchardId: string;
spotifyId: string;
links: { [key: string]: ServiceLinks[] };
linksCountries: string[];
description: string;
}

View File

@ -1,4 +0,0 @@
export interface ServiceLinks {
link: string;
countries: string[];
}

View File

@ -1,27 +0,0 @@
import { Artist } from "./Artist";
import { ServiceLinks } from "./ServiceLinks";
export interface WhippedResult {
status: string;
data: {
item: {
type: string;
id: number;
path: string;
name: string;
url: string;
sourceUrl: string;
sourceCountry: string;
releaseDate: string;
createdAt: string;
updatedAt: string;
refreshedAt: string;
image: string;
isrc: string;
isExplicit: boolean;
links: { [key: string]: ServiceLinks[] };
linksCountries: string[];
artists: Artist[];
};
};
}

View File

@ -1,32 +0,0 @@
import { WhippedResult } from "./models/whip";
import axios from "axios";
export class Songwhip {
/**
* Call the songwhip API and create a shareable songwhip page
* @param currentUrl
* @returns
*/
public static async whip(currentUrl: string): Promise<WhippedResult> {
try {
const response = await axios.post("https://songwhip.com/api/songwhip/create", {
url: currentUrl,
// doesn't actually matter.. returns everything the same way anyway
country: "NL",
});
return response.data;
} catch (error) {
console.log(JSON.stringify(error));
}
}
/**
* Transform a songwhip response into a shareable url
* @param response
* @returns
*/
public static getWhipUrl(response: WhippedResult) {
return `https://songwhip.com${response.data.item.url}`;
}
}

View File

@ -10,7 +10,7 @@ import {
releaseInhibitorIfActive,
} from "./features/idleInhibitor/idleInhibitor";
import { Logger } from "./features/logger";
import { Songwhip } from "./features/songwhip/songwhip";
import { SharingService } from "./features/sharingService/sharingService";
import { MediaInfo } from "./models/mediaInfo";
import { MediaStatus } from "./models/mediaStatus";
import { initRPC, rpc, unRPC } from "./scripts/discord";
@ -250,8 +250,8 @@ ipcMain.on(globalEvents.error, (event) => {
console.log(event);
});
ipcMain.handle(globalEvents.whip, async (event, url) => {
return Songwhip.whip(url);
ipcMain.handle(globalEvents.getUniversalLink, async (event, url) => {
return SharingService.getUniversalLink(url);
});
Logger.watch(ipcMain);

View File

@ -46,6 +46,7 @@ let adBlock: HTMLInputElement,
singleInstance: HTMLInputElement,
skipArtists: HTMLInputElement,
skippedArtists: HTMLInputElement,
staticWindowTitle: HTMLInputElement,
theme: HTMLSelectElement,
trayIcon: HTMLInputElement,
updateFrequency: HTMLInputElement,
@ -140,8 +141,9 @@ function refreshSettings() {
port.value = settingsStore.get(settings.apiSettings.port);
singleInstance.checked = settingsStore.get(settings.singleInstance);
skipArtists.checked = settingsStore.get(settings.skipArtists);
theme.value = settingsStore.get(settings.theme);
skippedArtists.value = settingsStore.get<string, string[]>(settings.skippedArtists).join("\n");
staticWindowTitle.checked = settingsStore.get(settings.staticWindowTitle);
theme.value = settingsStore.get(settings.theme);
trayIcon.checked = settingsStore.get(settings.trayIcon);
updateFrequency.value = settingsStore.get(settings.updateFrequency);
enableListenBrainz.checked = settingsStore.get(settings.ListenBrainz.enabled);
@ -261,6 +263,7 @@ window.addEventListener("DOMContentLoaded", () => {
trayIcon = get("trayIcon");
skipArtists = get("skipArtists");
skippedArtists = get("skippedArtists");
staticWindowTitle = get("staticWindowTitle");
singleInstance = get("singleInstance");
updateFrequency = get("updateFrequency");
enableListenBrainz = get("enableListenBrainz");
@ -295,6 +298,7 @@ window.addEventListener("DOMContentLoaded", () => {
addInputListener(port, settings.apiSettings.port);
addInputListener(skipArtists, settings.skipArtists);
addTextAreaListener(skippedArtists, settings.skippedArtists);
addInputListener(staticWindowTitle, settings.staticWindowTitle);
addInputListener(singleInstance, settings.singleInstance);
addSelectListener(theme, settings.theme);
addInputListener(trayIcon, settings.trayIcon);

View File

@ -1,25 +1,34 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<title>Tidal Hi-Fi settings</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="./settings.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
</head>
<body class="settings-window">
<div class="settings-window__wrapper">
<div class="settings-window__drag-area"></div>
<a id="close" class="settings-window__close-button" title="Close settings">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 348.333 348.334" class="settings-window__svg-icon">
<path fill="white" d="M336.559,68.611L231.016,174.165l105.543,105.549c15.699,15.705,15.699,41.145,0,56.85
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 348.333 348.334"
class="settings-window__svg-icon"
>
<path
fill="white"
d="M336.559,68.611L231.016,174.165l105.543,105.549c15.699,15.705,15.699,41.145,0,56.85
c-7.844,7.844-18.128,11.769-28.407,11.769c-10.296,0-20.581-3.919-28.419-11.769L174.167,231.003L68.609,336.563
c-7.843,7.844-18.128,11.769-28.416,11.769c-10.285,0-20.563-3.919-28.413-11.769c-15.699-15.698-15.699-41.139,0-56.85
l105.54-105.549L11.774,68.611c-15.699-15.699-15.699-41.145,0-56.844c15.696-15.687,41.127-15.687,56.829,0l105.563,105.554
L279.721,11.767c15.705-15.687,41.139-15.687,56.832,0C352.258,27.466,352.258,52.912,336.559,68.611z" />
L279.721,11.767c15.705-15.687,41.139-15.687,56.832,0C352.258,27.466,352.258,52.912,336.559,68.611z"
/>
</svg>
</a>
@ -66,7 +75,13 @@
<span class="switch__slider"></span>
</label>
</div>
<textarea id="skippedArtists" class="textarea" cols="40" rows="5" spellcheck="false"></textarea>
<textarea
id="skippedArtists"
class="textarea"
cols="40"
rows="5"
spellcheck="false"
></textarea>
<div class="group__option">
<div class="group__description">
<h4>Block ads</h4>
@ -106,6 +121,19 @@
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Static Window Title</h4>
<p>
Makes the window title "TIDAL Hi-Fi" instead of changing to the currently
playing song.
</p>
</div>
<label class="switch">
<input id="staticWindowTitle" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Minimize on Close</h4>
@ -121,7 +149,9 @@
<h4>Hotkeys</h4>
<p>
Enable extra hotkeys to achieve feature parity with the
<a class="external-link" data-url="https://defkey.com/tidal-desktop-shortcuts">desktop apps</a>.
<a class="external-link" data-url="https://defkey.com/tidal-desktop-shortcuts"
>desktop apps</a
>.
</p>
</div>
<label class="switch">
@ -147,8 +177,8 @@
<p class="group__title">API</p>
<div class="group__description">
<p>
TIDAL Hi-Fi has a built-in web API to allow users to get current media information.
You can optionally enable playback control as well.
TIDAL Hi-Fi has a built-in web API to allow users to get current media
information. You can optionally enable playback control as well.
</p>
</div>
<div class="group__option">
@ -170,7 +200,8 @@
<div class="group__option">
<div class="group__description">
<h4>API hostname</h4>
<p>By default (127.0.0.1) only local apps can interface with the API. <br />
<p>
By default (127.0.0.1) only local apps can interface with the API. <br />
Change to 0.0.0.0 to allow <strong>anyone</strong> to interact with it. <br />
Other options are available
</p>
@ -226,7 +257,6 @@
</label>
</div>
<div id="discord_options">
<div class="group__option" class="hidden">
<div class="group__description">
<h4>Show Idle Text</h4>
@ -242,15 +272,27 @@
<div class="group__description">
<h4>Idle Text</h4>
<p>The text displayed on Discord's rich presence while idling in the app.</p>
<input id="discord_idle_text" type="text" class="text-input" name="discord_idle_text" />
<input
id="discord_idle_text"
type="text"
class="text-input"
name="discord_idle_text"
/>
</div>
</div>
<div class="group__option" class="hidden">
<div class="group__description">
<h4>Using Tidal Text</h4>
<p>The text displayed on Discord's rich presence while "showSong" is turned off</p>
<input id="discord_using_text" type="text" class="text-input" name="discord_using_text" />
<p>
The text displayed on Discord's rich presence while "showSong" is turned off
</p>
<input
id="discord_using_text"
type="text"
class="text-input"
name="discord_using_text"
/>
</div>
</div>
@ -266,7 +308,6 @@
</div>
<div id="discord_show_song_options" class="hidden">
<div class="group__option" class="hidden">
<div class="group__description">
<h4>Include timestamps</h4>
@ -282,7 +323,12 @@
<div class="group__description">
<h4>Details prefix</h4>
<p>Prefix for the "details" field of Discord's rich presence.</p>
<input id="discord_details_prefix" type="text" class="text-input" name="discord_details_prefix" />
<input
id="discord_details_prefix"
type="text"
class="text-input"
name="discord_details_prefix"
/>
</div>
</div>
@ -290,11 +336,15 @@
<div class="group__description">
<h4>Button text</h4>
<p>Text to display on the button below the media information.</p>
<input id="discord_button_text" type="text" class="text-input" name="discord_button_text" />
<input
id="discord_button_text"
type="text"
class="text-input"
name="discord_button_text"
/>
</div>
</div>
</div>
</div>
</div>
<div class="group">
@ -313,23 +363,43 @@
<div class="group__option">
<div class="group__description">
<h4>ListenBrainz API Url</h4>
<p>There are multiple instances for ListenBrainz you can set the corresponding API url below.</p>
<input id="ListenBrainzAPI" type="text" class="text-input" name="ListenBrainzAPI" />
<p>
There are multiple instances for ListenBrainz you can set the corresponding
API url below.
</p>
<input
id="ListenBrainzAPI"
type="text"
class="text-input"
name="ListenBrainzAPI"
/>
</div>
</div>
<div class="group__option">
<div class="group__description">
<h4>ListenBrainz User Token</h4>
<p>Provide the user token you can get from the settings page.</p>
<input id="ListenBrainzToken" type="text" class="text-input" name="ListenBrainzToken" />
<input
id="ListenBrainzToken"
type="text"
class="text-input"
name="ListenBrainzToken"
/>
</div>
</div>
</div>
<div class="group__description">
<h4>ScrobbleDelay</h4>
<p>The delay (in ms) to send a listen to ListenBrainz. Prevents spamming the API when you fast forward
immediately</p>
<input id="listenbrainz_delay" type="number" class="text-input" name="listenbrainz_delay" />
<p>
The delay (in ms) to send a listen to ListenBrainz. Prevents spamming the API when
you fast forward immediately
</p>
<input
id="listenbrainz_delay"
type="number"
class="text-input"
name="listenbrainz_delay"
/>
</div>
</div>
</section>
@ -341,12 +411,16 @@
<div class="group__description">
<h4>Update frequency</h4>
<p>
The amount of time, in milliseconds, that TIDAL Hi-Fi will refresh its playback info by scraping the
website.
The default of 500 seems to work in more cases but if you are fine with a bit more resource usage you
can decrease it as well.
The amount of time, in milliseconds, that TIDAL Hi-Fi will refresh its playback
info by scraping the website. The default of 500 seems to work in more cases but
if you are fine with a bit more resource usage you can decrease it as well.
</p>
<input id="updateFrequency" type="number" class="text-input" name="updateFrequency" />
<input
id="updateFrequency"
type="number"
class="text-input"
name="updateFrequency"
/>
</div>
</div>
@ -359,7 +433,9 @@
</p>
<select class="select-input" id="channel" name="channel">
<option value="https://listen.tidal.com">Stable (listen.tidal.com)</option>
<option value="https://listen.stage.tidal.com">Staging (listen.stage.tidal.com)</option>
<option value="https://listen.stage.tidal.com">
Staging (listen.stage.tidal.com)
</option>
</select>
</div>
</div>
@ -405,7 +481,8 @@
<div class="group__description">
<h4>Wayland support</h4>
<p>
Adds a couple of Electron flags to help TIDAL Hi-Fi run smoothly on the Wayland window system.
Adds a couple of Electron flags to help TIDAL Hi-Fi run smoothly on the Wayland
window system.
</p>
</div>
<label class="switch">
@ -423,12 +500,19 @@
<div class="group__description">
<h4>Custom CSS</h4>
<p>
The css that you put in here will be injected into a style tag in the head of the document.
The css that you put in here will be injected into a style tag in the head of
the document.
</p>
</div>
</div>
</div>
<textarea id="customCSS" class="textarea" cols="40" rows="8" spellcheck="false"></textarea>
<textarea
id="customCSS"
class="textarea"
cols="40"
rows="8"
spellcheck="false"
></textarea>
<div class="group">
<p class="group__title">Theme files</p>
@ -438,9 +522,7 @@
<p>
Select a theme below or "Tidal - Default" to return to the original Tidal look.
</p>
<select class="select-input" id="themesList" name="themesList">
</select>
<select class="select-input" id="themesList" name="themesList"></select>
</div>
</div>
@ -448,14 +530,20 @@
<div class="group__description">
<h4>Upload new themes</h4>
<p>
Click the button and select the css files to import. They will be added to the theme list
automatically.
Click the button and select the css files to import. They will be added to the
theme list automatically.
</p>
<div class="file-drop-area">
<div>
<span class="file-btn">Choose files</span>
<span id="file-message" class="file-msg">or drag and drop files here</span>
<input id="theme-files" class="file-input" type="file" accept=".css" multiple>
<input
id="theme-files"
class="file-input"
type="file"
accept=".css"
multiple
/>
</div>
</div>
</div>
@ -467,17 +555,35 @@
<img alt="tidal icon" class="about-section__icon" src="./icon.png" />
<h4>TIDAL Hi-Fi</h4>
<div class="about-section__version">
<a target="_blank" rel="noopener"
href="https://github.com/Mastermindzh/tidal-hifi/releases/tag/5.16.0">5.16.0</a>
<a
target="_blank"
rel="noopener"
href="https://github.com/Mastermindzh/tidal-hifi/releases/tag/5.17.0"
>5.17.0</a
>
</div>
<div class="about-section__links">
<a target="_blank" rel="noopener" href="https://github.com/mastermindzh/tidal-hifi/"
class="about-section__button">Github
<i class="fa fa-external-link"></i></a>
<a target="_blank" rel="noopener" href="https://github.com/Mastermindzh/tidal-hifi/issues"
class="about-section__button">Report an issue <i class="fa fa-external-link"></i></a>
<a target="_blank" rel="noopener" href="https://github.com/Mastermindzh/tidal-hifi/graphs/contributors"
class="about-section__button">Contributors <i class="fa fa-external-link"></i></a>
<a
target="_blank"
rel="noopener"
href="https://github.com/mastermindzh/tidal-hifi/"
class="about-section__button"
>Github <i class="fa fa-external-link"></i
></a>
<a
target="_blank"
rel="noopener"
href="https://github.com/Mastermindzh/tidal-hifi/issues"
class="about-section__button"
>Report an issue <i class="fa fa-external-link"></i
></a>
<a
target="_blank"
rel="noopener"
href="https://github.com/Mastermindzh/tidal-hifi/graphs/contributors"
class="about-section__button"
>Contributors <i class="fa fa-external-link"></i
></a>
</div>
</section>
@ -490,5 +596,4 @@
</main>
</div>
</body>
</html>

View File

@ -10,7 +10,7 @@ import {
} from "./features/listenbrainz/listenbrainz";
import { StoreData } from "./features/listenbrainz/models/storeData";
import { Logger } from "./features/logger";
import { Songwhip } from "./features/songwhip/songwhip";
import { SharingService } from "./features/sharingService/sharingService";
import { addCustomCss } from "./features/theming/theming";
import { convertDurationToSeconds } from "./features/time/parse";
import { MediaInfo } from "./models/mediaInfo";
@ -48,8 +48,9 @@ const elements = {
search: '[class^="searchField"]',
shuffle: '*[data-test="shuffle"]',
repeat: '*[data-test="repeat"]',
account: '*[class^="profileOptions"]',
settings: '*[data-test^="open-settings"]',
account: '*[data-test^="profile-image-button"]',
settings: '*[data-test^="sidebar-menu-button"]',
openSettings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]',
image: "img",
current: '*[data-test="current-time"]',
@ -220,9 +221,9 @@ ListenBrainzStore.clear();
function addHotKeys() {
if (settingsStore.get(settings.enableCustomHotkeys)) {
addHotkey("Control+p", function () {
elements.click("account");
setTimeout(() => {
elements.click("settings");
setTimeout(() => {
elements.click("openSettings");
}, 100);
});
addHotkey("Control+l", function () {
@ -254,11 +255,10 @@ function addHotKeys() {
elements.click("repeat");
});
addHotkey("control+w", async function () {
const result = await ipcRenderer.invoke(globalEvents.whip, getTrackURL());
const url = Songwhip.getWhipUrl(result);
const url = SharingService.getUniversalLink(getTrackURL());
clipboard.writeText(url);
new Notification({
title: `Successfully whipped: `,
title: `Universal link generated: `,
body: `URL copied to clipboard: ${url}`,
}).show();
});
@ -550,6 +550,7 @@ setInterval(function () {
const artistsArray = elements.getArtistsArray();
const artistsString = elements.getArtistsString(artistsArray);
const songDashArtistTitle = `${title} - ${artistsString}`;
const staticTitle = "TIDAL Hi-Fi";
const titleOrArtistsChanged = currentSong !== songDashArtistTitle;
const current = elements.getText("current");
const currentStatus = getCurrentlyPlayingStatus();
@ -594,7 +595,9 @@ setInterval(function () {
};
// update title, url and play info with new info
setTitle(songDashArtistTitle);
settingsStore.get(settings.staticWindowTitle)
? setTitle(staticTitle)
: setTitle(songDashArtistTitle);
getTrackURL();
currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus;

View File

@ -1,4 +1,4 @@
import { Client, Presence } from "discord-rpc";
import { Client, SetActivity } from "@xhayper/discord-rpc";
import { app, ipcMain } from "electron";
import { globalEvents } from "../constants/globalEvents";
import { settings } from "../constants/settings";
@ -12,6 +12,10 @@ const clientId = "833617820704440341";
export let rpc: Client;
const ACTIVITY_LISTENING = 2;
const MAX_RETRIES = 5;
const RETRY_DELAY = 10000;
const observer = () => {
if (rpc) {
updateActivity();
@ -22,19 +26,20 @@ const defaultPresence = {
largeImageKey: "tidal-hifi-icon",
largeImageText: `TIDAL Hi-Fi ${app.getVersion()}`,
instance: false,
type: ACTIVITY_LISTENING,
};
const updateActivity = () => {
const showIdle = settingsStore.get<string, boolean>(settings.discord.showIdle) ?? true;
if (mediaInfo.status === MediaStatus.paused && !showIdle) {
rpc.clearActivity();
rpc.user?.clearActivity();
} else {
rpc.setActivity(getActivity());
rpc.user?.setActivity(getActivity());
}
};
const getActivity = (): Presence => {
const presence: Presence = { ...defaultPresence };
const getActivity = (): SetActivity => {
const presence: SetActivity = { ...defaultPresence };
if (mediaInfo.status === MediaStatus.paused) {
presence.details =
@ -50,6 +55,7 @@ const getActivity = (): Presence => {
settingsStore.get<string, string>(settings.discord.usingText) ?? "Playing media on TIDAL";
}
}
return presence;
function getFromStore() {
@ -98,10 +104,31 @@ const getActivity = (): Presence => {
const currentSeconds = convertDurationToSeconds(mediaInfo.current);
const durationSeconds = convertDurationToSeconds(mediaInfo.duration);
const date = new Date();
const now = (date.getTime() / 1000) | 0;
const remaining = date.setSeconds(date.getSeconds() + (durationSeconds - currentSeconds));
presence.startTimestamp = now;
presence.endTimestamp = remaining;
const now = Math.floor(date.getTime() / 1000);
presence.startTimestamp = now - currentSeconds;
presence.endTimestamp = presence.startTimestamp + durationSeconds;
}
}
};
/**
* Try to login to RPC and retry if it errors
* @param retryCount Max retry count
*/
const connectWithRetry = async (retryCount = 0) => {
try {
await rpc.login();
Logger.log("Connected to Discord");
rpc.on("ready", updateActivity);
Object.values(globalEvents).forEach((event) => ipcMain.on(event, observer));
} catch (error) {
if (retryCount < MAX_RETRIES) {
Logger.log(
`Failed to connect to Discord, retrying in ${RETRY_DELAY / 1000} seconds... (Attempt ${retryCount + 1}/${MAX_RETRIES})`
);
setTimeout(() => connectWithRetry(retryCount + 1), RETRY_DELAY);
} else {
Logger.log("Failed to connect to Discord after maximum retry attempts");
}
}
};
@ -110,22 +137,8 @@ const getActivity = (): Presence => {
* Set up the discord rpc and listen on globalEvents.updateInfo
*/
export const initRPC = () => {
rpc = new Client({ transport: "ipc" });
rpc.login({ clientId }).then(
() => {
rpc.on("ready", () => {
updateActivity();
});
const { updateInfo, play, pause, playPause } = globalEvents;
[updateInfo, play, pause, playPause].forEach((status) => {
ipcMain.on(status, observer);
});
},
() => {
Logger.log("Can't connect to Discord, is it running?");
}
);
rpc = new Client({ transport: { type: "ipc" }, clientId });
connectWithRetry();
};
/**
@ -133,7 +146,7 @@ export const initRPC = () => {
*/
export const unRPC = () => {
if (rpc) {
rpc.clearActivity();
rpc.user?.clearActivity();
rpc.destroy();
rpc = null;
ipcMain.removeListener(globalEvents.updateInfo, observer);

View File

@ -69,6 +69,7 @@ export const settingsStore = new Store({
singleInstance: true,
skipArtists: false,
skippedArtists: [""],
staticWindowTitle: false,
theme: "none",
trayIcon: true,
updateFrequency: 500,