Compare commits

..

73 Commits

Author SHA1 Message Date
8fc49f500e
Merge pull request #577 from Mastermindzh/snyk-upgrade-b144be84df6292449bfe4eacd9662b97
[Snyk] Upgrade sass from 1.79.4 to 1.85.1
2025-04-18 14:50:52 +02:00
snyk-bot
49f5a01b58
fix: upgrade sass from 1.79.4 to 1.85.1
Snyk has created this PR to upgrade sass from 1.79.4 to 1.85.1.

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
2025-04-05 07:45:32 +00:00
48f4fe47ef
Merge pull request #573 from Mastermindzh/next
Next
2025-03-30 13:10:28 +02:00
a819e1eb45 removing mac/win builds, they aren't targetted anyway 2025-03-30 12:52:48 +02:00
57e6342b5f Merge branch 'next' of github.com:Mastermindzh/tidal-hifi into next 2025-03-30 12:50:31 +02:00
58a543a3c8 fixed .desktop file 2025-03-30 12:50:27 +02:00
1c915d99fe
Merge pull request #572 from QF0xB/fix/nix-build-temporarily
Reverted sass to 1.79.4
2025-03-30 12:49:29 +02:00
Quirin Brändli
8994360415
Reverted sass to 1.79.4 2025-03-30 00:06:46 +01:00
a5c0d9e6e8
Merge pull request #570 from Mastermindzh/fix/login
Fix/login
2025-03-29 20:45:40 +01:00
f8eb36f4c7 bumped versions 2025-03-29 17:12:28 +01:00
5ca90c25d3 update builder to v35 as well 2025-03-29 17:05:17 +01:00
cf86969cfc updated builder, removed abstract-socket 2025-03-29 16:27:24 +01:00
afd05ae88b updated electron, deps, and moved remote calls after start 2025-03-29 15:58:28 +01:00
72c25dfdc1
Merge pull request #564 from Mastermindzh/snyk-fix-1995dd90a05e86ea7c2e4f0f7a284930
[Snyk] Security upgrade @xhayper/discord-rpc from 1.2.0 to 1.2.1
2025-03-23 21:32:20 +01:00
snyk-bot
25b30edf31
fix: package.json & package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-UNDICI-8641354
2025-03-22 08:54:58 +00:00
35ed4807e3
Merge pull request #558 from Mastermindzh/next
Next
2025-03-18 09:10:40 +01:00
deff9524a8 album & playingfrom info added to mediaInfo again. fixes #548 2025-03-17 11:05:10 +01:00
34c7777eeb
Merge pull request #554 from Dianoga/duration-fix
fix duration selector
2025-03-17 10:27:42 +01:00
048e949f30 Merge branch 'master' of github.com:Mastermindzh/tidal-hifi into next 2025-03-17 09:54:06 +01:00
Brian Steere
4a976bc58c
fix duration selector
Should fix the MPRIS issue as long as duration doesn't break again.
2025-03-12 14:21:13 -05:00
ff4c51234b
Merge pull request #524 from Mastermindzh/snyk-upgrade-278f4ec84db5f4ebc26d987909c6c73f
[Snyk] Upgrade hotkeys-js from 3.13.7 to 3.13.9
2025-01-06 17:48:43 +01:00
snyk-bot
c4ee6b51b9
fix: upgrade hotkeys-js from 3.13.7 to 3.13.9
Snyk has created this PR to upgrade hotkeys-js from 3.13.7 to 3.13.9.

See this package in npm:
hotkeys-js

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
2025-01-04 01:22:24 +00:00
45fe336598
Merge pull request #522 from Mastermindzh/snyk-upgrade-7f82ddec0efc65c72c6f7f2f182ace7c
[Snyk] Upgrade axios from 1.7.8 to 1.7.9
2024-12-28 12:45:52 +01:00
snyk-bot
fe9f50aaf5
fix: upgrade axios from 1.7.8 to 1.7.9
Snyk has created this PR to upgrade axios from 1.7.8 to 1.7.9.

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-12-27 23:17:27 +00:00
aaf7a1b662
Merge pull request #504 from darkiox/master
Fix Discord RPC Timestamp jitter
2024-12-09 11:21:27 +01:00
755be0ee30
Merge pull request #515 from Mastermindzh/snyk-fix-82857ea1745fc2cfbc5f06a87a8ff0b8
[Snyk] Security upgrade express from 4.21.1 to 4.21.2
2024-12-09 11:05:23 +01:00
snyk-bot
9d736b2bd9
fix: package.json & package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-PATHTOREGEXP-8482416
2024-12-06 20:33:54 +00:00
f4d4b1a1df
Merge pull request #508 from Mastermindzh/snyk-fix-a6630fbdb3c8f0161d0b939cf0ec3ca3
[Snyk] Security upgrade axios from 1.7.7 to 1.7.8
2024-12-02 09:03:11 +01:00
snyk-bot
0c27c815f5
fix: package.json & package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-6671926
2024-11-30 06:57:38 +00:00
Darkiox
a3aa45a96b fix: discord rpc timestamp jitter 2024-11-25 19:15:26 -03:00
ae699887b2
Merge pull request #494 from Mastermindzh/develop
Develop
2024-10-27 21:51:50 +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
4f72e1b35d fix: Notifications are now send at the end of the update process, allowing other events to happen sooner. 2024-08-10 14:46:08 +02:00
2c1c76d2d0 fix: all discord fields are now padded to 2+ chars 2024-08-10 14:20:13 +02:00
e2a84e119a
Merge pull request #448 from autumn-puffin/master
Add option to disable the discord rpc idle text
2024-08-05 18:28:53 +02:00
5b85e59fc3
Merge pull request #450 from HurleybirdJr/master
Fix issue #449 Discord RPC stuck on "Browsing Tidal"
2024-08-05 18:28:44 +02:00
c3b772919a
Merge pull request #451 from Mastermindzh/snyk-upgrade-ceb71bfecd1c7652906d5640e4d56a9b
[Snyk] Upgrade sass from 1.77.6 to 1.77.8
2024-08-02 14:27:00 +02:00
snyk-bot
70f2f5c248
fix: upgrade sass from 1.77.6 to 1.77.8
Snyk has created this PR to upgrade sass from 1.77.6 to 1.77.8.

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-08-02 08:17:34 +00:00
Will Hurley
ffcb563b35 Fix Discord RPC album length < 2 2024-08-01 12:10:37 +01:00
Autumn
2dd96dd48e Add option to disable the discord rpc idle text 2024-07-30 21:26:35 +10:00
23 changed files with 3677 additions and 1495 deletions

View File

@ -24,23 +24,27 @@ jobs:
node-version: 22.4 node-version: 22.4
- run: npm install - run: npm install
- run: npm run build - run: npm run build
# - uses: actions/upload-artifact@master
# with:
# name: linux-builds
# path: dist/
build_on_mac: # build_on_mac:
runs-on: macos-latest # runs-on: macos-latest
steps: # steps:
- uses: actions/checkout@master # - uses: actions/checkout@master
- uses: actions/setup-node@master # - uses: actions/setup-node@master
with: # with:
node-version: 22.4 # node-version: 22.4
- run: npm install # - run: npm install
- run: npm run build # - run: npm run build
build_on_win: # build_on_win:
runs-on: windows-latest # runs-on: windows-latest
steps: # steps:
- uses: actions/checkout@master # - uses: actions/checkout@master
- uses: actions/setup-node@master # - uses: actions/setup-node@master
with: # with:
node-version: 22.4 # node-version: 22.4
- run: npm install # - run: npm install
- run: npm run build # - run: npm run build

View File

@ -29,30 +29,30 @@ jobs:
name: linux-builds name: linux-builds
path: dist/ path: dist/
build_on_mac: # build_on_mac:
runs-on: macos-latest # runs-on: macos-latest
steps: # steps:
- uses: actions/checkout@master # - uses: actions/checkout@master
- uses: actions/setup-node@master # - uses: actions/setup-node@master
with: # with:
node-version: 22.4 # node-version: 22.4
- run: npm install # - run: npm install
- run: npm run build # - run: npm run build
- uses: actions/upload-artifact@master # - uses: actions/upload-artifact@master
with: # with:
name: mac-builds # name: mac-builds
path: ./dist/ # path: ./dist/
build_on_win: # build_on_win:
runs-on: windows-latest # runs-on: windows-latest
steps: # steps:
- uses: actions/checkout@master # - uses: actions/checkout@master
- uses: actions/setup-node@master # - uses: actions/setup-node@master
with: # with:
node-version: 22.4 # node-version: 22.4
- run: npm install # - run: npm install
- run: npm run build # - run: npm run build
- uses: actions/upload-artifact@master # - uses: actions/upload-artifact@master
with: # with:
name: windows-builds # name: windows-builds
path: dist/ # path: dist/

View File

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

View File

@ -4,6 +4,38 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [5.18.2]
- Reverted to sass 1.79.4 to fix `Nix` builds
- Changed electron-builder.base.yml to now generate the correct .desktop entries again
- Should fix flatpak build
## [5.18.1]
- Fixed the login bug
- Upgraded electron to 35.1.1
- Added Widevine/CDM info to startup
- delayed remote electron initializer
## [5.18.0]
- [Dianoga](https://github.com/Dianoga) fixed the duration selector, restoring mpris & partial API data.
- PR: #554
- Added `xesam:url` property to mpris metadata fixes [#506](https://github.com/Mastermindzh/tidal-hifi/issues/506)
## [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".
- Fix issue #448 Add option to disable the discord rpc idle text
- Notifications are now send at the end of the update process, allowing other events to happen sooner.
## [5.15.0] ## [5.15.0]
- Added all missing swagger/openApi info with the help of [Times-Z](https://github.com/Times-Z) - Added all missing swagger/openApi info with the help of [Times-Z](https://github.com/Times-Z)

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

View File

@ -1,7 +1,7 @@
appId: com.rickvanlieshout.tidal-hifi appId: com.rickvanlieshout.tidal-hifi
electronVersion: 28.1.1 electronVersion: 35.1.1
electronDownload: electronDownload:
version: 28.1.1+wvcus version: 35.1.1+wvcus
mirror: https://github.com/castlabs/electron-releases/releases/download/v mirror: https://github.com/castlabs/electron-releases/releases/download/v
snap: snap:
plugs: plugs:
@ -22,19 +22,19 @@ linux:
"--enable-features=WaylandWindowDecorations", "--enable-features=WaylandWindowDecorations",
] ]
desktop: desktop:
Encoding: UTF-8 entry:
Name: TIDAL Hi-Fi Encoding: "UTF-8"
GenericName: TIDAL Hi-Fi Name: "TIDAL Hi-Fi"
Comment: The web version of listen.tidal.com running in electron with hifi support thanks to widevine. GenericName: "TIDAL Hi-Fi"
Icon: tidal-hifi Comment: "The web version of listen.tidal.com running in electron with hifi support thanks to widevine."
StartupNotify: true Icon: "tidal-hifi"
Terminal: false StartupNotify: "true"
Type: Application Terminal: "false"
Categories: Network;Application;AudioVideo;Audio;Video Type: "Application"
StartupWMClass: tidal-hifi Categories: "Network;Application;AudioVideo;Audio;Video"
X-PulseAudio-Properties: media.role=music StartupWMClass: "tidal-hifi"
MimeType: x-scheme-handler/tidal; X-PulseAudio-Properties: "media.role=music"
MimeType: "x-scheme-handler/tidal;"
mac: mac:
category: public.app-category.entertainment category: public.app-category.entertainment
win: win:

4578
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
{ {
"name": "tidal-hifi", "name": "tidal-hifi",
"version": "5.15.0", "version": "5.18.2",
"description": "Tidal on Electron with widevine(hifi) support", "description": "Tidal on Electron with widevine(hifi) support",
"main": "ts-dist/main.js", "main": "ts-dist/main.js",
"scripts": { "scripts": {
"start": "electron --inspect=0.0.0.0:5858 .", "start": "electron --inspect=0.0.0.0:5858 --remote-debugging-port=8315 --remote-allow-origins=* .",
"watchStart": "nodemon dist -x \"npm run start\"", "watchStart": "nodemon dist -x \"npm run start\"",
"compile": "tsc && npm run sass-and-copy", "compile": "tsc && npm run sass-and-copy",
"deps": "npm run watch", "deps": "npm run watch",
@ -42,21 +42,20 @@
"dependencies": { "dependencies": {
"@electron/remote": "^2.1.2", "@electron/remote": "^2.1.2",
"@types/swagger-jsdoc": "^6.0.4", "@types/swagger-jsdoc": "^6.0.4",
"axios": "^1.7.2", "@xhayper/discord-rpc": "^1.2.1",
"cors": "^2.8.5", "axios": "^1.8.4",
"discord-rpc": "^4.0.1",
"electron-store": "^8.2.0", "electron-store": "^8.2.0",
"express": "^4.19.2", "express": "^4.21.2",
"hotkeys-js": "^3.13.7", "hotkeys-js": "^3.13.9",
"mpris-service": "^2.1.2", "mpris-service": "^2.1.2",
"request": "^2.88.2", "sass": "1.85.1",
"sass": "^1.77.7", "swagger-ui-express": "^5.0.1",
"swagger-ui-express": "^5.0.1" "cors": "^2.8.5",
"request": "^2.88.2"
}, },
"devDependencies": { "devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0", "@mastermindzh/prettier-config": "^1.0.0",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/discord-rpc": "^4.0.8",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/node": "^20.14.10", "@types/node": "^20.14.10",
"@types/request": "^2.48.12", "@types/request": "^2.48.12",
@ -64,8 +63,8 @@
"@typescript-eslint/eslint-plugin": "^7.16.0", "@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.15.0", "@typescript-eslint/parser": "^7.15.0",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"electron": "git+https://github.com/castlabs/electron-releases#v31.1.0+wvcus", "electron": "github:castlabs/electron-releases#v35.1.1+wvcus",
"electron-builder": "~24.9.4", "electron-builder": "~26.0.12",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"markdown-toc": "^1.2.0", "markdown-toc": "^1.2.0",

View File

@ -16,17 +16,19 @@
search: '[class^="searchField"]', search: '[class^="searchField"]',
shuffle: '*[data-test="shuffle"]', shuffle: '*[data-test="shuffle"]',
repeat: '*[data-test="repeat"]', repeat: '*[data-test="repeat"]',
account: '*[class^="profileOptions"]', account: '*[data-test^="profile-image-button"]',
settings: '*[data-test^="open-settings"]', settings: '*[data-test^="sidebar-menu-button"]',
openSettings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]', media: '*[data-test="current-media-imagery"]',
image: "img", image: "img",
current: '*[data-test="current-time"]', current: '*[data-test="current-time"]',
duration: '*[class^=playbackControlsContainer] *[data-test="duration"]', duration: '*[class^=_playbackControlsContainer] *[data-test="duration"]',
bar: '*[data-test="progress-bar"]', bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer", footer: "#footerPlayer",
mediaItem: "[data-type='mediaItem']", mediaItem: "[data-type='mediaItem']",
album_header_title: '*[class^="playingFrom"] span:nth-child(2)', album_header_title: '*[class^="_playingFrom"] span:nth-child(2)',
playingFrom: '*[class^="playingFrom"] span:nth-child(2)', playing_from: '*[class^="_playingFrom"] span:nth-child(2)',
queue_album: "*[class^=playQueueItemsContainer] *[class^=groupTitle] span:nth-child(2)",
currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']", currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']",
album_name_cell: '[class^="album"]', album_name_cell: '[class^="album"]',
tracklist_row: '[data-test="tracklist-row"]', tracklist_row: '[data-test="tracklist-row"]',

View File

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

View File

@ -30,6 +30,7 @@ export const settings = {
buttonText: "discord.buttonText", buttonText: "discord.buttonText",
includeTimestamps: "discord.includeTimestamps", includeTimestamps: "discord.includeTimestamps",
showSong: "discord.showSong", showSong: "discord.showSong",
showIdle: "discord.showIdle",
idleText: "discord.idleText", idleText: "discord.idleText",
usingText: "discord.usingText", usingText: "discord.usingText",
}, },
@ -54,6 +55,7 @@ export const settings = {
singleInstance: "singleInstance", singleInstance: "singleInstance",
skipArtists: "skipArtists", skipArtists: "skipArtists",
skippedArtists: "skippedArtists", skippedArtists: "skippedArtists",
staticWindowTitle: "staticWindowTitle",
theme: "theme", theme: "theme",
trayIcon: "trayIcon", trayIcon: "trayIcon",
updateFrequency: "updateFrequency", updateFrequency: "updateFrequency",

View File

@ -2,7 +2,7 @@
"openapi": "3.1.0", "openapi": "3.1.0",
"info": { "info": {
"title": "TIDAL Hi-Fi API", "title": "TIDAL Hi-Fi API",
"version": "5.15.0", "version": "5.18.2",
"description": "", "description": "",
"license": { "license": {
"name": "MIT", "name": "MIT",
@ -21,9 +21,7 @@
"/current": { "/current": {
"get": { "get": {
"summary": "Get current media info", "summary": "Get current media info",
"tags": [ "tags": ["current"],
"current"
],
"responses": { "responses": {
"200": { "200": {
"description": "Current media info", "description": "Current media info",
@ -41,9 +39,7 @@
"/current/image": { "/current/image": {
"get": { "get": {
"summary": "Get current media image", "summary": "Get current media image",
"tags": [ "tags": ["current"],
"current"
],
"responses": { "responses": {
"200": { "200": {
"description": "Current media image", "description": "Current media image",
@ -65,9 +61,7 @@
"/player/play": { "/player/play": {
"post": { "post": {
"summary": "Play the current media", "summary": "Play the current media",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -85,9 +79,7 @@
"/player/favorite/toggle": { "/player/favorite/toggle": {
"post": { "post": {
"summary": "Add the current media to your favorites, or remove it if its already added to your favorites", "summary": "Add the current media to your favorites, or remove it if its already added to your favorites",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -105,9 +97,7 @@
"/player/pause": { "/player/pause": {
"post": { "post": {
"summary": "Pause the current media", "summary": "Pause the current media",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -125,9 +115,7 @@
"/player/next": { "/player/next": {
"post": { "post": {
"summary": "Play the next song", "summary": "Play the next song",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -145,9 +133,7 @@
"/player/previous": { "/player/previous": {
"post": { "post": {
"summary": "Play the previous song", "summary": "Play the previous song",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -165,9 +151,7 @@
"/player/shuffle/toggle": { "/player/shuffle/toggle": {
"post": { "post": {
"summary": "Play the previous song", "summary": "Play the previous song",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -185,9 +169,7 @@
"/player/repeat/toggle": { "/player/repeat/toggle": {
"post": { "post": {
"summary": "Toggle the repeat status, toggles between \"off\" , \"single\" and \"all\"", "summary": "Toggle the repeat status, toggles between \"off\" , \"single\" and \"all\"",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -205,9 +187,7 @@
"/player/playpause": { "/player/playpause": {
"post": { "post": {
"summary": "Start playing the media if paused, or pause the media if playing", "summary": "Start playing the media if paused, or pause the media if playing",
"tags": [ "tags": ["player"],
"player"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok", "description": "Ok",
@ -225,9 +205,7 @@
"/settings/skipped-artists": { "/settings/skipped-artists": {
"get": { "get": {
"summary": "get a list of artists that TIDAL Hi-Fi will skip if skipping is enabled", "summary": "get a list of artists that TIDAL Hi-Fi will skip if skipping is enabled",
"tags": [ "tags": ["settings"],
"settings"
],
"responses": { "responses": {
"200": { "200": {
"description": "The list book.", "description": "The list book.",
@ -243,9 +221,7 @@
}, },
"post": { "post": {
"summary": "Add new artists to the list of skipped artists", "summary": "Add new artists to the list of skipped artists",
"tags": [ "tags": ["settings"],
"settings"
],
"requestBody": { "requestBody": {
"required": true, "required": true,
"content": { "content": {
@ -266,9 +242,7 @@
"/settings/skipped-artists/delete": { "/settings/skipped-artists/delete": {
"post": { "post": {
"summary": "Remove artists from the list of skipped artists", "summary": "Remove artists from the list of skipped artists",
"tags": [ "tags": ["settings"],
"settings"
],
"requestBody": { "requestBody": {
"required": true, "required": true,
"content": { "content": {
@ -289,9 +263,7 @@
"/settings/skipped-artists/current": { "/settings/skipped-artists/current": {
"post": { "post": {
"summary": "Add the current artist to the list of skipped artists", "summary": "Add the current artist to the list of skipped artists",
"tags": [ "tags": ["settings"],
"settings"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok" "description": "Ok"
@ -300,9 +272,7 @@
}, },
"delete": { "delete": {
"summary": "Remove the current artist from the list of skipped artists", "summary": "Remove the current artist from the list of skipped artists",
"tags": [ "tags": ["settings"],
"settings"
],
"responses": { "responses": {
"200": { "200": {
"description": "Ok" "description": "Ok"
@ -313,9 +283,7 @@
"/image": { "/image": {
"get": { "get": {
"summary": "Get current image", "summary": "Get current image",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -338,9 +306,7 @@
"/play": { "/play": {
"get": { "get": {
"summary": "Play the current media", "summary": "Play the current media",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -359,9 +325,7 @@
"/favorite/toggle": { "/favorite/toggle": {
"get": { "get": {
"summary": "Add the current media to your favorites, or remove it if its already added to your favorites", "summary": "Add the current media to your favorites, or remove it if its already added to your favorites",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -380,9 +344,7 @@
"/pause": { "/pause": {
"get": { "get": {
"summary": "Pause the current media", "summary": "Pause the current media",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -401,9 +363,7 @@
"/next": { "/next": {
"get": { "get": {
"summary": "Play the next song", "summary": "Play the next song",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -422,9 +382,7 @@
"/previous": { "/previous": {
"get": { "get": {
"summary": "Play the previous song", "summary": "Play the previous song",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -443,9 +401,7 @@
"/playpause": { "/playpause": {
"get": { "get": {
"summary": "Toggle play/pause", "summary": "Toggle play/pause",
"tags": [ "tags": ["legacy"],
"legacy"
],
"deprecated": true, "deprecated": true,
"responses": { "responses": {
"200": { "200": {
@ -558,10 +514,7 @@
"items": { "items": {
"type": "string" "type": "string"
}, },
"example": [ "example": ["Artist1", "Artist2"]
"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, releaseInhibitorIfActive,
} from "./features/idleInhibitor/idleInhibitor"; } from "./features/idleInhibitor/idleInhibitor";
import { Logger } from "./features/logger"; import { Logger } from "./features/logger";
import { Songwhip } from "./features/songwhip/songwhip"; import { SharingService } from "./features/sharingService/sharingService";
import { MediaInfo } from "./models/mediaInfo"; import { MediaInfo } from "./models/mediaInfo";
import { MediaStatus } from "./models/mediaStatus"; import { MediaStatus } from "./models/mediaStatus";
import { initRPC, rpc, unRPC } from "./scripts/discord"; import { initRPC, rpc, unRPC } from "./scripts/discord";
@ -26,7 +26,6 @@ import {
import { addTray, refreshTray } from "./scripts/tray"; import { addTray, refreshTray } from "./scripts/tray";
let mainInhibitorId = -1; let mainInhibitorId = -1;
initialize();
let mainWindow: BrowserWindow; let mainWindow: BrowserWindow;
const icon = path.join(__dirname, "../assets/icon.png"); const icon = path.join(__dirname, "../assets/icon.png");
const PROTOCOL_PREFIX = "tidal"; const PROTOCOL_PREFIX = "tidal";
@ -98,6 +97,7 @@ function createWindow(options = { x: 0, y: 0, backgroundColor: "white" }) {
}, },
}, },
}); });
enable(mainWindow.webContents); enable(mainWindow.webContents);
registerHttpProtocols(); registerHttpProtocols();
syncMenuBarWithStore(); syncMenuBarWithStore();
@ -126,6 +126,7 @@ function createWindow(options = { x: 0, y: 0, backgroundColor: "white" }) {
} }
return false; return false;
}); });
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on("closed", function () { mainWindow.on("closed", function () {
releaseInhibitorIfActive(mainInhibitorId); releaseInhibitorIfActive(mainInhibitorId);
@ -178,6 +179,7 @@ app.on("ready", async () => {
if (isMainInstance() || isMultipleInstancesAllowed()) { if (isMainInstance() || isMultipleInstancesAllowed()) {
await components.whenReady(); await components.whenReady();
initialize();
// Adblock // Adblock
if (settingsStore.get(settings.adBlock)) { if (settingsStore.get(settings.adBlock)) {
@ -188,6 +190,8 @@ app.on("ready", async () => {
}); });
} }
Logger.log("components ready:", components.status());
createWindow(); createWindow();
addMenu(mainWindow); addMenu(mainWindow);
createSettingsWindow(); createSettingsWindow();
@ -250,8 +254,8 @@ ipcMain.on(globalEvents.error, (event) => {
console.log(event); console.log(event);
}); });
ipcMain.handle(globalEvents.whip, async (event, url) => { ipcMain.handle(globalEvents.getUniversalLink, async (event, url) => {
return Songwhip.whip(url); return SharingService.getUniversalLink(url);
}); });
Logger.watch(ipcMain); Logger.watch(ipcMain);

View File

@ -46,6 +46,7 @@ let adBlock: HTMLInputElement,
singleInstance: HTMLInputElement, singleInstance: HTMLInputElement,
skipArtists: HTMLInputElement, skipArtists: HTMLInputElement,
skippedArtists: HTMLInputElement, skippedArtists: HTMLInputElement,
staticWindowTitle: HTMLInputElement,
theme: HTMLSelectElement, theme: HTMLSelectElement,
trayIcon: HTMLInputElement, trayIcon: HTMLInputElement,
updateFrequency: HTMLInputElement, updateFrequency: HTMLInputElement,
@ -58,6 +59,7 @@ let adBlock: HTMLInputElement,
discord_include_timestamps: HTMLInputElement, discord_include_timestamps: HTMLInputElement,
discord_button_text: HTMLInputElement, discord_button_text: HTMLInputElement,
discord_show_song: HTMLInputElement, discord_show_song: HTMLInputElement,
discord_show_idle: HTMLInputElement,
discord_idle_text: HTMLInputElement, discord_idle_text: HTMLInputElement,
discord_using_text: HTMLInputElement; discord_using_text: HTMLInputElement;
@ -139,8 +141,9 @@ function refreshSettings() {
port.value = settingsStore.get(settings.apiSettings.port); port.value = settingsStore.get(settings.apiSettings.port);
singleInstance.checked = settingsStore.get(settings.singleInstance); singleInstance.checked = settingsStore.get(settings.singleInstance);
skipArtists.checked = settingsStore.get(settings.skipArtists); skipArtists.checked = settingsStore.get(settings.skipArtists);
theme.value = settingsStore.get(settings.theme);
skippedArtists.value = settingsStore.get<string, string[]>(settings.skippedArtists).join("\n"); 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); trayIcon.checked = settingsStore.get(settings.trayIcon);
updateFrequency.value = settingsStore.get(settings.updateFrequency); updateFrequency.value = settingsStore.get(settings.updateFrequency);
enableListenBrainz.checked = settingsStore.get(settings.ListenBrainz.enabled); enableListenBrainz.checked = settingsStore.get(settings.ListenBrainz.enabled);
@ -151,6 +154,7 @@ function refreshSettings() {
discord_include_timestamps.checked = settingsStore.get(settings.discord.includeTimestamps); discord_include_timestamps.checked = settingsStore.get(settings.discord.includeTimestamps);
discord_button_text.value = settingsStore.get(settings.discord.buttonText); discord_button_text.value = settingsStore.get(settings.discord.buttonText);
discord_show_song.checked = settingsStore.get(settings.discord.showSong); discord_show_song.checked = settingsStore.get(settings.discord.showSong);
discord_show_idle.checked = settingsStore.get(settings.discord.showIdle);
discord_idle_text.value = settingsStore.get(settings.discord.idleText); discord_idle_text.value = settingsStore.get(settings.discord.idleText);
discord_using_text.value = settingsStore.get(settings.discord.usingText); discord_using_text.value = settingsStore.get(settings.discord.usingText);
@ -259,6 +263,7 @@ window.addEventListener("DOMContentLoaded", () => {
trayIcon = get("trayIcon"); trayIcon = get("trayIcon");
skipArtists = get("skipArtists"); skipArtists = get("skipArtists");
skippedArtists = get("skippedArtists"); skippedArtists = get("skippedArtists");
staticWindowTitle = get("staticWindowTitle");
singleInstance = get("singleInstance"); singleInstance = get("singleInstance");
updateFrequency = get("updateFrequency"); updateFrequency = get("updateFrequency");
enableListenBrainz = get("enableListenBrainz"); enableListenBrainz = get("enableListenBrainz");
@ -269,6 +274,7 @@ window.addEventListener("DOMContentLoaded", () => {
listenbrainz_delay = get("listenbrainz_delay"); listenbrainz_delay = get("listenbrainz_delay");
discord_button_text = get("discord_button_text"); discord_button_text = get("discord_button_text");
discord_show_song = get("discord_show_song"); discord_show_song = get("discord_show_song");
discord_show_idle = get("discord_show_idle");
discord_using_text = get("discord_using_text"); discord_using_text = get("discord_using_text");
discord_idle_text = get("discord_idle_text"); discord_idle_text = get("discord_idle_text");
@ -292,6 +298,7 @@ window.addEventListener("DOMContentLoaded", () => {
addInputListener(port, settings.apiSettings.port); addInputListener(port, settings.apiSettings.port);
addInputListener(skipArtists, settings.skipArtists); addInputListener(skipArtists, settings.skipArtists);
addTextAreaListener(skippedArtists, settings.skippedArtists); addTextAreaListener(skippedArtists, settings.skippedArtists);
addInputListener(staticWindowTitle, settings.staticWindowTitle);
addInputListener(singleInstance, settings.singleInstance); addInputListener(singleInstance, settings.singleInstance);
addSelectListener(theme, settings.theme); addSelectListener(theme, settings.theme);
addInputListener(trayIcon, settings.trayIcon); addInputListener(trayIcon, settings.trayIcon);
@ -312,6 +319,7 @@ window.addEventListener("DOMContentLoaded", () => {
settings.discord.showSong, settings.discord.showSong,
switchesWithSettings.discord_show_song switchesWithSettings.discord_show_song
); );
addInputListener(discord_show_idle, settings.discord.showIdle);
addInputListener(discord_idle_text, settings.discord.idleText); addInputListener(discord_idle_text, settings.discord.idleText);
addInputListener(discord_using_text, settings.discord.usingText); addInputListener(discord_using_text, settings.discord.usingText);
}); });

View File

@ -106,6 +106,16 @@
<span class="switch__slider"></span> <span class="switch__slider"></span>
</label> </label>
</div> </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__option">
<div class="group__description"> <div class="group__description">
<h4>Minimize on Close</h4> <h4>Minimize on Close</h4>
@ -227,6 +237,17 @@
</div> </div>
<div id="discord_options"> <div id="discord_options">
<div class="group__option" class="hidden">
<div class="group__description">
<h4>Show Idle Text</h4>
<p>Should the idle text be shown when idle?</p>
</div>
<label class="switch">
<input id="discord_show_idle" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option" class="hidden"> <div class="group__option" class="hidden">
<div class="group__description"> <div class="group__description">
<h4>Idle Text</h4> <h4>Idle Text</h4>
@ -457,7 +478,7 @@
<h4>TIDAL Hi-Fi</h4> <h4>TIDAL Hi-Fi</h4>
<div class="about-section__version"> <div class="about-section__version">
<a target="_blank" rel="noopener" <a target="_blank" rel="noopener"
href="https://github.com/Mastermindzh/tidal-hifi/releases/tag/5.15.0">5.15.0</a> href="https://github.com/Mastermindzh/tidal-hifi/releases/tag/5.18.2">5.18.2</a>
</div> </div>
<div class="about-section__links"> <div class="about-section__links">
<a target="_blank" rel="noopener" href="https://github.com/mastermindzh/tidal-hifi/" <a target="_blank" rel="noopener" href="https://github.com/mastermindzh/tidal-hifi/"

View File

@ -10,7 +10,7 @@ import {
} from "./features/listenbrainz/listenbrainz"; } from "./features/listenbrainz/listenbrainz";
import { StoreData } from "./features/listenbrainz/models/storeData"; import { StoreData } from "./features/listenbrainz/models/storeData";
import { Logger } from "./features/logger"; import { Logger } from "./features/logger";
import { Songwhip } from "./features/songwhip/songwhip"; import { SharingService } from "./features/sharingService/sharingService";
import { addCustomCss } from "./features/theming/theming"; import { addCustomCss } from "./features/theming/theming";
import { convertDurationToSeconds } from "./features/time/parse"; import { convertDurationToSeconds } from "./features/time/parse";
import { MediaInfo } from "./models/mediaInfo"; import { MediaInfo } from "./models/mediaInfo";
@ -48,17 +48,18 @@ const elements = {
search: '[class^="searchField"]', search: '[class^="searchField"]',
shuffle: '*[data-test="shuffle"]', shuffle: '*[data-test="shuffle"]',
repeat: '*[data-test="repeat"]', repeat: '*[data-test="repeat"]',
account: '*[class^="profileOptions"]', account: '*[data-test^="profile-image-button"]',
settings: '*[data-test^="open-settings"]', settings: '*[data-test^="sidebar-menu-button"]',
openSettings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]', media: '*[data-test="current-media-imagery"]',
image: "img", image: "img",
current: '*[data-test="current-time"]', current: '*[data-test="current-time"]',
duration: '*[class^=playbackControlsContainer] *[data-test="duration"]', duration: '*[class^=_playbackControlsContainer] *[data-test="duration"]',
bar: '*[data-test="progress-bar"]', bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer", footer: "#footerPlayer",
mediaItem: "[data-type='mediaItem']", mediaItem: "[data-type='mediaItem']",
album_header_title: '*[class^="playingFrom"] span:nth-child(2)', album_header_title: '*[class^="_playingFrom"] span:nth-child(2)',
playing_from: '*[class^="playingFrom"] span:nth-child(2)', playing_from: '*[class^="_playingFrom"] span:nth-child(2)',
queue_album: "*[class^=playQueueItemsContainer] *[class^=groupTitle] span:nth-child(2)", queue_album: "*[class^=playQueueItemsContainer] *[class^=groupTitle] span:nth-child(2)",
currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']", currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']",
album_name_cell: '[class^="album"]', album_name_cell: '[class^="album"]',
@ -220,9 +221,9 @@ ListenBrainzStore.clear();
function addHotKeys() { function addHotKeys() {
if (settingsStore.get(settings.enableCustomHotkeys)) { if (settingsStore.get(settings.enableCustomHotkeys)) {
addHotkey("Control+p", function () { addHotkey("Control+p", function () {
elements.click("account");
setTimeout(() => {
elements.click("settings"); elements.click("settings");
setTimeout(() => {
elements.click("openSettings");
}, 100); }, 100);
}); });
addHotkey("Control+l", function () { addHotkey("Control+l", function () {
@ -254,11 +255,10 @@ function addHotKeys() {
elements.click("repeat"); elements.click("repeat");
}); });
addHotkey("control+w", async function () { addHotkey("control+w", async function () {
const result = await ipcRenderer.invoke(globalEvents.whip, getTrackURL()); const url = SharingService.getUniversalLink(getTrackURL());
const url = Songwhip.getWhipUrl(result);
clipboard.writeText(url); clipboard.writeText(url);
new Notification({ new Notification({
title: `Successfully whipped: `, title: `Universal link generated: `,
body: `URL copied to clipboard: ${url}`, body: `URL copied to clipboard: ${url}`,
}).show(); }).show();
}); });
@ -394,8 +394,24 @@ function updateMediaInfo(mediaInfo: MediaInfo, notify: boolean) {
if (mediaInfo) { if (mediaInfo) {
currentMediaInfo = mediaInfo; currentMediaInfo = mediaInfo;
ipcRenderer.send(globalEvents.updateInfo, mediaInfo); ipcRenderer.send(globalEvents.updateInfo, mediaInfo);
if (settingsStore.get(settings.notifications) && notify) { updateMpris(mediaInfo);
if (currentNotification) currentNotification.close(); updateListenBrainz(mediaInfo);
if (notify) {
sendNotification(mediaInfo);
}
}
}
/**
* send a desktop notification if enabled in settings
* @param mediaInfo
* @param notify Whether to notify
*/
async function sendNotification(mediaInfo: MediaInfo) {
if (settingsStore.get(settings.notifications)) {
if (currentNotification) {
currentNotification.close();
}
currentNotification = new Notification({ currentNotification = new Notification({
title: mediaInfo.title, title: mediaInfo.title,
body: mediaInfo.artists, body: mediaInfo.artists,
@ -403,10 +419,6 @@ function updateMediaInfo(mediaInfo: MediaInfo, notify: boolean) {
}); });
currentNotification.show(); currentNotification.show();
} }
updateMpris(mediaInfo);
updateListenBrainz(mediaInfo);
}
} }
function addMPRIS() { function addMPRIS() {
@ -471,6 +483,7 @@ function updateMpris(mediaInfo: MediaInfo) {
"xesam:title": mediaInfo.title, "xesam:title": mediaInfo.title,
"xesam:artist": [mediaInfo.artists], "xesam:artist": [mediaInfo.artists],
"xesam:album": mediaInfo.album, "xesam:album": mediaInfo.album,
"xesam:url": mediaInfo.url,
"mpris:artUrl": mediaInfo.image, "mpris:artUrl": mediaInfo.image,
"mpris:length": convertDuration(mediaInfo.duration) * 1000 * 1000, "mpris:length": convertDuration(mediaInfo.duration) * 1000 * 1000,
"mpris:trackid": "/org/mpris/MediaPlayer2/track/" + getTrackID(), "mpris:trackid": "/org/mpris/MediaPlayer2/track/" + getTrackID(),
@ -538,6 +551,7 @@ setInterval(function () {
const artistsArray = elements.getArtistsArray(); const artistsArray = elements.getArtistsArray();
const artistsString = elements.getArtistsString(artistsArray); const artistsString = elements.getArtistsString(artistsArray);
const songDashArtistTitle = `${title} - ${artistsString}`; const songDashArtistTitle = `${title} - ${artistsString}`;
const staticTitle = "TIDAL Hi-Fi";
const titleOrArtistsChanged = currentSong !== songDashArtistTitle; const titleOrArtistsChanged = currentSong !== songDashArtistTitle;
const current = elements.getText("current"); const current = elements.getText("current");
const currentStatus = getCurrentlyPlayingStatus(); const currentStatus = getCurrentlyPlayingStatus();
@ -582,7 +596,9 @@ setInterval(function () {
}; };
// update title, url and play info with new info // update title, url and play info with new info
setTitle(songDashArtistTitle); settingsStore.get(settings.staticWindowTitle)
? setTitle(staticTitle)
: setTitle(songDashArtistTitle);
getTrackURL(); getTrackURL();
currentSong = songDashArtistTitle; currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus; 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 { app, ipcMain } from "electron";
import { globalEvents } from "../constants/globalEvents"; import { globalEvents } from "../constants/globalEvents";
import { settings } from "../constants/settings"; import { settings } from "../constants/settings";
@ -12,9 +12,13 @@ const clientId = "833617820704440341";
export let rpc: Client; export let rpc: Client;
const ACTIVITY_LISTENING = 2;
const MAX_RETRIES = 5;
const RETRY_DELAY = 10000;
const observer = () => { const observer = () => {
if (rpc) { if (rpc) {
rpc.setActivity(getActivity()); updateActivity();
} }
}; };
@ -22,10 +26,20 @@ const defaultPresence = {
largeImageKey: "tidal-hifi-icon", largeImageKey: "tidal-hifi-icon",
largeImageText: `TIDAL Hi-Fi ${app.getVersion()}`, largeImageText: `TIDAL Hi-Fi ${app.getVersion()}`,
instance: false, instance: false,
type: ACTIVITY_LISTENING
}; };
const getActivity = (): Presence => { const updateActivity = () => {
const presence: Presence = { ...defaultPresence }; const showIdle = settingsStore.get<string, boolean>(settings.discord.showIdle) ?? true;
if (mediaInfo.status === MediaStatus.paused && !showIdle) {
rpc.user?.clearActivity();
} else {
rpc.user?.setActivity(getActivity());
}
};
const getActivity = (): SetActivity => {
const presence: SetActivity = { ...defaultPresence };
if (mediaInfo.status === MediaStatus.paused) { if (mediaInfo.status === MediaStatus.paused) {
presence.details = presence.details =
@ -41,6 +55,7 @@ const getActivity = (): Presence => {
settingsStore.get<string, string>(settings.discord.usingText) ?? "Playing media on TIDAL"; settingsStore.get<string, string>(settings.discord.usingText) ?? "Playing media on TIDAL";
} }
} }
return presence; return presence;
function getFromStore() { function getFromStore() {
@ -54,18 +69,33 @@ const getActivity = (): Presence => {
return { includeTimestamps, detailsPrefix, buttonText }; return { includeTimestamps, detailsPrefix, buttonText };
} }
function setPresenceFromMediaInfo(detailsPrefix: string, buttonText: string) { /**
if (mediaInfo.url) { * Pad a string using spaces to at least 2 characters
presence.details = `${detailsPrefix}${mediaInfo.title}`; * @param input string to pad with 2 characters
presence.state = mediaInfo.artists ? mediaInfo.artists : "unknown artist(s)"; * @returns
presence.largeImageKey = mediaInfo.image; */
if (mediaInfo.album) { function pad(input: string): string {
presence.largeImageText = mediaInfo.album; return input.padEnd(2, " ");
} }
function setPresenceFromMediaInfo(detailsPrefix: string, buttonText: string) {
// discord requires a minimum of 2 characters
const title = pad(mediaInfo.title);
const album = pad(mediaInfo.album);
const artists = pad(mediaInfo.artists);
if (mediaInfo.url) {
presence.details = `${detailsPrefix}${title}`;
presence.state = artists ? artists : "unknown artist(s)";
presence.largeImageKey = mediaInfo.image;
if (album) {
presence.largeImageText = album;
}
presence.buttons = [{ label: buttonText, url: mediaInfo.url }]; presence.buttons = [{ label: buttonText, url: mediaInfo.url }];
} else { } else {
presence.details = `Watching ${mediaInfo.title}`; presence.details = `Watching ${title}`;
presence.state = mediaInfo.artists; presence.state = artists;
} }
} }
@ -73,11 +103,29 @@ const getActivity = (): Presence => {
if (includeTimestamps) { if (includeTimestamps) {
const currentSeconds = convertDurationToSeconds(mediaInfo.current); const currentSeconds = convertDurationToSeconds(mediaInfo.current);
const durationSeconds = convertDurationToSeconds(mediaInfo.duration); const durationSeconds = convertDurationToSeconds(mediaInfo.duration);
const date = new Date(); const now = Math.trunc((Date.now() + 500) / 1000);
const now = (date.getTime() / 1000) | 0; presence.startTimestamp = now - currentSeconds;
const remaining = date.setSeconds(date.getSeconds() + (durationSeconds - currentSeconds)); presence.endTimestamp = presence.startTimestamp + durationSeconds;
presence.startTimestamp = now; }
presence.endTimestamp = remaining; }
};
/**
* 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');
} }
} }
}; };
@ -86,18 +134,8 @@ const getActivity = (): Presence => {
* Set up the discord rpc and listen on globalEvents.updateInfo * Set up the discord rpc and listen on globalEvents.updateInfo
*/ */
export const initRPC = () => { export const initRPC = () => {
rpc = new Client({ transport: "ipc" }); rpc = new Client({ transport: {type: "ipc"}, clientId });
rpc.login({ clientId }).then( connectWithRetry();
() => {
rpc.on("ready", () => {
rpc.setActivity(getActivity());
});
ipcMain.on(globalEvents.updateInfo, observer);
},
() => {
Logger.log("Can't connect to Discord, is it running?");
}
);
}; };
/** /**
@ -105,7 +143,7 @@ export const initRPC = () => {
*/ */
export const unRPC = () => { export const unRPC = () => {
if (rpc) { if (rpc) {
rpc.clearActivity(); rpc.user?.clearActivity();
rpc.destroy(); rpc.destroy();
rpc = null; rpc = null;
ipcMain.removeListener(globalEvents.updateInfo, observer); ipcMain.removeListener(globalEvents.updateInfo, observer);

View File

@ -43,6 +43,7 @@ export const settingsStore = new Store({
enableDiscord: false, enableDiscord: false,
discord: { discord: {
showSong: true, showSong: true,
showIdle: true,
idleText: "Browsing Tidal", idleText: "Browsing Tidal",
usingText: "Playing media on TIDAL", usingText: "Playing media on TIDAL",
includeTimestamps: true, includeTimestamps: true,
@ -68,6 +69,7 @@ export const settingsStore = new Store({
singleInstance: true, singleInstance: true,
skipArtists: false, skipArtists: false,
skippedArtists: [""], skippedArtists: [""],
staticWindowTitle: false,
theme: "none", theme: "none",
trayIcon: true, trayIcon: true,
updateFrequency: 500, updateFrequency: 500,
@ -115,6 +117,9 @@ export const settingsStore = new Store({
{ key: settings.advanced.tidalUrl, value: "https://listen.tidal.com" }, { key: settings.advanced.tidalUrl, value: "https://listen.tidal.com" },
]); ]);
}, },
"5.16.0": (migrationStore) => {
buildMigration("5.16.0", migrationStore, [{ key: settings.discord.showIdle, value: "true" }]);
},
}, },
}); });