Compare commits

...

147 Commits
0.5 ... 5.1.0

Author SHA1 Message Date
8d44ff8afb Merge pull request #223 from Mastermindzh/release/5.1.0
Release/5.1.0
2023-04-27 15:30:09 +02:00
bccc979f43 fixed pacman icon 2023-04-27 15:07:40 +02:00
6849952c41 feat: added proper updates through the mediasession api. fixes #198 2023-04-27 14:29:28 +02:00
07be74af9f feat: added custom CSS settings. fixes #213 2023-04-27 14:13:32 +02:00
fc6adc25ca release: docs 2023-04-27 11:35:10 +02:00
4498e8a73e feat: you can now set updateFrequency in the settings window 2023-04-27 11:35:00 +02:00
3d2a9c3992 Merge pull request #219 from thanasistrisp/artists
[Bug]: get multiple artists instead of a single one
2023-04-27 10:51:35 +02:00
af6bfaf55e Merge pull request #222 from mdh34/mdh34-desktop-icon-fix
Fix Linux Desktop Icons
2023-04-27 10:46:44 +02:00
Matt Harris
8bac90e0f1 Fix Linux Icons 2023-04-26 21:34:17 +01:00
Matt Harris
887c75f61a change icon name in desktop file 2023-04-25 21:46:20 +01:00
Thanasis Trispiotis
cde7408cc4 fix: get multiple artists
- in mrpis multiple names are showing
- also at title
- skipping from settings any artist that is present in current artists at Tidal
2023-04-23 22:24:04 +03:00
05b422e045 Merge branch 'master' of github.com:Mastermindzh/tidal-hifi 2023-04-22 21:19:42 +02:00
35289d8216 ci: updated workflow node versions 2023-04-22 21:19:36 +02:00
ea42b79cd8 ci: updated workflow node versions 2023-04-22 21:14:56 +02:00
6d859cf780 chore: updated deps. fixes #203 (kinda.. as much as we can) 2023-04-22 20:57:38 +02:00
af20092053 fix: fixed tray click bug. fixes #196 2023-04-22 20:51:20 +02:00
166ca353cf chore: updating deps 2023-04-22 16:56:23 +02:00
b807aa2f76 Merge pull request #215 from Mastermindzh/dependabot/npm_and_yarn/minimatch-and-electron-builder-3.1.2
Bump minimatch and electron-builder
2023-04-21 11:47:10 +02:00
dependabot[bot]
ef8ffe47f5 Bump minimatch and electron-builder
Bumps [minimatch](https://github.com/isaacs/minimatch) to 3.1.2 and updates ancestor dependency [electron-builder](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder). These dependencies need to be updated together.


Updates `minimatch` from 3.0.4 to 3.1.2
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

Updates `electron-builder` from 23.5.1 to 24.2.1
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/electron-builder/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/v24.2.1/packages/electron-builder)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
- dependency-name: electron-builder
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-21 09:32:10 +00:00
ba7b2a5717 Merge pull request #218 from thanasistrisp/adblocking
Add real Ad blocking using custom ad filters
2023-04-21 11:31:24 +02:00
0d93bedb4d prep v5.0.0, refactored getTrackUrl to use the duplicated getTrackId, and applied some formatting 2023-04-21 11:26:10 +02:00
Thanasis Trispiotis
1de71aa82b update docs 2023-04-21 01:38:20 +03:00
Thanasis Trispiotis
b2e68f5a8f add trackid
- mpris important property (maybe needed sometime)
2023-04-20 20:11:22 +03:00
Thanasis Trispiotis
a2a2023853 exit when restart
- if tray minimize is enabled quit hides the app to tray
2023-04-20 20:09:40 +03:00
Thanasis Trispiotis
26c8a38350 adblocking thanks to custom ad filters
derived from uBlock Origin: https://github.com/uBlockOrigin/uAssets/issues/17495
2023-04-20 20:07:44 +03:00
eb93fbc35d Merge pull request #216 from Mastermindzh/snyk-upgrade-5b1ac3f20e6c8576da25c1dd4616f859
[Snyk] Upgrade sass from 1.58.3 to 1.60.0
2023-04-19 16:49:07 +02:00
snyk-bot
d3c56fa445 fix: upgrade sass from 1.58.3 to 1.60.0
Snyk has created this PR to upgrade sass from 1.58.3 to 1.60.0.

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
2023-04-19 08:58:36 +00:00
6998992011 Merge pull request #210 from Mastermindzh/snyk-upgrade-24c736c74c97f0ad9986633990f52b6f
[Snyk] Upgrade @electron/remote from 2.0.8 to 2.0.9
2023-04-18 10:03:56 +02:00
108e1d65d4 Merge pull request #209 from Mastermindzh/snyk-upgrade-5f032e18d6ad67fa2ee5145171e2630c
[Snyk] Upgrade hotkeys-js from 3.9.4 to 3.10.1
2023-04-18 10:03:38 +02:00
1097f83911 Merge pull request #208 from Mastermindzh/snyk-upgrade-d65f22e3a110c0ac5f17545a20ca2873
[Snyk] Upgrade sass from 1.54.9 to 1.58.3
2023-04-18 10:03:20 +02:00
8c734777cc Merge pull request #204 from Mastermindzh/dependabot/npm_and_yarn/http-cache-semantics-4.1.1
Bump http-cache-semantics from 4.1.0 to 4.1.1
2023-04-18 10:02:54 +02:00
snyk-bot
de17ac6113 fix: upgrade @electron/remote from 2.0.8 to 2.0.9
Snyk has created this PR to upgrade @electron/remote from 2.0.8 to 2.0.9.

See this package in npm:
https://www.npmjs.com/package/@electron/remote

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
2023-03-27 22:16:23 +00:00
snyk-bot
ced41c00d7 fix: upgrade hotkeys-js from 3.9.4 to 3.10.1
Snyk has created this PR to upgrade hotkeys-js from 3.9.4 to 3.10.1.

See this package in npm:
https://www.npmjs.com/package/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
2023-03-27 22:16:17 +00:00
snyk-bot
744016f307 fix: upgrade sass from 1.54.9 to 1.58.3
Snyk has created this PR to upgrade sass from 1.54.9 to 1.58.3.

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
2023-03-27 22:16:13 +00:00
dependabot[bot]
ad8ef71c6b Bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-03 05:12:03 +00:00
d0f9a34f9c Merge pull request #195 from Mastermindzh/feature/4.4.0
Feature/4.4.0
2023-01-22 22:30:43 +01:00
3b316f2301 chore: upping the version numbers 2023-01-22 22:19:07 +01:00
c0d9cd2834 feat: added the ability to skip artists automatically. Takes precedence over muting. fixes #175 2023-01-22 21:33:00 +01:00
0620d87d8b feature: added click handler to tray icon to focus/show tidal-hifi. fixes #193 #143 2023-01-22 21:32:48 +01:00
57b7f9148f feat: Move the quit command from the system sub-menu to the main menu fixes #185 2023-01-20 22:18:25 +01:00
63ccff97ea feature: Add support to autoHide the menubar and showing it with the key. fixes #188 2023-01-20 22:15:36 +01:00
3a4d23738f fix: Reverted icon path to instead of the hardcoded linux path fixes #170 2023-01-20 22:00:19 +01:00
c96bdb0d28 fix: Updated shortcut hint on the menubar to reflect the new shortcut. fixes #174 2023-01-20 21:57:41 +01:00
dependabot[bot]
115d8c6c5c Bump json5 from 2.2.1 to 2.2.3 (#191)
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 21:26:12 +01:00
Marie
cd2a068470 Fix CHANGELOG (#181) 2022-10-25 14:35:07 +02:00
bf260b14e0 https://github.com/Mastermindzh/tidal-hifi/pull/178 (#180)
* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

* Redesign of the settings window (#168)

* Pr dest (#166)

* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>

* Redesign of the settings window

* changed sass to scss, fixed color of switches and disabled rounded corners

Co-authored-by: Rick van Lieshout <info@rickvanlieshout.com>

* - icon is set to new static path based on Arch/Debian
  - Name has changed to Tidal-Hifi

* Check if app is default protocol client before setting (#178)

* Release of settings window and desktop file fixes (#169)

* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

* Redesign of the settings window (#168)

* Pr dest (#166)

* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>

* Redesign of the settings window

* changed sass to scss, fixed color of switches and disabled rounded corners

Co-authored-by: Rick van Lieshout <info@rickvanlieshout.com>

* - icon is set to new static path based on Arch/Debian
  - Name has changed to Tidal-Hifi

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>

* Disable background throttling (#171)

* disable background throttling for consistent setInterval

* add disable throttle as config option

* 4.3.0

* Check if app is default protocol client before setting

Co-authored-by: Rick van Lieshout <info@rickvanlieshout.com>
Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>
Co-authored-by: Cukmekerb <cukmekerb@gmail.com>
Co-authored-by: Brecht Yperman <brecht.yperman@trustbuilder.com>

* docs

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>
Co-authored-by: Brecht Yperman <brecht@yperman.eu>
Co-authored-by: Cukmekerb <cukmekerb@gmail.com>
Co-authored-by: Brecht Yperman <brecht.yperman@trustbuilder.com>
2022-10-24 11:38:00 +02:00
d161a68c95 4.3.0 2022-10-05 19:44:04 +02:00
Cukmekerb
9de8cea50e Disable background throttling (#171)
* disable background throttling for consistent setInterval

* add disable throttle as config option
2022-10-05 19:38:01 +02:00
5f330a7c48 Release of settings window and desktop file fixes (#169)
* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

* Redesign of the settings window (#168)

* Pr dest (#166)

* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>

* Redesign of the settings window

* changed sass to scss, fixed color of switches and disabled rounded corners

Co-authored-by: Rick van Lieshout <info@rickvanlieshout.com>

* - icon is set to new static path based on Arch/Debian
  - Name has changed to Tidal-Hifi

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>
2022-09-25 12:50:41 +02:00
732710c3ef Pr dest (#166)
* Update configuration of the desktop file (#165)

* - Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"

Co-authored-by: Ivo Šmerek <ivo97@centrum.cz>
2022-09-11 22:54:08 +02:00
4941aae950 Bugfix/4.1.1 (#161)
* - Fixed `cannot read property of undefined` error because of not passing mainWindow around.
- vincens2005, fixed inconsistent auto muting

* Fix inconsistent auto-muting (#159)

* fix muting sometimes not working

* fix inconsistent unmuting

* fix bad code in inconsistent muting fig

Co-authored-by: Cukmekerb <cukmekerb@gmail.com>
2022-08-23 21:20:46 +02:00
1439a11969 Feature/4.1.0 (#156)
* added protocol handler

* Switched icon strategies to fix bugs with icons

* fixed tray icon issues

* fixed about :)

* Fixed playback, mpris and API issues
2022-08-07 16:05:48 +02:00
Tomasz Hołubowicz
3a3e0e1a2d Add instruction for installation via Nix (#153) 2022-07-05 16:41:12 +02:00
fa9ab22867 - Updated build config to make use of a base file that doesn't build anything.
- This fixes the issue of unwanted extra build targets that were introduced with the electron-builder update
2022-06-25 21:45:30 +02:00
207a61d199 4.0.0 with electron 19.0.5 release 2022-06-23 17:06:17 +02:00
Tomasz Hołubowicz
7b18322e17 Bump electron version (#152) 2022-06-23 17:00:35 +02:00
8f47756244 fixed hardware media flag upadting gpu rasterization options 2022-06-18 10:40:31 +02:00
Marie
cdcf9431bf Fix bugs related to media info (#150)
* Fix duration time element

* change interval time from 200 to 500
2022-06-18 10:26:21 +02:00
374f3da740 Added a separate advanced options settings panel with flags
- Added gpu-rasterization flag

config setting `disableHardwareMediaKeys` moved to `flags.disableHardwareMediaKeys`, it will be migrated automatically
2022-05-07 18:13:36 +02:00
3965ada0a2 Add link to flathub 2022-04-23 23:18:08 +02:00
79ff02d06c Electron 15, single-instance-lock and setting to disable hardwareMediaKeyHandling (#134)
* Update to Electron 15 and add Flatpak to README (#131)

* Update functional

* Change to 15

* Replace window.hide, window.restart with counterparts

* Fix openexternal by doing default JS

* Change mouse to pointer for <a> elements

* turn mouse to pointer for exit button

* Fix deprecation of Audio for AudioVideo

* Made a small mistake

* Add Flatpak to readme

* 3.0.0 prep

* Added setting to disable multiple tidal-hifi windows (defaults to true)

fixes #121

* Added setting to disable HardwareMediaKeyHandling (defaults to false)

fixes #133

Co-authored-by: Marie <marie@kaifa.ch>
2022-04-23 22:59:32 +02:00
7b2afd2290 added separate build-unpacked config 2022-04-22 00:21:05 +02:00
5fde20ace1 hotfix, downgrade packaged version to 8.5.2 2022-04-21 23:58:48 +02:00
7f5f5e7f62 Merge branch 'master' of github.com:Mastermindzh/tidal-hifi 2022-04-21 17:52:50 +02:00
6a1a1efe74 add apt-get update 2022-04-21 17:52:31 +02:00
94e1bb1780 add apt-get update 2022-04-21 17:51:21 +02:00
d66dd8cc9e 2.8.1 release 2022-04-21 17:49:11 +02:00
Cukmekerb
de97ac8a00 make quit button actually quit app (#123) 2022-04-21 17:46:49 +02:00
Marie
82ac5edf22 Update build version to fit with package.json electron version (#128)
* Update build version to fit with package.json electron version

* Update electron-builder.yml

* Use wvcus.2 for additional fixes

* Also use wvcus.2 for package.json
2022-04-21 17:46:05 +02:00
Bruno Unna
909c8ee8ba Fix the link to donations for kwf (#120)
Co-authored-by: Bruno Unna <bunna@getvisibility.com>
2022-04-11 12:07:55 +02:00
Marie
15b6b13e14 Change tidal-hifi to tidal-hifi-bin for AUR (#118) 2022-04-01 15:41:30 +02:00
89589b75e1 changed muted artists to TIDAL 2022-03-31 17:42:34 +02:00
6a7b3eefd4 Muting artists automatically (#116)
Co-authored-by: Cukmekerb <cukmekerb@gmail.com>
2022-03-31 17:37:12 +02:00
dependabot[bot]
0583c4a188 Bump plist from 3.0.4 to 3.0.5 (#115)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 08:53:36 +02:00
dependabot[bot]
8855e7f89f Bump minimist from 1.2.5 to 1.2.6 (#114)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-26 18:59:52 +01:00
dependabot[bot]
4fbb598c50 Bump node-fetch from 2.6.1 to 2.6.7 (#105)
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-17 16:47:48 +01:00
101fe967d3 version bump and changelog 2022-02-17 12:58:35 +01:00
Marie
53468e0dc3 Fix issue causing the app to crash (#104) 2022-02-17 12:57:26 +01:00
53cecbcd18 added a hotkey on ctrl+0 for settings. fixes #91 2022-01-23 11:02:28 +01:00
5a65f60cc5 Fixed bug: Triggering fullscreen from the Tidal web app would cause the menubar to be visible even if it was disabled in the settings (#96)
Co-authored-by: Diogo Oliveira <dromarques@outlook.com>
2022-01-23 10:55:50 +01:00
d51d5cdc24 Use native electron notifications and album text (#90)
Co-authored-by: Diogo Oliveira <dromarques@outlook.com>
2021-12-28 17:31:10 +01:00
0dec967e71 Add album image to media module and discord (#86)
Co-authored-by: Diogo Oliveira <diogoomarques98@gmail.com>
Co-authored-by: Marie <marie@kaifa.ch>
2021-12-19 13:18:39 +01:00
Kevin Yuan
c940d0991d Generate pacman builds using Github workflows (#81)
* Show "Tidal Hifi" as application name for notify-send

Set the 'app-name' variable for notify-send under Linux

* Generate pacman builds using Github workflows
Previous builds failed due to missing build dependencies when building the package using the Github workflow Linux environment
2021-12-08 18:35:39 +01:00
662ef6ad7b new deps (#77) 2021-12-04 12:51:19 +01:00
5313ab13d3 Merge pull request #76 from Mastermindzh/develop 2021-12-04 11:23:32 +01:00
f43f227191 added changelog and bumped version 2021-12-04 11:23:03 +01:00
Kevin Yuan
ae25d88e94 Show "Tidal Hifi" as application name for notify-send notifications (#75) 2021-12-04 11:21:14 +01:00
5ef6074015 Release 2.4.0 (#74)
Update MPRIS functionality to provide length, artist, and current position.
Also added rescrobbler explanation to the README.

Co-authored-by: Vinay V <cool00geek@yahoo.com>
2021-11-29 22:43:51 +01:00
8fea5265e7 Merge branch 'master' of github.com:Mastermindzh/tidal-hifi 2021-06-17 20:51:15 +02:00
8d2e03ca6b Merge branch 'master' of github.com:Mastermindzh/tidal-hifi 2021-06-17 20:45:33 +02:00
1074de228b Merge branch 'master' of github.com:Mastermindzh/tidal-hifi 2021-06-17 20:44:30 +02:00
Ignacio Brasca
64d1aa4041 Implement minimized action to hide the application on close (#60)
* Implement minimzed action to hide the application on close instead of
closing it.

* Add close event on the tray icon

* Add all items in the menu (not only Show/Close app)

* Changes on mainWindow. Toggle instead of Open/Close pair button
2021-06-17 20:44:23 +02:00
Ignacio Brasca
6608330ed3 Implement minimized action to hide the application on close (#60)
* Implement minimzed action to hide the application on close instead of
closing it.

* Add close event on the tray icon

* Add all items in the menu (not only Show/Close app)

* Changes on mainWindow. Toggle instead of Open/Close pair button
2021-06-17 20:37:14 +02:00
aa562c4a30 Merge branch 'master' of github.com:Mastermindzh/tidal-hifi 2021-05-30 15:40:32 +02:00
d6f63ac560 fixed artists retrieval, removed arch build steps 2021-05-30 15:40:27 +02:00
cdc0f49789 corrected KWF link 2021-04-30 08:55:48 +02:00
2f290f83fd Dev -> master (#49)
Co-authored-by: Cristian <54779545+callapa1@users.noreply.github.com>
2021-04-30 08:55:04 +02:00
d34ddfeb75 new version + readme updates 2021-04-25 18:32:52 +02:00
b7f163c1a1 Merge pull request #43 from MXY-Group/time 2021-04-25 18:23:11 +02:00
Marie
791a92a446 change let to const and use full length as names. 2021-04-22 11:34:26 +02:00
Marie
10c1e57680 extract Switch to function and change variable names. 2021-04-22 11:33:57 +02:00
Marie
c65d1a56c8 add the enableRemoteModule preference as the new castlabs release uses v10 2021-04-22 11:14:49 +02:00
Marie
d8f2dbd0c2 Update castlabs electron to the newest release from 6 days ago. 2021-04-21 17:24:18 +02:00
Marie
43ce85bb28 Change "listening to" to "watching" for videos 2021-04-21 04:56:59 +02:00
Marie
8201e23e4b Forgot to remove the now unused url element. 2021-04-21 04:48:23 +02:00
Marie
3cc288e014 Fix URL fetching 2021-04-21 04:44:44 +02:00
Marie
08ec7fadac Add Video support 2021-04-21 03:31:44 +02:00
Marie
7a30b125ec Add switch case for Video/Song 2021-04-21 03:31:28 +02:00
Marie
cac5db123f fix 1-2 second deflay between switching songs at song end 2021-04-20 23:09:53 +02:00
Marie
59f8b2d0b5 remove durationchanged as it was no longer needed 2021-04-20 22:22:04 +02:00
Marie
81a536bbdb revert last change 2021-04-20 22:15:34 +02:00
Marie
5e952e3899 Fix delay of one second when switching at song end 2021-04-20 22:12:11 +02:00
Marie
df887b8628 add time left to discord rpc & duration, current to mediainfo 2021-04-20 21:56:45 +02:00
Marie
31d90a342c add time left for discord rpc 2021-04-20 21:56:02 +02:00
Marie
8607337580 Merge pull request #2 from Mastermindzh/master
Merge new updates to master
2021-04-20 01:18:16 +02:00
4fe42a3671 update aur version files 2021-04-19 23:39:46 +02:00
148d1746ad Merge pull request #42 from Mastermindzh/feature/improving-discord-integration-with-maroxy
Discord improvements with Mar0xy
2021-04-19 23:18:41 +02:00
ae51f9610c - The discord integration now doesn't send an update every 15 seconds it sends an update whenever the media info changes
- consolidated updating the media info changes with the status changes into a single global event
2021-04-19 23:08:19 +02:00
5eb3b8d95f released version 2.1.0 on the AUR version as well. 2021-04-19 21:31:12 +02:00
ebdae6bc88 bumped a few packages and added a changelog 2021-04-19 20:59:37 +02:00
Marie
ab25bf16b2 Add Discord RPC & fix artist in mediainfo (#40) 2021-04-19 20:43:25 +02:00
31670d0c2b updated aur info 2021-01-10 14:11:45 +01:00
9ca3d3b37d added some extra images, updated the TOC and bumped the ini dependency 2021-01-10 13:53:34 +01:00
fb9082e995 Changes some hotkeys around and fixed some bugs with the hotkeys. Fixes #34 and #35 2021-01-10 13:52:22 +01:00
87a4ff3fc5 Merge pull request #32 from Mastermindzh/feature/mpris-403-fix 2020-11-29 12:32:47 +01:00
1e5b7d61f5 added changelog etc 2020-11-29 12:31:58 +01:00
8177e6e3ca re-enabled mpris after the 403 has been fixed 2020-11-29 10:55:01 +01:00
9f26db22fc updated arch install files. fixes #27 2020-10-07 20:33:39 +02:00
e2ea4d13c4 added the option to disable the system tray icon and fixed the settings menu 2020-10-07 20:10:31 +02:00
c222113cf1 updated arch builds 2020-10-05 10:50:15 +02:00
bdab6c3a17 added homepage attribute for electron-builder.. 2020-10-05 10:38:34 +02:00
813beec863 having pkgbuild run the internal electron-builder 2020-10-05 10:17:27 +02:00
591d42b31a updated arch info 2020-10-04 12:00:19 +02:00
beacedd64b Develop (#25) 2020-10-04 11:52:08 +02:00
dependabot[bot]
e13af7a2d5 Bump yargs-parser from 13.1.1 to 13.1.2 (#21)
Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 13.1.1 to 13.1.2.
- [Release notes](https://github.com/yargs/yargs-parser/releases)
- [Changelog](https://github.com/yargs/yargs-parser/blob/master/docs/CHANGELOG-full.md)
- [Commits](https://github.com/yargs/yargs-parser/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-14 09:12:06 +02:00
73cba7f761 Create stale.yml 2020-08-16 16:30:51 +02:00
71208bbf81 updated the aur info 2020-04-10 11:36:34 +02:00
ef37478788 trying to fix the pipeline, have to test on master because apparantly the master branch always tries to release 2020-04-10 11:07:45 +02:00
c411c2cf85 Updated NPM packages to fix CVE-2020-7598 2020-04-10 10:45:24 +02:00
Steffen Sun Lyng
b6185c3e12 Fix/add readme for local snap install (#10)
* Added more explicit installation guide for snap

* changed a few details about snap installation

* Removed unwanted character
2020-04-09 17:02:49 +02:00
Luka Jankovic
c90902e9a9 RPM support and icns icon (#9)
* rpm support and a new icon

* removed unneeded dependency
2020-03-19 21:26:40 +01:00
e72b607f29 updated aur scripts 2020-02-02 10:17:19 +00:00
d05d8f48b6 v1.0 (#6) 2020-02-01 21:14:34 +01:00
Matthieu Le brazidec
30844cb51b Updated the express api to only listen on localhost (#5) 2020-02-01 20:55:05 +01:00
a59cc16f34 Update README.md 2020-01-18 12:10:49 +01:00
52b32c0783 added arch (AUR) PKGBUILD files
Package is visible on: https://aur.archlinux.org/packages/tidal-hifi-git/
2019-12-02 22:55:10 +01:00
José Augusto Bolina
0636c8b92f added individual build scripts. (#1)
* added individual build scripts.

- squashed + changed commit messages.
2019-12-01 23:02:59 +01:00
53 changed files with 9086 additions and 3336 deletions

View File

@@ -9,20 +9,24 @@ jobs:
build_on_linux:
runs-on: ubuntu-latest
steps:
- name: update apt
run: sudo apt-get update
- name: Install libarchive-tools
run: sudo apt-get install -y libarchive-tools
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: 12
node-version: 19
- run: npm install
- run: npm run build
build_on_mac:
runs-on: macOS-latest
runs-on: macos-latest
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: 12
node-version: 19
- run: npm install
- run: npm run build
@@ -32,6 +36,6 @@ jobs:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: 12
node-version: 19
- run: npm install
- run: npm run build

View File

@@ -9,10 +9,14 @@ jobs:
build_on_linux:
runs-on: ubuntu-latest
steps:
- name: update apt
run: sudo apt-get update
- name: Install libarchive-tools
run: sudo apt-get install -y libarchive-tools
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: 12
node-version: 16
- run: npm install
- run: npm run build
- uses: actions/upload-artifact@master
@@ -26,13 +30,13 @@ jobs:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: 12
node-version: 16
- run: npm install
- run: npm run build
- uses: actions/upload-artifact@master
with:
name: mac-builds
path: dist/
path: ./dist/
build_on_win:
runs-on: windows-latest
@@ -40,7 +44,7 @@ jobs:
- uses: actions/checkout@master
- uses: actions/setup-node@master
with:
node-version: 12
node-version: 16
- run: npm install
- run: npm run build
- uses: actions/upload-artifact@master

12
.gitignore vendored
View File

@@ -1,2 +1,14 @@
node_modules
dist
# ignore all build files except for the .desktop and PKGBUILD files
build/linux/arch/*
!build/linux/arch/PKGBUILD
!build/linux/arch/.SRCINFO
!build/linux/arch/tidal-hifi.desktop
!build/linux/arch/install.sh
*.css
*.css.map
# JetBrains IDE configuration
.idea

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
19.8.1

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"cSpell.words": ["hifi", "rescrobbler", "widevine"]
}

219
CHANGELOG.md Normal file
View File

@@ -0,0 +1,219 @@
# Changelog
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.1.0
### New features
- Added proper updates through the MediaSession API
- You can now add custom CSS in the "advanced" settings tab
- You can now configure the updateFrequency in the settings window
- Default value is set to 500 and will overwrite the hardcoded value of 100
### Fixes
- Any songs **including** an artist listed in the `skipped artists` setting will now be skipped even if the song is a collaboration.
- Linux desktop icons have been fixed. See [#222](https://github.com/Mastermindzh/tidal-hifi/pull/222) for details.
## 5.0.0
- Replaced "muting artists" with a full implementation of an Adblock mechanism
> Disabled audio & visual ads, unlocked lyrics, suggested track, track info, unlimited skips thanks to uBlockOrigin custom filters ([source](https://github.com/uBlockOrigin/uAssets/issues/17495))
- @thanasistrisp updated Electron to 24.1.2 and fixed the tray bug :)
## 4.4.0
- Updated shortcut hint on the menubar to reflect the new `ctrl+=` shortcut.
- Reverted icon path to `icon.png` instead of the hardcoded linux path.
- Add support to autoHide the menubar and showing it with the `alt` key.
- Move the quit command from the system sub-menu to the main menu
- Added single click focus/show on the tray icon
- Doesn't work on all platforms. Nothing I can do about that unfortunately!
- Added a list of artists to automatically skip.
- I don't like the vast majority of dutch music so I added one of them to my list to test: [./docs/no-dutch-music.mp4](./docs/no-dutch-music.mp4)
## 4.3.1
- fix: App always requests a default-url-handler-scheme change on start
## 4.3.0
- Added a setting to disable background throttling ([docs](https://www.electronjs.org/docs/latest/api/browser-window))
## 4.2.0
- New settings window by BlueManCZ
- Fixed the desktop files in electron-builder
- icon is set to new static path based on Arch/Debian
- Name has changed to Tidal-Hifi
## 4.1.2
- Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"
## 4.1.1
- Fixed `cannot read property of undefined` error because of not passing mainWindow around.
- vincens2005, fixed inconsistent auto muting
## 4.1.0
- Added `tidal://` protocol support
- Switched icon strategies to fix bugs with icons
- Fixed tray icon bugs
- Menu now shows in KDE as well
- Toggle window is supported from tray icon
- regular click is still ignored, see [this issue](https://github.com/electron/electron/issues/6773)
- Fixed about tab not showing
- Fixed playback, mpris and API issues
## 4.0.1
- Updated build config to make use of a base file that doesn't build anything.
- This fixes the issue of unwanted extra build targets that were introduced with the electron-builder update
## 4.0.0
- Updated to Electron 19.0.5
## 3.1.1
- Media update timeout set to 500 instead of 200
- Updated property name for duration because of a tidal update
- flag for "disable hardware media keys" now working again
## 3.1.0
- Added a separate advanced options settings panel with flags
- Added gpu-rasterization flag
- config setting `disableHardwareMediaKeys` moved to `flags.disableHardwareMediaKeys`, it will be migrated automatically
## 3.0.0
- Updated to Electron 15
- Fixed the develop "build-unpacked" command
- Added setting to disable multiple tidal-hifi windows (defaults to true)
- Added setting to disable HardwareMediaKeyHandling (defaults to false)
## 2.8.2
- Updated dependencies
- Downgraded packaged version of electron to 8.5.2, doesn't seem to like a newer build
- Fixed the annoying (and useless) terminal warning about `allowRendererProcessReuse`
## 2.8.1
- Mar0xy fixed some build issues (thanks!)
- vincens2005 fixed the quit button in the menubar
## 2.8.0
- Added the ability to mute artists automatically
- Added better error handling for discord rpc
## 2.7.2
- Disabled sandboxing to fix a display compositor issue on Linux.
## 2.7.1
- Fixed bug: Triggering full screen from the Tidal web app would cause the menubar to be visible even if it was disabled in the settings
## 2.7.0
- Switched to the native Notifier (removed node-notifier)
- Album art now also has a name, based on [best effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847)
## 2.6.0
- Add album images to media info and discord
## 2.5.0
- Notify-send now correctly shows "Tidal HiFi" as the program name
- Updated dependencies (including electron itself)
### known issues
- Requires older version of nodejs due to electron-builder (use lts/gallium)
### builds
updated to nodejs 16 in actions
## 2.4.0
- Added more MPRIS settings
- Added instruction for rescrobbler to get last.fm working without sandbox mode
## 2.3.0
- Added a setting to minimize to tray on app close (off by default)
- Added the main menu to the tray icon
## 2.2.1
- artists is now gotten specifically from the footer. This fixes the [unknown artists bug](https://github.com/Mastermindzh/tidal-hifi/issues/45).
- the discord module will check whether the artists is empty and if so substitute it with a default message. This is to prevent sending an empty state to Discord (which it doesn't support). fixes [#45](https://github.com/Mastermindzh/tidal-hifi/issues/54)
### removed arch build details from source control
moved to: [https://github.com/Mastermindzh/tidal-hifi-aur](https://github.com/Mastermindzh/tidal-hifi-aur)
## 2.2.0
- The discord integration now adds a time remaining field based on the song duration
- All fields (current, remaining, and url are also available in the API\*)
- the artist field is now correctly identified
\* current time only updates on play/pause.
## 2.1.1
- The discord integration now doesn't send an update every 15 seconds it sends an update whenever the media info changes
- consolidated updating the media info changes with the status changes into a single global event
## 2.1.0
- [Mar0xy](https://github.com/Mar0xy) added Discord integration.
- Several versions have been bumped to fix vulnerabilities
## 2.0.0
### Breaking changes
- Changed settings hotkey from "ctrl+/" to "ctrl+=" to avoid a conflict with the default Tidal hotkeys
## Other changes
- Added a setting to disable custom hotkeys
- Fixed the bug that the previous song hotkey would register 3 times. (Twice due to a duplicate block of code + once from the default tidal hotkey)
## 1.3.0
-- re-enabled MPRIS-service wit the electron downloader fixes
## 1.2.0
- Added the ability to disable the tray icon
## 1.1.1
Bugfixes:
- Arch AUR install failed before, it is fixed now by using the included build scripts
## 1.1.0
- updated to electron 8.0.0
- Added a beta-version of the MPRIS service
- Bugfixes:
- icon on gnome not showing in launcher
- app not remembering size on startup

120
README.md
View File

@@ -1,55 +1,131 @@
<h1>
Tidal-hifi
<img src = "./build/icon.png" height="40" align="right" />
</h1>
# Tidal-hifi<img src = "./build/icon.png" height="40" align="right"/>
The web version of [listen.tidal.com](listen.tidal.com) running in electron with hifi support thanks to widevine.
![GitHub release](https://img.shields.io/github/release/Mastermindzh/tidal-hifi.svg)
The web version of [listen.tidal.com](https://listen.tidal.com) running in electron with hifi support thanks to widevine.
![tidal-hifi preview](./docs/preview.png)
## Table of contents
## Table of Contents
<!-- toc -->
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Using releases](#using-releases)
- [Snap](#snap)
- [Arch Linux](#arch-linux)
- [Flatpak](#flatpak)
- [Nix](#nix)
- [Using source](#using-source)
- [features](#features)
- [Features](#features)
- [Integrations](#integrations)
- [Known bugs](#known-bugs)
- [last.fm doesn't work out of the box. Use rescrobbler as a workaround](#lastfm-doesnt-work-out-of-the-box-use-rescrobbler-as-a-workaround)
- [Why](#why)
- [Why not extend existing projects?](#why-not-extend-existing-projects)
- [Special thanks to..](#special-thanks-to)
- [Special thanks to](#special-thanks-to)
- [Buy me a coffee? Please don't](#buy-me-a-coffee-please-dont)
- [Images](#images)
- [Settings window](#settings-window)
- [User setups](#user-setups)
<!-- tocstop -->
## Installation
### Dependencies
Note that you **need** a notification library such as [libnotify](https://github.com/GNOME/libnotify) or [dunst](https://github.com/dunst-project/dunst) in order for the software to work properly.
### Using releases
Various packaged versions of the software are available on the [releases](https://github.com/Mastermindzh/tidal-hifi/releases) tab.
### Snap
To install with `snap` you need to download the pre-packaged snap-package from this repository, found under releases:
1. Download
```sh
wget <URI> #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap
```
2. Install
```sh
snap install --dangerous <path> #for instance: tidal-hifi_1.0.0_amd64.snap
```
### Arch Linux
Arch Linux users can use the AUR to install tidal-hifi:
```sh
trizen tidal-hifi-bin
```
### Flatpak
To install via [Flatpak](https://flathub.org/apps/details/com.mastermindzh.tidal-hifi) run the following command:
```sh
flatpak install flathub com.mastermindzh.tidal-hifi
```
### Nix
To install with Nix run the following command:
```sh
nix-env -iA nixpkgs.tidal-hifi
```
### Using source
To install and work with the code on this project follow these steps:
- git clone https://github.com/Mastermindzh/tidal-hifi.git
- git clone [https://github.com/Mastermindzh/tidal-hifi.git](https://github.com/Mastermindzh/tidal-hifi.git)
- cd tidal-hifi
- npm install
- npm start
## features
## Features
- HiFi playback
- Notifications
- Shortcuts ([source](https://defkey.com/tidal-desktop-shortcuts))
- Custom hotkeys ([source](https://defkey.com/tidal-desktop-shortcuts))
- API for status and playback
- [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+/`)
- Tray(/mini) player (coming soon)
- Disabled audio & visual ads, unlocked lyrics, suggested track, track info, and unlimited skips thanks to uBlockOrigin custom filters ([source](https://github.com/uBlockOrigin/uAssets/issues/17495))
- Custom [integrations](#integrations)
- [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+=` or `ctrl+0`)
- AlbumArt in integrations ([best-effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847))
## Integrations
Tidal-hifi comes with several integrations out of the box.
You can find these in the settings menu (`ctrl + =` by default) under the "integrations" tab.
![integrations menu, showing a list of integrations](./docs/integrations.png)
It currently includes:
- MPRIS - MPRIS media player controls/status
- Discord - Shows what you're listening to on Discord.
Not included:
- [i3 blocks config](https://github.com/Mastermindzh/dotfiles/commit/9714b2fa1d670108ce811d5511fd3b7a43180647) - My dotfiles where I use this app to fetch currently playing music (direct commit)
### Known bugs
#### last.fm doesn't work out of the box. Use rescrobbler as a workaround
The last.fm login doesn't work, as is evident from the following issue: [Last.fm login doesn't work](https://github.com/Mastermindzh/tidal-hifi/issues/4).
However, in that same issue you can read about a workaround using [rescrobbler](https://github.com/InputUsername/rescrobbled).
For now that will be the default workaround.
## Why
I moved from Spotify over to Tidal and found Linux support to be lacking.
@@ -67,7 +143,23 @@ Whilst there are a handful of projects attempting to run Tidal on Electron they
Sometimes it's just easier to start over, cover my own needs and then making it available to the public :)
## Special thanks to..
## Special thanks to
- [Castlabs](https://castlabs.com/)
For maintaining Electron with Widevine CDM installation, Verified Media Path (VMP), and persistent licenses (StorageID)
## Buy me a coffee? Please don't
Instead spend some money on a charity I care for: [kwf.nl](https://www.kwf.nl/donatie/donation).
Inspired by [haydenjames' issue](https://github.com/Mastermindzh/tidal-hifi/issues/27#issuecomment-704198429)
## Images
### Settings window
![settings window](./docs/settings-preview.png)
### User setups
Some of our users are kind enough to share their usage pictures.
If you want to see them or possibly even add one please do so in the following issue: [#3 - image thread](https://github.com/Mastermindzh/tidal-hifi/issues/3).

BIN
assets/TIDAL.icns Executable file

Binary file not shown.

BIN
assets/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/icons/16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
assets/icons/22x22.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
assets/icons/24x24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
assets/icons/256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
assets/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
assets/icons/384x384.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
assets/icons/48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/icons/64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,40 @@
appId: com.rickvanlieshout.tidal-hifi
electronVersion: 24.1.2
electronDownload:
version: 24.1.2+wvcus
mirror: https://github.com/castlabs/electron-releases/releases/download/v
snap:
plugs:
- default
- screen-inhibit-control
linux:
category: AudioVideo
icon: assets/icons
target:
- dir
executableName: tidal-hifi
desktop:
Encoding: UTF-8
Name: TIDAL Hi-Fi
GenericName: TIDAL Hi-Fi
Comment: The web version of listen.tidal.com running in electron with hifi support thanks to widevine.
Icon: tidal-hifi
StartupNotify: true
Terminal: false
Type: Application
Categories: Network;Application;AudioVideo;Audio;Video
StartupWMClass: tidal-hifi
X-PulseAudio-Properties: media.role=music
MimeType: x-scheme-handler/tidal;
mac:
category: public.app-category.entertainment
win:
icon: icon.png
artifactName: "tidalhifi"
appId: com.rickvanlieshout.tidalhifi
executableName: tidalhifi
protocols:
name: "tidal"
role: "Viewer"
schemes: ["tidal"]

View File

@@ -0,0 +1,4 @@
extends: ./build/electron-builder.base.yml
linux:
target:
- deb

View File

@@ -0,0 +1,4 @@
extends: ./build/electron-builder.base.yml
linux:
target:
- pacman

View File

@@ -0,0 +1,4 @@
extends: ./build/electron-builder.base.yml
linux:
target:
- rpm

View File

@@ -0,0 +1,4 @@
extends: ./build/electron-builder.base.yml
linux:
target:
- snap

View File

@@ -0,0 +1,4 @@
extends: ./build/electron-builder.base.yml
linux:
target:
- dir

View File

@@ -1,19 +1,16 @@
appId: com.rickvanlieshout.tidal-hifi
electronDownload:
mirror: https://github.com/castlabs/electron-releases/releases/download/v
snap:
plugs:
- default
- screen-inhibit-control
extends: ./build/electron-builder.base.yml
linux:
category: Audio
target:
# - pacman
- pacman
- tar.gz
- deb
- rpm
- AppImage
- snap
mac:
category: public.app-category.entertainment
- freebsd
win:
target: msi
icon: icon.png
artifactName: "tidalhifi"
appId: com.rickvanlieshout.tidalhifi
executableName: tidalhifi

BIN
build/icon-inverted.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
build/icon.icns Executable file

Binary file not shown.

BIN
docs/integrations.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/no-dutch-music.mp4 Normal file

Binary file not shown.

BIN
docs/settings-preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

9681
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,25 @@
{
"name": "tidal-hifi",
"version": "0.1.0",
"version": "5.1.0",
"description": "Tidal on Electron with widevine(hifi) support",
"main": "src/main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder -c ./build/electron-builder.yml",
"build-wl": "electron-builder -c ./build/electron-builder.yml -wl",
"build-mac": "electron-builder -c ./build/electron-builder.yml -m"
"build": "npm run builder -- -c ./build/electron-builder.yml",
"build-deb": "npm run builder -- -c ./build/electron-builder.deb.yml",
"build-unpacked": "npm run builder -- -c ./build/electron-builder.unpacked.yml",
"build-rpm": "npm run builder -- -c ./build/electron-builder.rpm.yml",
"build-snap": "npm run builder -- -c ./build/electron-builder.snap.yml",
"build-arch": "npm run builder -- -c ./build/electron-builder.pacman.yml",
"build-wl": "npm run builder -- -c ./build/electron-builder.yml -wl",
"build-mac": "npm run builder -- -c ./build/electron-builder.yml -m",
"build-base": "npm run builder -- -c ./build/electron-builder.base.yml",
"prestart": "npm run sass",
"prebuilder": "npm run sass",
"builder": "electron-builder --publish=never",
"sass": "sass ./src/pages/settings/settings.scss ./src/pages/settings/settings.css",
"sass-lint": "sass-lint -vc ./sass-lint.yml ./src/pages/settings/settings.scss",
"sass-lint-fix": "sass-lint-auto-fix ./src/pages/settings/settings.scss --config-sass-lint ./sass-lint.yml"
},
"keywords": [
"electron",
@@ -16,20 +28,27 @@
"linux"
],
"author": "Rick van Lieshout <info@rickvanlieshout.com> (http://rickvanlieshout.com)",
"homepage": "https://github.com/Mastermindzh/tidal-hifi",
"license": "MIT",
"dependencies": {
"electron-store": "^5.1.0",
"express": "^4.17.1",
"hotkeys-js": "^3.7.1",
"node-notifier": "^6.0.0",
"request": "^2.88.0"
"@electron/remote": "^2.0.9",
"discord-rpc": "^4.0.1",
"electron-store": "^8.1.0",
"express": "^4.18.2",
"hotkeys-js": "^3.10.2",
"mpris-service": "^2.1.2",
"request": "^2.88.2",
"sass": "^1.62.0"
},
"devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0",
"electron": "https://github.com/castlabs/electron-releases#v6.1.0-wvvmp",
"electron-builder": "^21.2.0",
"electron-reload": "^1.5.0",
"prettier": "^1.18.2"
"electron": "git+https://github.com/castlabs/electron-releases.git#v24.1.2+wvcus",
"electron-builder": "^24.2.1",
"js-yaml": "^4.1.0",
"markdown-toc": "^1.2.0",
"prettier": "^2.8.7",
"sass-lint": "^1.13.1",
"sass-lint-auto-fix": "^0.21.2"
},
"prettier": "@mastermindzh/prettier-config"
}
}

21
sass-lint.yml Normal file
View File

@@ -0,0 +1,21 @@
rules:
property-sort-order:
- 1
- order: "smacss"
class-name-format:
- 1
- convention: "hyphenatedbem"
quotes:
- 1
- style: "double"
nesting-depth:
- 1
- max-depth: 3
placeholder-in-extend:
- 0
no-vendor-prefixes:
- 0
empty-line-between-blocks:
- 0
force-pseudo-nesting:
- 0

6
src/constants/flags.js Normal file
View File

@@ -0,0 +1,6 @@
const flags = {
gpuRasterization: [{ flag: "enable-gpu-rasterization", value: undefined }],
disableHardwareMediaKeys: [{ flag: "disable-features", value: "HardwareMediaKeyHandling" }],
};
module.exports = flags;

View File

@@ -6,8 +6,8 @@ const globalEvents = {
previous: "previous",
updateInfo: "update-info",
hideSettings: "hideSettings",
refreshMenuBar: "refreshMenubar",
showSettings: "showSettings",
updateStatus: "update-status",
storeChanged: "storeChanged",
error: "error",
};

View File

@@ -9,14 +9,31 @@
* windowBounds: { width: 800, height: 600 },
*/
const settings = {
notifications: "notifications",
adBlock: "adBlock",
api: "api",
menuBar: "menuBar",
playBackControl: "playBackControl",
apiSettings: {
root: "apiSettings",
port: "apiSettings.port",
},
customCSS: "customCSS",
disableBackgroundThrottle: "disableBackgroundThrottle",
disableHardwareMediaKeys: "disableHardwareMediaKeys",
enableCustomHotkeys: "enableCustomHotkeys",
enableDiscord: "enableDiscord",
flags: {
disableHardwareMediaKeys: "flags.disableHardwareMediaKeys",
gpuRasterization: "flags.gpuRasterization",
},
menuBar: "menuBar",
minimizeOnClose: "minimizeOnClose",
mpris: "mpris",
notifications: "notifications",
playBackControl: "playBackControl",
singleInstance: "singleInstance",
skipArtists: "skipArtists",
skippedArtists: "skippedArtists",
trayIcon: "trayIcon",
updateFrequency: "updateFrequency",
windowBounds: {
root: "windowBounds",
width: "windowBounds.width",

3
src/constants/values.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
name: "tidal-hifi",
};

View File

@@ -1,4 +1,13 @@
const { app, BrowserWindow, globalShortcut, ipcMain } = require("electron");
require("@electron/remote/main").initialize();
const {
app,
BrowserWindow,
components,
globalShortcut,
ipcMain,
protocol,
session,
} = require("electron");
const {
settings,
store,
@@ -9,24 +18,66 @@ const {
} = require("./scripts/settings");
const { addTray, refreshTray } = require("./scripts/tray");
const { addMenu } = require("./scripts/menu");
const path = require("path");
const tidalUrl = "https://listen.tidal.com";
const expressModule = require("./scripts/express");
const mediaKeys = require("./constants/mediaKeys");
const mediaInfoModule = require("./scripts/mediaInfo");
const discordModule = require("./scripts/discord");
const globalEvents = require("./constants/globalEvents");
const flagValues = require("./constants/flags");
let mainWindow;
let icon = path.join(__dirname, "../assets/icon.png");
const PROTOCOL_PREFIX = "tidal";
setFlags();
function setFlags() {
const flags = store.get().flags;
if (flags) {
for (const [key, value] of Object.entries(flags)) {
if (value) {
flagValues[key].forEach((flag) => {
console.log(`enabling command line switch ${flag.flag} with value ${flag.value}`);
app.commandLine.appendSwitch(flag.flag, flag.value);
});
}
}
}
/**
* Fix Display Compositor issue.
*/
app.commandLine.appendSwitch("disable-seccomp-filter-sandbox");
}
/**
* Enable live reload in development builds
* Update the menuBarVisbility according to the store value
*
*/
if (!app.isPackaged) {
require("electron-reload")(`${__dirname}`, {
electron: require(`${__dirname}/../node_modules/electron`),
});
function syncMenuBarWithStore() {
const fixedMenuBar = store.get(settings.menuBar);
mainWindow.autoHideMenuBar = !fixedMenuBar;
mainWindow.setMenuBarVisibility(fixedMenuBar);
}
/**
* Determine whether the current window is the main window
* if singleInstance is requested.
* If singleInstance isn't requested simply return true
* @returns true if singInstance is not requested, otherwise true/false based on whether the current window is the main window
*/
function isMainInstanceOrMultipleInstancesAllowed() {
if (store.get(settings.singleInstance)) {
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
return false;
}
}
return true;
}
function createWindow(options = {}) {
@@ -34,29 +85,43 @@ function createWindow(options = {}) {
mainWindow = new BrowserWindow({
x: options.x,
y: options.y,
width: 1024,
height: 800,
width: store && store.get(settings.windowBounds.width),
height: store && store.get(settings.windowBounds.height),
icon,
tray: true,
backgroundColor: options.backgroundColor,
autoHideMenuBar: true,
webPreferences: {
affinity: "window",
sandbox: false,
preload: path.join(__dirname, "preload.js"),
plugins: true,
devTools: !app.isPackaged,
devTools: true, // I like tinkering, others might too
},
});
mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
require("@electron/remote/main").enable(mainWindow.webContents);
registerHttpProtocols();
syncMenuBarWithStore();
// load the Tidal website
mainWindow.loadURL(tidalUrl);
if (store.get(settings.disableBackgroundThrottle)) {
// prevent setInterval lag
mainWindow.webContents.setBackgroundThrottling(false);
}
// run stuff after first load
mainWindow.webContents.once("did-finish-load", () => {});
mainWindow.on("close", function (event) {
if (!app.isQuiting && store.get(settings.minimizeOnClose)) {
event.preventDefault();
mainWindow.hide();
refreshTray(mainWindow);
}
return false;
});
// Emitted when the window is closed.
mainWindow.on("closed", function() {
mainWindow.on("closed", function () {
closeSettingsWindow();
app.quit();
});
@@ -67,6 +132,15 @@ function createWindow(options = {}) {
});
}
function registerHttpProtocols() {
protocol.registerHttpProtocol(PROTOCOL_PREFIX, (request, _callback) => {
mainWindow.loadURL(`${tidalUrl}/${request.url.substring(PROTOCOL_PREFIX.length + 3)}`);
});
if (!app.isDefaultProtocolClient(PROTOCOL_PREFIX)) {
app.setAsDefaultProtocolClient(PROTOCOL_PREFIX);
}
}
function addGlobalShortcuts() {
Object.keys(mediaKeys).forEach((key) => {
globalShortcut.register(`${key}`, () => {
@@ -78,17 +152,32 @@ function addGlobalShortcuts() {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", () => {
createWindow();
addMenu();
createSettingsWindow();
addGlobalShortcuts();
addTray({ icon });
refreshTray();
store.get(settings.api) && expressModule.run(mainWindow);
app.on("ready", async () => {
if (isMainInstanceOrMultipleInstancesAllowed()) {
await components.whenReady();
// Adblock
if (store.get(settings.adBlock)) {
const filter = { urls: ["https://listen.tidal.com/*"] };
session.defaultSession.webRequest.onBeforeRequest(filter, (details, callback) => {
if (details.url.match(/\/users\/.*\d\?country/)) callback({ cancel: true });
else callback({ cancel: false });
});
}
createWindow();
addMenu(mainWindow);
createSettingsWindow();
addGlobalShortcuts();
store.get(settings.trayIcon) && addTray(mainWindow, { icon }) && refreshTray();
store.get(settings.api) && expressModule.run(mainWindow);
store.get(settings.enableDiscord) && discordModule.initRPC();
} else {
app.quit();
}
});
app.on("activate", function() {
app.on("activate", function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
@@ -96,26 +185,36 @@ app.on("activate", function() {
}
});
// IPC
app.on("browser-window-created", (_, window) => {
require("@electron/remote/main").enable(window.webContents);
});
ipcMain.on(globalEvents.updateInfo, (event, arg) => {
// IPC
ipcMain.on(globalEvents.updateInfo, (_event, arg) => {
mediaInfoModule.update(arg);
});
ipcMain.on(globalEvents.hideSettings, (event, arg) => {
ipcMain.on(globalEvents.hideSettings, (_event, _arg) => {
hideSettingsWindow();
});
ipcMain.on(globalEvents.showSettings, (event, arg) => {
ipcMain.on(globalEvents.showSettings, (_event, _arg) => {
showSettingsWindow();
});
ipcMain.on(globalEvents.updateStatus, (event, arg) => {
mediaInfoModule.updateStatus(arg);
});
ipcMain.on(globalEvents.storeChanged, (event, arg) => {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
ipcMain.on(globalEvents.refreshMenuBar, (_event, _arg) => {
syncMenuBarWithStore();
});
ipcMain.on(globalEvents.error, (event, arg) => {
ipcMain.on(globalEvents.storeChanged, (_event, _arg) => {
syncMenuBarWithStore();
if (store.get(settings.enableDiscord) && !discordModule.rpc) {
discordModule.initRPC();
} else if (!store.get(settings.enableDiscord) && discordModule.rpc) {
discordModule.unRPC();
}
});
ipcMain.on(globalEvents.error, (event, _arg) => {
console.log(event);
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,47 +1,75 @@
let notifications;
let playBackControl;
let api;
let port;
let menuBar;
let adBlock,
api,
customCSS,
disableBackgroundThrottle,
disableHardwareMediaKeys,
enableCustomHotkeys,
enableDiscord,
gpuRasterization,
menuBar,
minimizeOnClose,
mpris,
notifications,
playBackControl,
port,
singleInstance,
skipArtists,
skippedArtists,
trayIcon,
updateFrequency;
const { store, settings } = require("./../../scripts/settings");
const { ipcRenderer } = require("electron");
const globalEvents = require("./../../constants/globalEvents");
const remote = require("@electron/remote");
const { app } = remote;
/**
* Sync the UI forms with the current settings
*/
function refreshSettings() {
adBlock.checked = store.get(settings.adBlock);
api.checked = store.get(settings.api);
customCSS.value = store.get(settings.customCSS);
disableBackgroundThrottle.checked = store.get("disableBackgroundThrottle");
disableHardwareMediaKeys.checked = store.get(settings.flags.disableHardwareMediaKeys);
enableCustomHotkeys.checked = store.get(settings.enableCustomHotkeys);
enableDiscord.checked = store.get(settings.enableDiscord);
gpuRasterization.checked = store.get(settings.flags.gpuRasterization);
menuBar.checked = store.get(settings.menuBar);
minimizeOnClose.checked = store.get(settings.minimizeOnClose);
mpris.checked = store.get(settings.mpris);
notifications.checked = store.get(settings.notifications);
playBackControl.checked = store.get(settings.playBackControl);
api.checked = store.get(settings.api);
port.value = store.get(settings.apiSettings.port);
menuBar.checked = store.get(settings.menuBar);
singleInstance.checked = store.get(settings.singleInstance);
skipArtists.checked = store.get(settings.skipArtists);
skippedArtists.value = store.get(settings.skippedArtists).join("\n");
trayIcon.checked = store.get(settings.trayIcon);
updateFrequency.value = store.get(settings.updateFrequency);
}
/**
* Open an url in the default browsers
*/
window.openExternal = function(url) {
function openExternal(url) {
const { shell } = require("electron");
shell.openExternal(url);
};
}
/**
* hide the settings window
*/
window.hide = function() {
function hide() {
ipcRenderer.send(globalEvents.hideSettings);
};
}
/**
* Restart tidal-hifi after changes
*/
window.restart = function() {
const remote = require("electron").remote;
remote.app.relaunch();
remote.app.exit(0);
};
function restart() {
app.relaunch();
app.exit();
}
/**
* Bind UI components to functions after DOMContentLoaded
@@ -51,8 +79,16 @@ window.addEventListener("DOMContentLoaded", () => {
return document.getElementById(id);
}
document.getElementById("close").addEventListener("click", hide);
document.getElementById("restart").addEventListener("click", restart);
document.querySelectorAll(".external-link").forEach((elem) =>
elem.addEventListener("click", function (event) {
openExternal(event.target.getAttribute("data-url"));
})
);
function addInputListener(source, key) {
source.addEventListener("input", function(event, data) {
source.addEventListener("input", function (_event, _data) {
if (this.value === "on") {
store.set(key, source.checked);
} else {
@@ -62,25 +98,60 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
function addTextAreaListener(source, key) {
source.addEventListener("input", function (_event, _data) {
store.set(key, source.value.split("\n"));
ipcRenderer.send(globalEvents.storeChanged);
});
}
ipcRenderer.on("refreshData", () => {
refreshSettings();
});
ipcRenderer.on("goToTab", (event, tab) => {
ipcRenderer.on("goToTab", (_, tab) => {
document.getElementById(tab).click();
});
adBlock = get("adBlock");
api = get("apiCheckbox");
customCSS = get("customCSS");
disableBackgroundThrottle = get("disableBackgroundThrottle");
disableHardwareMediaKeys = get("disableHardwareMediaKeys");
enableCustomHotkeys = get("enableCustomHotkeys");
enableDiscord = get("enableDiscord");
gpuRasterization = get("gpuRasterization");
menuBar = get("menuBar");
minimizeOnClose = get("minimizeOnClose");
mpris = get("mprisCheckbox");
notifications = get("notifications");
playBackControl = get("playBackControl");
api = get("apiCheckbox");
port = get("port");
menuBar = get("menuBar");
trayIcon = get("trayIcon");
skipArtists = get("skipArtists");
skippedArtists = get("skippedArtists");
singleInstance = get("singleInstance");
updateFrequency = get("updateFrequency");
refreshSettings();
addInputListener(adBlock, settings.adBlock);
addInputListener(api, settings.api);
addTextAreaListener(customCSS, settings.customCSS);
addInputListener(disableBackgroundThrottle, settings.disableBackgroundThrottle);
addInputListener(disableHardwareMediaKeys, settings.flags.disableHardwareMediaKeys);
addInputListener(enableCustomHotkeys, settings.enableCustomHotkeys);
addInputListener(enableDiscord, settings.enableDiscord);
addInputListener(gpuRasterization, settings.flags.gpuRasterization);
addInputListener(menuBar, settings.menuBar);
addInputListener(minimizeOnClose, settings.minimizeOnClose);
addInputListener(mpris, settings.mpris);
addInputListener(notifications, settings.notifications);
addInputListener(playBackControl, settings.playBackControl);
addInputListener(api, settings.api);
addInputListener(port, settings.apiSettings.port);
addInputListener(menuBar, settings.menuBar);
addInputListener(skipArtists, settings.skipArtists);
addTextAreaListener(skippedArtists, settings.skippedArtists);
addInputListener(singleInstance, settings.singleInstance);
addInputListener(trayIcon, settings.trayIcon);
addInputListener(updateFrequency, settings.updateFrequency);
});

View File

@@ -2,399 +2,302 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<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" />
</head>
<body>
<div class="header">
<h1 class="title">Settings</h1>
<a href="javascript:hide();" class="exitWindow">
<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
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" />
</svg>
</a>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 348.333 348.334">
<g>
<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" />
</svg>
<main class="settings">
<input type="radio" name="tab" id="general" checked />
<label for="general">General</label>
</a>
</div>
<div class="body">
<div class="tabset">
<!-- Tab 1 -->
<input type="radio" name="tabset" id="tab1" checked />
<label for="tab1">General</label>
<!-- Tab 2 -->
<input type="radio" name="tabset" id="tab2" />
<label for="tab2">Api</label>
<!-- Tab 3 -->
<input type="radio" name="tabset" id="tab3" />
<label for="tab3">About</label>
<input type="radio" name="tab" id="api" />
<label for="api">API</label>
<div class="tab-panels">
<section id="general" class="tab-panel">
<div class="section">
<h3>Playback</h3>
<div class="option">
<h4>Notifications</h4>
<p>
Whether to show a notification when a new song starts.
</p>
<label class="switch">
<input id="notifications" type="checkbox">
<span class="slider round"></span>
</label>
</div>
</div>
<div class="section">
<h3>UI</h3>
<div class="option">
<h4>Menubar</h4>
<p>
Show Tidal-hifi's menu bar
</p>
<label class="switch">
<input id="menuBar" type="checkbox">
<span class="slider round"></span>
</label>
</div>
</div>
</section>
<section id="api" class="tab-panel">
<div class="section">
<h3>Api</h3>
<p style="margin-bottom: 15px;">
Tidal-hifi has a web api built in to allow users to get current song information. You can optionally enable playback control as well.
<br />
<br />
<small>* api changes require a restart to update</small>
</p>
<input type="radio" name="tab" id="integrations" />
<label for="integrations">Integrations</label>
<div class="option">
<h4>Web API</h4>
<p>
Whether to enable the Tidal-hifi web api
</p>
<label class="switch">
<input id="apiCheckbox" type="checkbox">
<span class="slider round"></span>
</label>
</div>
<div class="option">
<h4 style="margin-bottom: 5px;">API port</h4>
<input id="port" type="text" class="freeTextInput" name="port">
</div>
<div class="option">
<h4>Playback control</h4>
<p>
Whether to enable playback control from the api
</p>
<label class="switch">
<input id="playBackControl" type="checkbox">
<span class="slider round"></span>
</label>
</div>
</div>
<button onClick="restart()">Restart Tidal-hifi</button>
</section>
<section id="general" class="tab-panel">
<div class="section">
<img style="width: 100px; height: auto; display: block; margin: 0 auto; margin-bottom: 20px; margin-top: 20px;" src = "./icon.png">
<p style="max-width: 350px; display:block; margin: 0 auto; text-align: center;">
<a href ="javascript:openExternal('https://github.com/Mastermindzh/tidal-hifi');">Tidal-hifi</a> is made by <a href ="javascript:openExternal('https://www.rickvanlieshout.com')">Rick van Lieshout</a>.<br />
It uses <a href="javascript:openExternal('https://castlabs.com/');">castlabs</a> versions of Electron for widevine support.
</p>
<input type="radio" name="tab" id="advanced" />
<label for="advanced">Advanced</label>
</div>
</section>
<input type="radio" name="tab" id="about" />
<label for="about">About</label>
<div class="tabs">
<section id="general-section" class="tabs__section">
<div class="group">
<p class="group__title">Playback</p>
<div class="group__option">
<div class="group__description">
<h4>Notifications</h4>
<p>Show a notification when a new song starts.</p>
</div>
<label class="switch">
<input id="notifications" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
</div>
<div class="group__option">
<div class="group__description">
<h4>Skip Artists automatically</h4>
<p>The following list of artists (1 per line) will be skipped automatically.</p>
</div>
<label class="switch">
<input id="skipArtists" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<textarea id="skippedArtists" class="textarea" cols="40" rows="5" spellcheck="false"></textarea>
<div class="group__option">
<div class="group__description">
<h4>Block ads</h4>
<p>
Disabled audio & visual ads, unlocked lyrics, suggested track, track info,
unlimited skips
</p>
</div>
<label class="switch">
<input id="adBlock" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
<div class="group">
<p class="group__title">UI</p>
<div class="group__option">
<div class="group__description">
<h4>Fixed menubar</h4>
<p>Always show TIDAL Hi-Fi's menu bar.</p>
</div>
<label class="switch">
<input id="menuBar" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
<div class="group">
<p class="group__title">System</p>
<div class="group__option">
<div class="group__description">
<h4>Tray icon</h4>
<p>Show TIDAL Hi-Fi's tray icon.</p>
</div>
<label class="switch">
<input id="trayIcon" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Minimize on Close</h4>
<p>Minimize window on close instead.</p>
</div>
<label class="switch">
<input id="minimizeOnClose" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<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>.
</p>
</div>
<label class="switch">
<input id="enableCustomHotkeys" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Single instance</h4>
<p>Prevent opening multiple TIDAL Hi-Fi's instances.</p>
</div>
<label class="switch">
<input id="singleInstance" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
</section>
<section id="api-section" class="tabs__section">
<div class="group">
<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 song information.
You can optionally enable playback control as well.
</p>
</div>
<div class="group__option">
<div class="group__description">
<h4>Web API</h4>
<p>Enable the TIDAL Hi-Fi web API.</p>
</div>
<label class="switch">
<input id="apiCheckbox" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<label for="port">API port</label>
<input id="port" type="number" class="text-input" name="port" />
</div>
</div>
<div class="group__option">
<div class="group__description">
<h4>Playback control</h4>
<p>Enable playback control from the web API.</p>
</div>
<label class="switch">
<input id="playBackControl" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
</section>
<section id="integrations-section" class="tabs__section">
<div class="group">
<p class="group__title">Integrations</p>
<div class="group__description">
<p>
TIDAL Hi-Fi is extensible through the use of integrations. You can enable or
disable them here.
</p>
</div>
<div class="group__option">
<div class="group__description">
<h4>MPRIS</h4>
<p>
Enable MPRIS interface which provides a mechanism for discovery, querying and
basic playback control on Linux systems.
</p>
</div>
<label class="switch">
<input id="mprisCheckbox" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Discord RPC</h4>
<p>Show what you're listening to on Discord.</p>
</div>
<label class="switch">
<input id="enableDiscord" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
</section>
<section id="advanced-section" class="tabs__section">
<div class="group">
<p class="group__title">Settings</p>
<div class="group__option">
<div class="group__description">
<h4>Update frequency</h4>
<p>
The amount of time, in milliseconds, that tidal-hifi 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" />
</div>
</div>
<div class="group__option">
<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.
</p>
</div>
</div>
</div>
<textarea id="customCSS" class="textarea" cols="40" rows="8" spellcheck="false"></textarea>
<div class="group">
<p class="group__title">Flags</p>
<div class="group__option">
<div class="group__description">
<h4>Disable hardware built-in media keys</h4>
<p>
Also prevents certain desktop environments from recognizing the chrome MPRIS
client separately from the custom MPRIS client.
</p>
</div>
<label class="switch">
<input id="disableHardwareMediaKeys" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Enable GPU rasterization</h4>
<p>Move a part of the rendering to the GPU for increased performance.</p>
</div>
<label class="switch">
<input id="gpuRasterization" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Disable Background Throttling</h4>
<p>
Makes app more responsive while in the background, at the cost of performance.
</p>
</div>
<label class="switch">
<input id="disableBackgroundThrottle" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
</div>
</section>
<section id="about-section" class="tabs__section about-section">
<img alt="tidal icon" class="about-section__icon" src="./icon.png" />
<p class="about-section__text">
<a class="external-link" data-url="https://github.com/Mastermindzh/tidal-hifi">TIDAL Hi-Fi</a>
is made by
<a class="external-link" data-url="https://www.rickvanlieshout.com">
Rick van Lieshout</a>. <br />It uses
<a class="external-link" data-url="https://castlabs.com/">Castlabs'</a>
version of Electron for widevine support.
</p>
</section>
<footer class="footer">
<p class="footer__note">
Some settings may require a restart of TIDAL Hi-Fi. To do so, click the button below:
</p>
<button class="footer__button" id="restart">Restart TIDAL Hi-Fi</button>
</footer>
</div>
</main>
</div>
</body>
<style>
.header {
-webkit-user-select: none;
-webkit-app-region: drag;
}
.header a {
-webkit-app-region: no-drag;
}
* {
margin: 0%;
padding: 0%;
color: #ffffff;
font-weight: 400;
font-stretch: normal;
-webkit-font-smoothing: antialiased;
font-family: nationale, nationale-regular, Helvetica, sans-serif;
}
html,
body {
height: 100%;
background-color: black;
display: flex;
flex-direction: column;
}
h2 {
font-size: 1.2rem;
}
small {
font-style: italic;
color: #72777f;
}
.header {
background-color: #242528;
border-bottom: 1px solid #5a5a5a;
height: 50px;
}
.title {
float: left;
line-height: 50px;
margin-left: 15px;
}
.accent {
color: #0ff;
}
.exitWindow {
border: none;
text-decoration: none;
font-size: 1.4rem;
float: right;
margin-right: 15px;
height: 50px;
line-height: 50px;
}
.exitWindow svg {
height: 50px;
color: white;
}
.section {
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(246, 245, 255, .1);
}
.section .option {
margin-bottom: 15px;
}
.section .option p {
max-width: 75%;
float: left
}
.section .option label {
float: right;
}
.section:after,
.section .option:after {
content: "";
display: table;
clear: both;
}
.section h3 {
margin-bottom: 15px;
}
.section h4 {
font-size: 0.9rem;
}
.section p {
color: #72777f;
}
.bottom-border {
border-bottom: 1px solid #0ff;
}
.body {
padding: 15px;
flex: 1 1 auto;
position: relative;
overflow-y: auto;
}
.body::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-color: 2a2a2a;
}
.body::-webkit-scrollbar {
width: 10px;
background-color: #2a2a2a;
}
.body::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: #5a5a5a;
}
/* Tabs */
.tabset > input[type="radio"] {
position: absolute;
left: -200vw;
}
.tabset .tab-panel {
display: none;
}
.tabset > input:first-child:checked ~ .tab-panels > .tab-panel:first-child,
.tabset > input:nth-child(3):checked ~ .tab-panels > .tab-panel:nth-child(2),
.tabset > input:nth-child(5):checked ~ .tab-panels > .tab-panel:nth-child(3) {
display: block;
}
.tabset > label {
position: relative;
display: inline-block;
padding: 15px 0px 10px;
border-bottom: 0;
cursor: pointer;
}
.tabset > input + label {
color: #e0e0e0;
margin-right: 30px;
}
.tabset > input:checked + label {
color: #0ff;
border-bottom: 2px solid #0ff;
}
.tab-panel {
padding: 10px 0;
}
/* switches */
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(246, 245, 255, .1);
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 24px;
width: 24px;
left: 2px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #0ff;
}
input:focus + .slider {
box-shadow: 0 0 1px #0ff;
}
input:checked + .slider:before {
-webkit-transform: translateX(22px);
-ms-transform: translateX(22px);
transform: translateX(22px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/* input field */
input {
background: transparent;
border: 0;
border-bottom: 1px solid rgba(246, 245, 255, .1);
color: rgba(229, 238, 255, .6);
width: 100%;
display: block;
padding: 0 0 12px;
}
.freeTextInput:focus {
outline: none;
border-bottom: 1px solid #0ff;
}
/* buttons */
button{
border:none;
background:none;
align-items: center;
background-color: rgba(229,238,255,.2);
display: inline-flex;
justify-content: center;
border-radius: 12px;
height: 48px;
line-height: 49px;
padding: 0 24px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: background .35s ease;
min-height: 0;
min-width: 0;
font-size: 1.14286rem;
font-family: nationale,nationale-regular,Helvetica,sans-serif;
margin-top: 10px;
cursor: pointer;
}
button:hover{
background-color: rgba(229,238,255,.3);
}
</style>
</html>
</html>

View File

@@ -0,0 +1,363 @@
// --- Variables ---
$black: #17171a;
$grey-333: #333;
$white: #f9f9f9;
$tidal-blue: #0ff;
$tidal-grey: #72777f;
$tidal-grey-darker: #404248;
$tidal-grey-darker-focus: #55585f;
$tidal-grey-darkest: #242528;
// --- Fonts ---
@font-face {
font-family: "Noto Sans";
font-weight: 300;
src: url("fonts/NotoSans-Light.ttf") format("truetype");
}
@font-face {
font-family: "Noto Sans";
font-weight: normal;
src: url("fonts/NotoSans-Regular.ttf") format("truetype");
}
@font-face {
font-family: "Noto Sans";
font-weight: 600;
src: url("fonts/NotoSans-SemiBold.ttf") format("truetype");
}
@font-face {
font-family: "Noto Sans";
font-weight: bold;
src: url("fonts/NotoSans-Bold.ttf") format("truetype");
}
$font1: "Noto Sans", Helvetica, sans-serif;
// --- Mixins ---
@mixin drag($enabled: true) {
@if $enabled {
-webkit-app-region: drag;
}
@else {
-webkit-app-region: no-drag;
}
}
button {
cursor: pointer;
}
// --- Settings window ---
html {
height: 100%;
}
.external-link {
@extend button;
text-decoration: underline;
}
.settings-window {
height: 100%;
margin: 0;
color: $white;
font-family: $font1;
&__wrapper {
height: 100%;
background: $black;
box-shadow: inset 0 0 2px 0 $tidal-grey;
overflow: hidden;
}
&__drag-area {
@include drag;
position: absolute;
width: 100%;
height: 50px;
z-index: 0;
user-select: none;
}
&__close-button {
@extend button;
@include drag(false);
position: absolute;
top: 12px;
right: 10px;
padding: 10px;
border-radius: 100%;
z-index: 1;
&:hover {
background: $grey-333;
}
}
&__svg-icon {
display: block;
width: 18px;
height: 18px;
opacity: .7;
}
// --- Settings tabs ---
}
.settings {
height: 100%;
margin: 20px 0;
padding-left: 15px;
font-size: 0;
input {
&[type="radio"] {
margin-right: -18px;
transform: scale(0);
outline: none;
}
&+label {
@include drag(false);
display: inline-block;
position: relative;
margin-right: 35px;
padding-bottom: 8px;
border-bottom: 0;
font-size: 16px;
cursor: pointer;
z-index: 1;
user-select: none;
}
&:checked+label {
border-bottom: 2px solid $tidal-blue;
color: $tidal-blue;
}
}
}
.tabs {
height: calc(100% - 70px);
padding-right: 15px;
font-size: 16px;
overflow: auto;
&__section {
display: none;
}
@for $i from 1 to 6 {
.settings>input:nth-child(#{$i*2-1}):checked~&>.tabs__section:nth-child(#{$i}) {
display: block;
}
}
&::-webkit-scrollbar {
width: 10px;
&-thumb {
border-radius: 10px;
background-color: $tidal-grey-darker;
box-shadow: inset 0 0 10px 2px $tidal-grey-darkest;
}
// --- Settings group ---
}
}
.group {
padding: 10px 0;
border-bottom: 1px solid $grey-333;
&:last-child {
border: 0;
}
&__title {
margin-bottom: 10px;
font-size: 16px;
font-weight: bold;
}
&__option {
display: flex;
align-items: center;
}
&__description {
flex-grow: 1;
h4,
label {
display: block;
margin-top: 10px;
margin-bottom: 0;
font-size: 14px;
font-weight: 600;
}
p {
margin-top: 5px;
margin-bottom: 8px;
color: $tidal-grey;
font-size: 14px;
}
.text-input {
display: block;
width: 100%;
margin-bottom: 10px;
padding: 5px 0;
transition: .2s;
border: 0;
border-bottom: solid 1px $grey-333;
outline: none;
background: transparent;
color: $tidal-grey;
font-size: 14px;
&:focus {
border-color: $tidal-blue;
color: $white;
}
// --- Switch slider component ---
}
}
}
.switch {
$this: &;
position: relative;
min-width: 50px;
height: 28px;
margin-left: 10px;
input {
transform: scale(0);
outline: none;
&:checked+#{$this}__slider {
background-color: $tidal-blue;
&::before {
transform: translateX(22px);
background-color: white;
}
}
&:focus+#{$this}__slider {
box-shadow: inset 0 0 0 1px $tidal-blue;
}
}
&__slider {
@extend button;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transition: .4s;
border-radius: 40px;
background-color: $tidal-grey-darkest;
&::before {
position: absolute;
bottom: 2px;
left: 2px;
width: 24px;
height: 24px;
transition: .4s;
border-radius: 50%;
background-color: $white;
content: "";
}
// --- Textarea component
}
}
.textarea {
min-width: 100%;
max-width: 100%;
min-height: 50px;
max-height: 100px;
padding: 8px;
transition: .2s;
border: 0;
border-bottom: 1px solid transparent;
background: $tidal-grey-darkest;
color: $tidal-grey;
font-size: 13px;
box-sizing: border-box;
&:focus {
border-color: $tidal-blue;
outline: none;
color: $white;
}
// --- About section ---
}
.about-section {
padding-top: 120px;
text-align: center;
&__icon {
display: inline-block;
width: 100px;
}
&__text {
display: block;
max-width: 350px;
margin: 20px auto 0;
}
// --- Footer ---
}
.footer {
position: sticky;
top: calc(100% - 120px);
height: 100px;
padding-top: 20px;
text-align: center;
&__note {
max-width: 300px;
margin: 0 auto 15px;
color: $tidal-grey;
font-size: 12px;
}
&__button {
@extend button;
display: block;
height: 48px;
margin: auto;
padding: 0 24px;
transition: .2s;
border: 0;
border-radius: 12px;
background: $tidal-grey-darker;
color: $white;
font-size: 16px;
&:hover {
background: $tidal-grey-darker-focus;
}
}
}

View File

@@ -1,15 +1,18 @@
const { setTitle, getTitle } = require("./scripts/window-functions");
const { dialog } = require("electron").remote;
const { setTitle } = require("./scripts/window-functions");
const { dialog, process, Notification } = require("@electron/remote");
const { store, settings } = require("./scripts/settings");
const { ipcRenderer } = require("electron");
const { app } = require("electron").remote;
const { app } = require("@electron/remote");
const { downloadFile } = require("./scripts/download");
const statuses = require("./constants/statuses");
const hotkeys = require("./scripts/hotkeys");
const globalEvents = require("./constants/globalEvents");
const notifier = require("node-notifier");
const { skipArtists, updateFrequency, customCSS } = require("./constants/settings");
const notificationPath = `${app.getPath("userData")}/notification.jpg`;
const appName = "Tidal Hifi";
let currentSong = "";
let player;
let currentPlayStatus = statuses.paused;
const elements = {
play: '*[data-test="play"]',
@@ -17,7 +20,7 @@ const elements = {
next: '*[data-test="next"]',
previous: 'button[data-test="previous"]',
title: '*[data-test^="footer-track-title"]',
artists: '*[class^="mediaArtists"]',
artists: '*[data-test^="grid-item-detail-text-title-artist"]',
home: '*[data-test="menu--home"]',
back: '[class^="backwardButton"]',
forward: '[class^="forwardButton"]',
@@ -28,37 +31,96 @@ const elements = {
account: '*[data-test^="profile-image-button"]',
settings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]',
image: '*[class^="image--"]',
image: "img",
current: '*[data-test="current-time"]',
duration: '*[data-test="duration"]',
bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer",
album_header_title: '.header-details [data-test="title"]',
playing_title: 'span[data-test="table-cell-title"].css-geqnfr',
album_name_cell: '[data-test="table-cell-album"]',
tracklist_row: '[data-test="tracklist-row"]',
volume: '*[data-test="volume"]',
/**
* Get an element from the dom
* @param {*} key key in elements object to fetch
*/
get: function(key) {
get: function (key) {
return window.document.querySelector(this[key.toLowerCase()]);
},
/**
* Get the icon of the current song
*/
getSongIcon: function() {
getSongIcon: function () {
const figure = this.get("media");
if (figure) {
const mediaElement = figure.querySelector(this["image"]);
if (mediaElement) {
return mediaElement.src;
return mediaElement.src.replace("80x80", "640x640");
}
}
return "";
},
/**
* returns an array of all artists in the current song
* @returns {Array} artists
*/
getArtistsArray: function () {
const footer = this.get("footer");
if (footer) {
const artists = footer.querySelectorAll(this.artists);
if (artists) return Array.from(artists).map((artist) => artist.textContent);
}
return [];
},
/**
* unify the artists array into a string separated by commas
* @param {Array} artistsArray
* @returns {String} artists
*/
getArtistsString: function (artistsArray) {
if (artistsArray.length > 0) return artistsArray.join(", ");
return "unknown artist(s)";
},
getAlbumName: function () {
//If listening to an album, get its name from the header title
if (window.location.href.includes("/album/")) {
const albumName = window.document.querySelector(this.album_header_title);
if (albumName) {
return albumName.textContent;
}
//If listening to a playlist or a mix, get album name from the list
} else if (
window.location.href.includes("/playlist/") ||
window.location.href.includes("/mix/")
) {
if (currentPlayStatus === statuses.playing) {
const row = window.document.querySelector(this.playing_title).closest(this.tracklist_row);
if (row) {
return row.querySelector(this.album_name_cell).textContent;
}
}
}
return "";
},
isMuted: function () {
return this.get("volume").getAttribute("aria-checked") === "false"; // it's muted if aria-checked is false
},
/**
* Shorthand function to get the text of a dom element
* @param {*} key key in elements object to fetch
*/
getText: function(key) {
getText: function (key) {
const element = this.get(key);
return element ? element.textContent : "";
},
@@ -67,7 +129,7 @@ const elements = {
* Shorthand function to click a dom element
* @param {*} key key in elements object to fetch
*/
click: function(key) {
click: function (key) {
this.get(key).click();
return this;
},
@@ -76,11 +138,34 @@ const elements = {
* Shorthand function to focus a dom element
* @param {*} key key in elements object to fetch
*/
focus: function(key) {
focus: function (key) {
return this.get(key).focus();
},
};
function addCustomCss() {
window.addEventListener("DOMContentLoaded", () => {
const style = document.createElement("style");
style.innerHTML = store.get(customCSS);
document.head.appendChild(style);
});
}
/**
* Get the update frequency from the store
* make sure it returns a number, if not use the default
*/
function getUpdateFrequency() {
const storeValue = store.get(updateFrequency);
const defaultValue = 500;
if (!isNaN(storeValue)) {
return storeValue;
} else {
return defaultValue;
}
}
/**
* Play or pause the current song
*/
@@ -100,55 +185,41 @@ function playPause() {
* https://defkey.com/tidal-desktop-shortcuts
*/
function addHotKeys() {
hotkeys.add("Control+p", function() {
elements.click("account").click("settings");
});
hotkeys.add("Control+l", function() {
handleLogout();
});
if (store.get(settings.enableCustomHotkeys)) {
hotkeys.add("Control+p", function () {
elements.click("account").click("settings");
});
hotkeys.add("Control+l", function () {
handleLogout();
});
hotkeys.add("Control+h", function() {
elements.click("home");
});
hotkeys.add("Control+h", function () {
elements.click("home");
});
hotkeys.add("backspace", function() {
elements.click("back");
});
hotkeys.add("backspace", function () {
elements.click("back");
});
hotkeys.add("shift+backspace", function() {
elements.click("forward");
});
hotkeys.add("shift+backspace", function () {
elements.click("forward");
});
hotkeys.add("control+f", function() {
elements.focus("search");
});
hotkeys.add("control+u", function () {
// reloading window without cache should show the update bar if applicable
window.location.reload(true);
});
hotkeys.add("control+u", function() {
// reloading window without cache should show the update bar if applicable
window.location.reload(true);
});
hotkeys.add("control+r", function () {
elements.click("repeat");
});
}
hotkeys.add("control+left", function() {
elements.click("previous");
// always add the hotkey for the settings window
hotkeys.add("control+=", function () {
ipcRenderer.send(globalEvents.showSettings);
});
hotkeys.add("control+right", function() {
elements.click("next");
});
hotkeys.add("control+right", function() {
elements.click("next");
});
hotkeys.add("control+s", function() {
elements.click("shuffle");
});
hotkeys.add("control+r", function() {
elements.click("repeat");
});
hotkeys.add("control+/", function() {
hotkeys.add("control+0", function () {
ipcRenderer.send(globalEvents.showSettings);
});
}
@@ -169,10 +240,10 @@ function handleLogout() {
buttons: logoutOptions,
defaultId: 2,
},
function(response) {
function (response) {
if (logoutOptions.indexOf("Yes, please") == response) {
for (i = 0; i < window.localStorage.length; i++) {
key = window.localStorage.key(i);
for (let i = 0; i < window.localStorage.length; i++) {
const key = window.localStorage.key(i);
if (key.startsWith("_TIDAL_activeSession")) {
window.localStorage.removeItem(key);
i = window.localStorage.length + 1;
@@ -184,13 +255,19 @@ function handleLogout() {
);
}
function addFullScreenListeners() {
window.document.addEventListener("fullscreenchange", () => {
ipcRenderer.send(globalEvents.refreshMenuBar);
});
}
/**
* Add ipc event listeners.
* Some actions triggered outside of the site need info from the site.
*/
function addIPCEventListeners() {
window.addEventListener("DOMContentLoaded", () => {
ipcRenderer.on("globalEvent", (event, args) => {
ipcRenderer.on("globalEvent", (_event, args) => {
switch (args) {
case globalEvents.playPause:
playPause();
@@ -215,61 +292,229 @@ function addIPCEventListeners() {
/**
* Update the current status of tidal (e.g playing or paused)
*/
function updateStatus() {
const play = elements.get("play");
let status = statuses.paused;
// if play button is NOT visible tidal is playing
if (!play) {
function getCurrentlyPlayingStatus() {
let pause = elements.get("pause");
let status = undefined;
// if pause button is visible tidal is playing
if (pause) {
status = statuses.playing;
} else {
status = statuses.paused;
}
return status;
}
/**
* Convert the duration from MM:SS to seconds
* @param {*} duration
*/
function convertDuration(duration) {
const parts = duration.split(":");
return parseInt(parts[1]) + 60 * parseInt(parts[0]);
}
/**
* Update Tidal-hifi's media info
*
* @param {*} options
*/
function updateMediaInfo(options, notify) {
if (options) {
ipcRenderer.send(globalEvents.updateInfo, options);
if (store.get(settings.notifications) && notify) {
new Notification({ title: options.title, body: options.artists, icon: options.icon }).show();
}
if (player) {
player.metadata = {
...player.metadata,
...{
"xesam:title": options.title,
"xesam:artist": [options.artists],
"xesam:album": options.album,
"mpris:artUrl": options.image,
"mpris:length": convertDuration(options.duration) * 1000 * 1000,
"mpris:trackid": "/org/mpris/MediaPlayer2/track/" + getTrackID(),
},
};
player.playbackStatus = options.status == statuses.paused ? "Paused" : "Playing";
}
}
}
/**
* Checks if Tidal is playing a video or song by grabbing the "a" element from the title.
* If it's a song it returns the track URL, if not it will return undefined
*/
function getTrackURL() {
const id = getTrackID();
return `https://tidal.com/browse/track/${id}`;
}
function getTrackID() {
const URLelement = elements.get("title").querySelector("a");
if (URLelement !== null) {
const id = URLelement.href.replace(/\D/g, "");
return id;
}
return window.location;
}
function updateMediaSession(options) {
if ("mediaSession" in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: options.title,
artist: options.artists,
album: options.album,
artwork: [
{
src: options.icon,
sizes: "640x640",
type: "image/png",
},
],
});
}
ipcRenderer.send(globalEvents.updateStatus, status);
}
/**
* Watch for song changes and update title + notify
*/
setInterval(function() {
setInterval(function () {
const title = elements.getText("title");
const artists = elements.getText("artists");
const songDashArtistTitle = `${title} - ${artists}`;
const artistsArray = elements.getArtistsArray();
const artistsString = elements.getArtistsString(artistsArray);
skipArtistsIfFoundInSkippedArtistsList(artistsArray);
updateStatus();
const album = elements.getAlbumName();
const current = elements.getText("current");
const duration = elements.getText("duration");
const songDashArtistTitle = `${title} - ${artistsString}`;
const currentStatus = getCurrentlyPlayingStatus();
const options = {
title,
artists: artistsString,
album: album,
status: currentStatus,
url: getTrackURL(),
current,
duration,
"app-name": appName,
};
if (getTitle() !== songDashArtistTitle) {
setTitle(songDashArtistTitle);
const titleOrArtistsChanged = currentSong !== songDashArtistTitle;
if (currentSong !== songDashArtistTitle) {
currentSong = songDashArtistTitle;
const image = elements.getSongIcon();
// update title, url and play info with new info
setTitle(songDashArtistTitle);
getTrackURL();
currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus;
const options = {
title,
message: artists,
};
new Promise((resolve, reject) => {
if (image.startsWith("http")) {
downloadFile(image, notificationPath).then(
() => {
options.icon = notificationPath;
resolve();
},
() => {
reject();
}
);
} else {
reject();
}
}).then(
const image = elements.getSongIcon();
new Promise((resolve) => {
if (image.startsWith("http")) {
options.image = image;
downloadFile(image, notificationPath).then(
() => {
ipcRenderer.send(globalEvents.updateInfo, options);
store.get(settings.notifications) && notifier.notify(options);
options.icon = notificationPath;
resolve();
},
() => {}
() => {
// if the image can't be downloaded then continue without it
resolve();
}
);
} else {
// if the image can't be found on the page continue without it
resolve();
}
}).then(
() => {
updateMediaInfo(options, titleOrArtistsChanged);
if (titleOrArtistsChanged) {
updateMediaSession(options);
}
},
() => {}
);
/**
* automatically skip a song if the artists are found in the list of artists to skip
* @param {*} artists array of artists
*/
function skipArtistsIfFoundInSkippedArtistsList(artists) {
if (store.get(skipArtists)) {
const skippedArtists = store.get(settings.skippedArtists);
if (skippedArtists.length > 0) {
const artistsToSkip = skippedArtists.map((artist) => artist);
const artistNames = Object.values(artists).map((artist) => artist);
const foundArtist = artistNames.some((artist) => artistsToSkip.includes(artist));
if (foundArtist) {
elements.click("next");
}
}
}
}
}, 200);
}, getUpdateFrequency());
if (process.platform === "linux" && store.get(settings.mpris)) {
try {
const Player = require("mpris-service");
player = Player({
name: "tidal-hifi",
identity: "tidal-hifi",
supportedUriSchemes: ["file"],
supportedMimeTypes: [
"audio/mpeg",
"audio/flac",
"audio/x-flac",
"application/ogg",
"audio/wav",
],
supportedInterfaces: ["player"],
desktopEntry: "tidal-hifi",
});
// Events
var events = {
next: "next",
previous: "previous",
pause: "pause",
playpause: "playpause",
stop: "stop",
play: "play",
loopStatus: "repeat",
shuffle: "shuffle",
seek: "seek",
};
Object.keys(events).forEach(function (eventName) {
player.on(eventName, function () {
const eventValue = events[eventName];
switch (events[eventValue]) {
case events.playpause:
playPause();
break;
default:
elements.click(eventValue);
}
});
});
// Override get position function
player.getPosition = function () {
return convertDuration(elements.getText("current")) * 1000 * 1000;
};
player.on("quit", function () {
app.quit();
});
} catch (exception) {
console.log("player api not working");
}
}
addCustomCss();
addHotKeys();
addIPCEventListeners();
addFullScreenListeners();

95
src/scripts/discord.js Normal file
View File

@@ -0,0 +1,95 @@
const discordrpc = require("discord-rpc");
const { app, ipcMain } = require("electron");
const globalEvents = require("../constants/globalEvents");
const clientId = "833617820704440341";
const mediaInfoModule = require("./mediaInfo");
const discordModule = [];
function timeToSeconds(timeArray) {
let minutes = timeArray[0] * 1;
let seconds = minutes * 60 + timeArray[1] * 1;
return seconds;
}
let rpc;
const observer = (event, arg) => {
if (mediaInfoModule.mediaInfo.status == "paused" && rpc) {
rpc.setActivity(idleStatus);
} else if (rpc) {
const currentSeconds = timeToSeconds(mediaInfoModule.mediaInfo.current.split(":"));
const durationSeconds = timeToSeconds(mediaInfoModule.mediaInfo.duration.split(":"));
const date = new Date();
const now = (date.getTime() / 1000) | 0;
const remaining = date.setSeconds(date.getSeconds() + (durationSeconds - currentSeconds));
if (mediaInfoModule.mediaInfo.url) {
rpc.setActivity({
...idleStatus,
...{
details: `Listening to ${mediaInfoModule.mediaInfo.title}`,
state: mediaInfoModule.mediaInfo.artists
? mediaInfoModule.mediaInfo.artists
: "unknown artist(s)",
startTimestamp: parseInt(now),
endTimestamp: parseInt(remaining),
largeImageKey: mediaInfoModule.mediaInfo.image,
largeImageText: mediaInfoModule.mediaInfo.album
? mediaInfoModule.mediaInfo.album
: `${idleStatus.largeImageText}`,
buttons: [{ label: "Play on Tidal", url: mediaInfoModule.mediaInfo.url }],
},
});
} else {
rpc.setActivity({
...idleStatus,
...{
details: `Watching ${mediaInfoModule.mediaInfo.title}`,
state: mediaInfoModule.mediaInfo.artists,
startTimestamp: parseInt(now),
endTimestamp: parseInt(remaining),
},
});
}
}
};
const idleStatus = {
details: `Browsing Tidal`,
largeImageKey: "tidal-hifi-icon",
largeImageText: `Tidal HiFi ${app.getVersion()}`,
instance: false,
};
/**
* Set up the discord rpc and listen on globalEvents.updateInfo
*/
discordModule.initRPC = function () {
rpc = new discordrpc.Client({ transport: "ipc" });
rpc.login({ clientId }).then(
() => {
discordModule.rpc = rpc;
rpc.on("ready", () => {
rpc.setActivity(idleStatus);
});
ipcMain.on(globalEvents.updateInfo, observer);
},
() => {
console.error("Can't connect to Discord, is it running?");
}
);
};
/**
* Remove any RPC connection with discord and remove the event listener on globalEvents.updateInfo
*/
discordModule.unRPC = function () {
if (rpc) {
rpc.clearActivity();
rpc.destroy();
rpc = false;
discordModule.rpc = undefined;
ipcMain.removeListener(globalEvents.updateInfo, observer);
}
};
module.exports = discordModule;

View File

@@ -11,7 +11,7 @@ let expressInstance;
/**
* Function to enable tidal-hifi's express api
*/
expressModule.run = function(mainWindow) {
expressModule.run = function (mainWindow) {
/**
* Shorthand to handle a fire and forget global event
* @param {*} res
@@ -24,14 +24,14 @@ expressModule.run = function(mainWindow) {
const expressApp = express();
expressApp.get("/", (req, res) => res.send("Hello World!"));
expressApp.get("/current", (req, res) => res.json(mediaInfo));
expressApp.get("/current", (req, res) => res.json({ ...mediaInfo, artist: mediaInfo.artists }));
expressApp.get("/image", (req, res) => {
var stream = fs.createReadStream(mediaInfo.icon);
stream.on("open", function() {
stream.on("open", function () {
res.set("Content-Type", "image/png");
stream.pipe(res);
});
stream.on("error", function() {
stream.on("error", function () {
res.set("Content-Type", "text/plain");
res.status(404).end("Not found");
});
@@ -53,8 +53,8 @@ expressModule.run = function(mainWindow) {
if (store.get(settings.api)) {
let port = store.get(settings.apiSettings.port);
expressInstance = expressApp.listen(port, () => {});
expressInstance.on("error", function(e) {
expressInstance = expressApp.listen(port, "127.0.0.1", () => {});
expressInstance.on("error", function (e) {
let message = e.code;
if (e.code === "EADDRINUSE") {
message = `Port ${port} in use.`;

View File

@@ -2,9 +2,14 @@ const statuses = require("./../constants/statuses");
const mediaInfo = {
title: "",
artist: "",
artists: "",
album: "",
icon: "",
status: statuses.paused,
url: "",
current: "",
duration: "",
image: "tidal-hifi-icon",
};
const mediaInfoModule = {
mediaInfo,
@@ -13,19 +18,16 @@ const mediaInfoModule = {
/**
* Update artist and song info in the mediaInfo constant
*/
mediaInfoModule.update = function(arg) {
mediaInfoModule.update = function (arg) {
mediaInfo.title = propOrDefault(arg.title);
mediaInfo.artist = propOrDefault(arg.message);
mediaInfo.artists = propOrDefault(arg.artists);
mediaInfo.album = propOrDefault(arg.album);
mediaInfo.icon = propOrDefault(arg.icon);
};
/**
* Update tidal's status in the mediaInfo constant
*/
mediaInfoModule.updateStatus = function(status) {
if (Object.values(statuses).includes(status)) {
mediaInfo.status = status;
}
mediaInfo.url = propOrDefault(arg.url);
mediaInfo.status = propOrDefault(arg.status);
mediaInfo.current = propOrDefault(arg.current);
mediaInfo.duration = propOrDefault(arg.duration);
mediaInfo.image = propOrDefault(arg.image);
};
/**

View File

@@ -1,107 +1,123 @@
const { Menu } = require("electron");
const { Menu, app } = require("electron");
const { showSettingsWindow } = require("./settings");
const isMac = process.platform === "darwin";
const { name } = require("./../constants/values");
const settingsMenuEntry = {
label: "Settings",
click() {
showSettingsWindow();
},
accelerator: "Control+/",
accelerator: "Control+=",
};
const mainMenu = [
...(isMac
? [
{
label: app.name,
submenu: [
{ role: "about" },
settingsMenuEntry,
{ type: "separator" },
{ role: "services" },
{ type: "separator" },
{ role: "hide" },
{ role: "hideothers" },
{ role: "unhide" },
{ type: "separator" },
{ role: "quit" },
],
},
]
: []),
// { role: 'fileMenu' }
{
label: "File",
submenu: [settingsMenuEntry, isMac ? { role: "close" } : { role: "quit" }],
const quitMenuEntry = {
label: "Quit",
click() {
app.exit(0);
},
// { role: 'editMenu' }
{
label: "Edit",
submenu: [
{ role: "undo" },
{ role: "redo" },
{ type: "separator" },
{ role: "cut" },
{ role: "copy" },
{ role: "paste" },
...(isMac
? [
{ role: "pasteAndMatchStyle" },
{ role: "delete" },
{ role: "selectAll" },
{ type: "separator" },
{
label: "Speech",
submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }],
},
]
: [{ role: "delete" }, { type: "separator" }, { role: "selectAll" }]),
{ type: "separator" },
settingsMenuEntry,
],
},
// { role: 'viewMenu' }
{
label: "View",
submenu: [
{ role: "reload" },
{ role: "forcereload" },
{ type: "separator" },
{ role: "resetzoom" },
{ role: "zoomin" },
{ role: "zoomout" },
{ type: "separator" },
{ role: "togglefullscreen" },
],
},
// { role: 'windowMenu' }
{
label: "Window",
submenu: [
{ role: "minimize" },
...(isMac
? [{ type: "separator" }, { role: "front" }, { type: "separator" }, { role: "window" }]
: [{ role: "close" }]),
],
},
settingsMenuEntry,
{
label: "About",
click() {
showSettingsWindow("tab3");
accelerator: "Control+Q",
};
const menuModule = {};
menuModule.getMenu = function (mainWindow) {
const toggleWindow = {
label: "Toggle Window",
click: function () {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
},
},
];
};
const menuModule = { mainMenu };
const mainMenu = [
...(isMac
? [
{
label: name,
submenu: [
{ role: "about" },
settingsMenuEntry,
{ type: "separator" },
{ role: "services" },
{ type: "separator" },
{ role: "hide" },
{ role: "hideothers" },
{ role: "unhide" },
{ type: "separator" },
quitMenuEntry,
],
},
]
: []),
{
label: "File",
submenu: [settingsMenuEntry, isMac ? { role: "close" } : quitMenuEntry],
},
{
label: "Edit",
submenu: [
{ role: "undo" },
{ role: "redo" },
{ type: "separator" },
{ role: "cut" },
{ role: "copy" },
{ role: "paste" },
...(isMac
? [
{ role: "pasteAndMatchStyle" },
{ role: "delete" },
{ role: "selectAll" },
{ type: "separator" },
{
label: "Speech",
submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }],
},
]
: [{ role: "delete" }, { type: "separator" }, { role: "selectAll" }]),
{ type: "separator" },
settingsMenuEntry,
],
},
{
label: "View",
submenu: [
{ role: "reload" },
{ role: "forcereload" },
{ type: "separator" },
{ role: "resetzoom" },
{ role: "zoomin" },
{ role: "zoomout" },
{ type: "separator" },
{ role: "togglefullscreen" },
{ role: "toggledevtools" },
],
},
{
label: "Window",
submenu: [
{ role: "minimize" },
toggleWindow,
...(isMac
? [{ type: "separator" }, { role: "front" }, { type: "separator" }, { role: "window" }]
: [{ role: "close" }]),
],
},
settingsMenuEntry,
{
label: "About",
click() {
showSettingsWindow("about");
},
},
toggleWindow,
quitMenuEntry,
];
menuModule.getMenu = function() {
return Menu.buildFromTemplate(mainMenu);
};
menuModule.addMenu = function() {
Menu.setApplicationMenu(menuModule.getMenu());
menuModule.addMenu = function (mainWindow) {
Menu.setApplicationMenu(menuModule.getMenu(mainWindow));
};
module.exports = menuModule;

View File

@@ -7,16 +7,41 @@ let settingsWindow;
const store = new Store({
defaults: {
notifications: true,
adBlock: false,
api: true,
playBackControl: true,
menuBar: true,
apiSettings: {
port: 47836,
},
customCSS: "",
disableBackgroundThrottle: true,
disableHardwareMediaKeys: false,
enableCustomHotkeys: false,
enableDiscord: false,
flags: {
gpuRasterization: true,
disableHardwareMediaKeys: false,
},
menuBar: true,
minimizeOnClose: false,
mpris: false,
notifications: true,
playBackControl: true,
singleInstance: true,
skipArtists: false,
skippedArtists: [""],
trayIcon: true,
updateFrequency: 500,
windowBounds: { width: 800, height: 600 },
},
migrations: {
"3.1.0": (migrationStore) => {
console.log("running migrations for 3.1.0");
migrationStore.set(
settings.flags.disableHardwareMediaKeys,
migrationStore.get("disableHardwareMediaKeys") ?? false
);
},
},
});
const settingsModule = {
@@ -25,15 +50,16 @@ const settingsModule = {
settingsWindow,
};
settingsModule.createSettingsWindow = function() {
settingsModule.createSettingsWindow = function () {
settingsWindow = new BrowserWindow({
width: 500,
width: 700,
height: 600,
resizable: false,
show: false,
transparent: true,
frame: false,
title: "Tidal-hifi - settings",
title: "TIDAL Hi-Fi settings",
webPreferences: {
affinity: "window",
preload: path.join(__dirname, "../pages/settings/preload.js"),
plugins: true,
nodeIntegration: true,
@@ -52,18 +78,18 @@ settingsModule.createSettingsWindow = function() {
settingsModule.settingsWindow = settingsWindow;
};
settingsModule.showSettingsWindow = function(tab = "tab1") {
settingsModule.showSettingsWindow = function (tab = "general") {
settingsWindow.webContents.send("goToTab", tab);
// refresh data just before showing the window
settingsWindow.webContents.send("refreshData");
settingsWindow.show();
};
settingsModule.hideSettingsWindow = function() {
settingsModule.hideSettingsWindow = function () {
settingsWindow.hide();
};
settingsModule.closeSettingsWindow = function() {
settingsModule.closeSettingsWindow = function () {
settingsWindow = null;
};

View File

@@ -3,17 +3,32 @@ const { getMenu } = require("./menu");
const trayModule = {};
let tray;
trayModule.addTray = function(options = { icon: "" }) {
trayModule.addTray = function (mainWindow, options = { icon: "" }) {
tray = new Tray(options.icon);
tray.setIgnoreDoubleClickEvents(true);
tray.setToolTip("Tidal-hifi");
const menu = getMenu(mainWindow);
tray.setContextMenu(menu);
tray.on("click", function () {
if (mainWindow.isVisible()) {
if (!mainWindow.isFocused()) {
mainWindow.focus();
} else {
mainWindow.hide();
}
} else {
mainWindow.show();
}
});
};
trayModule.refreshTray = function() {
tray.on("click", function(e) {
// do nothing on click
});
tray.setToolTip("Tidal-hifi");
tray.setContextMenu(getMenu());
trayModule.refreshTray = function (mainWindow) {
if (!tray) {
trayModule.addTray(mainWindow);
}
};
module.exports = trayModule;

18
stale.yml Normal file
View File

@@ -0,0 +1,18 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 2
# Issues with these labels will never be considered stale
exemptLabels:
- good first issue
- waiting for support
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false