Compare commits

...

55 Commits

Author SHA1 Message Date
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
23 changed files with 7104 additions and 1304 deletions

View File

@@ -9,10 +9,12 @@ jobs:
build_on_linux: build_on_linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install libarchive-tools
run: sudo apt-get install -y libarchive-tools
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master
with: with:
node-version: 12 node-version: 16
- run: npm install - run: npm install
- run: npm run build - run: npm run build
@@ -22,7 +24,7 @@ jobs:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master
with: with:
node-version: 12 node-version: 16
- run: npm install - run: npm install
- run: npm run build - run: npm run build
@@ -32,6 +34,6 @@ jobs:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master
with: with:
node-version: 12 node-version: 16
- run: npm install - run: npm install
- run: npm run build - run: npm run build

View File

@@ -9,10 +9,12 @@ jobs:
build_on_linux: build_on_linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install libarchive-tools
run: sudo apt-get install -y libarchive-tools
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master
with: with:
node-version: 12 node-version: 16
- run: npm install - run: npm install
- run: npm run build - run: npm run build
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master
@@ -26,7 +28,7 @@ jobs:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master
with: with:
node-version: 12 node-version: 16
- run: npm install - run: npm install
- run: npm run build - run: npm run build
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master
@@ -40,7 +42,7 @@ jobs:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master
with: with:
node-version: 12 node-version: 16
- run: npm install - run: npm install
- run: npm run build - run: npm run build
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master

View File

@@ -4,6 +4,94 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 2.7.1
- Fixed bug: Triggering fullscreen 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 mediainfo 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 trayicon
## 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 ## 1.1.0
- updated to electron 8.0.0 - updated to electron 8.0.0

View File

@@ -3,7 +3,7 @@ Tidal-hifi
<img src = "./build/icon.png" height="40" align="right" /> <img src = "./build/icon.png" height="40" align="right" />
</h1> </h1>
The web version of [listen.tidal.com](listen.tidal.com) running in electron with hifi support thanks to widevine. 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) ![tidal-hifi preview](./docs/preview.png)
@@ -14,12 +14,20 @@ The web version of [listen.tidal.com](listen.tidal.com) running in electron with
- [Installation](#installation) - [Installation](#installation)
- [Using releases](#using-releases) - [Using releases](#using-releases)
- [Snap install](#snap-install) - [Snap install](#snap-install)
- [Arch Linux](#arch-linux)
- [Using source](#using-source) - [Using source](#using-source)
- [features](#features) - [features](#features)
- [Integrations](#integrations) - [Integrations](#integrations)
- [not included](#not-included)
- [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](#why)
- [Why not extend existing projects?](#why-not-extend-existing-projects) - [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 --> <!-- tocstop -->
@@ -33,13 +41,13 @@ Various packaged versions of the software are available on the [releases](https:
To install with `snap` you need to download the pre-packaged snap-package from this repository, found under releases: To install with `snap` you need to download the pre-packaged snap-package from this repository, found under releases:
1. Download: 1. Download
```sh ```sh
wget <URI> #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap wget <URI> #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap
``` ```
2. Install: 2. Install
```sh ```sh
snap install --dangerous <path> #for instance: tidal-hifi_1.0.0_amd64.snap snap install --dangerous <path> #for instance: tidal-hifi_1.0.0_amd64.snap
@@ -57,7 +65,7 @@ trizen tidal-hifi
To install and work with the code on this project follow these steps: 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 - cd tidal-hifi
- npm install - npm install
- npm start - npm start
@@ -66,17 +74,35 @@ To install and work with the code on this project follow these steps:
- HiFi playback - HiFi playback
- Notifications - Notifications
- Shortcuts ([source](https://defkey.com/tidal-desktop-shortcuts)) - Custom hotkeys ([source](https://defkey.com/tidal-desktop-shortcuts))
- API for status and playback - API for status and playback
- [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+/`) - 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 ## 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) - [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 ### Known bugs
- [Last.fm login doesn't work](https://github.com/Mastermindzh/tidal-hifi/issues/4). #### 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 ## Why
@@ -95,7 +121,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 :) 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/) - [Castlabs](https://castlabs.com/)
For maintaining Electron with Widevine CDM installation, Verified Media Path (VMP), and persistent licenses (StorageID) 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://secure.kwf.nl/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).

View File

@@ -1,5 +1,7 @@
appId: com.rickvanlieshout.tidal-hifi appId: com.rickvanlieshout.tidal-hifi
electronVersion: 8.5.2
electronDownload: electronDownload:
version: 8.5.2-wvvmp
mirror: https://github.com/castlabs/electron-releases/releases/download/v mirror: https://github.com/castlabs/electron-releases/releases/download/v
snap: snap:
plugs: plugs:
@@ -8,7 +10,7 @@ snap:
linux: linux:
category: Audio category: Audio
target: target:
# - pacman - pacman
- tar.gz - tar.gz
- deb - deb
- rpm - rpm
@@ -32,3 +34,7 @@ mac:
category: public.app-category.entertainment category: public.app-category.entertainment
win: win:
target: msi target: msi
icon: build/icon.png
artifactName: "tidalhifi"
appId: com.rickvanlieshout.tidalhifi
executableName: tidalhifi

View File

@@ -1,20 +0,0 @@
pkgbase = tidal-hifi-git
pkgdesc = The web version of listen.tidal.com running in electron with hifi support thanks to widevine.
pkgver = 1.1.0
pkgrel = 1
url = https://github.com/Mastermindzh/tidal-hifi
arch = x86_64
license = custom:MIT
makedepends = npm
makedepends = git
depends = libxss
depends = nss
depends = gtk3
provides = tidal-hifi
source = https://github.com/Mastermindzh/tidal-hifi/archive/1.1.0.zip
source = tidal-hifi.desktop
sha512sums = b855f6530b945e4b0f7891c04a8b0279cf642d9cfcf180b8ded629a85743899ac431d3de87fcecb605fae82e1df2dc86d0a72c3cc85d426bf8fe08b41630c299
sha512sums = fa5fa918ea890baa5f500db3153a6eff3d63966528ffa3349acda3ea02fbecb1ea78a1ba1d23ef7402de2228fc0a483252e0b7e72c73cfb25ed401bedaf856f5
pkgname = tidal-hifi-git

View File

@@ -1,56 +0,0 @@
# Maintainer: Rick van Lieshout <info@rickvanlieshout.com>
_pkgname=tidal-hifi
pkgname="$_pkgname-git"
pkgver=1.1.0
pkgrel=1
pkgdesc="The web version of listen.tidal.com running in electron with hifi support thanks to widevine."
arch=("x86_64")
url="https://github.com/Mastermindzh/tidal-hifi"
license=("custom:MIT")
depends=("libxss" "nss" "gtk3")
makedepends=("npm" "git")
provides=("$_pkgname")
source=("https://github.com/Mastermindzh/tidal-hifi/archive/$pkgver.zip"
"${_pkgname}.desktop")
sha512sums=('b855f6530b945e4b0f7891c04a8b0279cf642d9cfcf180b8ded629a85743899ac431d3de87fcecb605fae82e1df2dc86d0a72c3cc85d426bf8fe08b41630c299'
'fa5fa918ea890baa5f500db3153a6eff3d63966528ffa3349acda3ea02fbecb1ea78a1ba1d23ef7402de2228fc0a483252e0b7e72c73cfb25ed401bedaf856f5')
cdToPkg(){
cd "tidal-hifi-$pkgver"
}
prepare() {
cdToPkg
# install build dependencies
npm install
}
build() {
cdToPkg
# We are not using the systems Electron as we need castlab's Electron.
npx electron-builder --linux dir
}
package() {
cdToPkg
install -d "${pkgdir}/opt/${_pkgname}/" "${pkgdir}/usr/bin" "${pkgdir}/usr/share/doc" "${pkgdir}/usr/share/licenses"
cp -r dist/linux-unpacked/* "${pkgdir}/opt/${_pkgname}/"
chmod +x "${pkgdir}/opt/${_pkgname}/${_pkgname}"
ln -s "/opt/${_pkgname}/${_pkgname}" "${pkgdir}/usr/bin/${_pkgname}"
install -Dm 644 "build/icon.png" "${pkgdir}/usr/share/pixmaps/${_pkgname}.png"
install -Dm 644 "${srcdir}/${_pkgname}.desktop" "${pkgdir}/usr/share/applications/${_pkgname}.desktop"
install -Dm 644 "README.md" "${pkgdir}/usr/share/doc/${pkgname}/README.md"
install -Dm 644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
ln -s "/opt/${_pkgname}/LICENSE.electron.txt" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE.electron.txt"
ln -s "/opt/${_pkgname}/LICENSES.chromium.html" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSES.chromium.html"
}

View File

@@ -1,18 +0,0 @@
#!/bin/bash
# Will generate a correctly formatted SRCINFO file
SCRIPT_DIST=".SRCINFO"
# generate SRCINFO
makepkg --printsrcinfo > $SCRIPT_DIST
# replace pkgbase with tidal-hifi-git
pkgName="tidal-hifi-git"
sed -i "1s/.*/pkgbase = $pkgName/" $SCRIPT_DIST
# replace pkgbase with tidal-hifi-git
sed -i '/^pkgname/ d' $SCRIPT_DIST
echo "pkgname = $pkgName" >> $SCRIPT_DIST
# remove double line breaks and replace with single line breaks
sed -i '/^$/N;/^\n$/D' $SCRIPT_DIST

View File

@@ -1,13 +0,0 @@
[Desktop Entry]
Encoding=UTF-8
Name=tidal-hifi
GenericName=tidal-hifi
Comment=The web version of listen.tidal.com running in electron with hifi support thanks to widevine.
Exec=tidal-hifi %u
Icon=tidal-hifi.png
StartupNotify=true
Terminal=false
Type=Application
Categories=Network;Application;Audio;Video
StartupWMClass=tidal-hifi
X-PulseAudio-Properties=media.role=music

BIN
docs/integrations.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/settings-preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

7422
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "tidal-hifi", "name": "tidal-hifi",
"version": "1.1.0", "version": "2.7.1",
"description": "Tidal on Electron with widevine(hifi) support", "description": "Tidal on Electron with widevine(hifi) support",
"main": "src/main.js", "main": "src/main.js",
"scripts": { "scripts": {
@@ -9,7 +9,7 @@
"build-deb": "electron-builder --publish=never -c ./build/electron-builder.deb.yml", "build-deb": "electron-builder --publish=never -c ./build/electron-builder.deb.yml",
"build-rpm": "electron-builder --publish=never -c ./build/electron-builder.rpm.yml", "build-rpm": "electron-builder --publish=never -c ./build/electron-builder.rpm.yml",
"build-snap": "electron-builder --publish=never -c ./build/electron-builder.snap.yml", "build-snap": "electron-builder --publish=never -c ./build/electron-builder.snap.yml",
"build-arch": "npm run build-without-release -c ./build/electron-builder.pacman.yml", "build-arch": "electron-builder --publish=never -c ./build/electron-builder.pacman.yml",
"build-wl": "electron-builder --publish=never -c ./build/electron-builder.yml -wl", "build-wl": "electron-builder --publish=never -c ./build/electron-builder.yml -wl",
"build-mac": "electron-builder --publish=never -c ./build/electron-builder.yml -m" "build-mac": "electron-builder --publish=never -c ./build/electron-builder.yml -m"
}, },
@@ -20,21 +20,22 @@
"linux" "linux"
], ],
"author": "Rick van Lieshout <info@rickvanlieshout.com> (http://rickvanlieshout.com)", "author": "Rick van Lieshout <info@rickvanlieshout.com> (http://rickvanlieshout.com)",
"homepage": "https://github.com/Mastermindzh/tidal-hifi",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"electron-store": "^5.1.1", "discord-rpc": "^4.0.1",
"electron-store": "^8.0.1",
"express": "^4.17.1", "express": "^4.17.1",
"hotkeys-js": "^3.7.6", "hotkeys-js": "^3.8.7",
"node-notifier": "^6.0.0", "mpris-service": "^2.1.2",
"request": "^2.88.2" "request": "^2.88.2"
}, },
"devDependencies": { "devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0", "@mastermindzh/prettier-config": "^1.0.0",
"electron": "git+https://github.com/castlabs/electron-releases.git#v8.5.2-wvvmp", "electron": "git+https://github.com/castlabs/electron-releases.git#v16.0.4+wvcus",
"electron-builder": "^21.2.0", "electron-builder": "^22.14.5",
"electron-reload": "^1.5.0", "electron-reload": "^1.5.0",
"prettier": "^2.0.4", "prettier": "^2.5.0"
"dot-prop": ">=4.2.1"
}, },
"prettier": "@mastermindzh/prettier-config" "prettier": "@mastermindzh/prettier-config"
} }

View File

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

View File

@@ -18,11 +18,15 @@ const settings = {
port: "apiSettings.port", port: "apiSettings.port",
}, },
mpris: "mpris", mpris: "mpris",
enableCustomHotkeys: "enableCustomHotkeys",
trayIcon: "trayIcon",
enableDiscord: "enableDiscord",
windowBounds: { windowBounds: {
root: "windowBounds", root: "windowBounds",
width: "windowBounds.width", width: "windowBounds.width",
height: "windowBounds.height", height: "windowBounds.height",
}, },
minimizeOnClose: "minimizeOnClose",
}; };
module.exports = settings; module.exports = settings;

View File

@@ -9,12 +9,12 @@ const {
} = require("./scripts/settings"); } = require("./scripts/settings");
const { addTray, refreshTray } = require("./scripts/tray"); const { addTray, refreshTray } = require("./scripts/tray");
const { addMenu } = require("./scripts/menu"); const { addMenu } = require("./scripts/menu");
const path = require("path"); const path = require("path");
const tidalUrl = "https://listen.tidal.com"; const tidalUrl = "https://listen.tidal.com";
const expressModule = require("./scripts/express"); const expressModule = require("./scripts/express");
const mediaKeys = require("./constants/mediaKeys"); const mediaKeys = require("./constants/mediaKeys");
const mediaInfoModule = require("./scripts/mediaInfo"); const mediaInfoModule = require("./scripts/mediaInfo");
const discordModule = require("./scripts/discord");
const globalEvents = require("./constants/globalEvents"); const globalEvents = require("./constants/globalEvents");
let mainWindow; let mainWindow;
@@ -29,6 +29,14 @@ if (!app.isPackaged) {
}); });
} }
/**
* Update the menuBarVisbility according to the store value
*
*/
function syncMenuBarWithStore() {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
}
function createWindow(options = {}) { function createWindow(options = {}) {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
@@ -44,10 +52,11 @@ function createWindow(options = {}) {
preload: path.join(__dirname, "preload.js"), preload: path.join(__dirname, "preload.js"),
plugins: true, plugins: true,
devTools: true, // I like tinkering, others might too devTools: true, // I like tinkering, others might too
enableRemoteModule: true,
}, },
}); });
mainWindow.setMenuBarVisibility(store.get(settings.menuBar)); syncMenuBarWithStore();
// load the Tidal website // load the Tidal website
mainWindow.loadURL(tidalUrl); mainWindow.loadURL(tidalUrl);
@@ -55,6 +64,14 @@ function createWindow(options = {}) {
// run stuff after first load // run stuff after first load
mainWindow.webContents.once("did-finish-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. // Emitted when the window is closed.
mainWindow.on("closed", function () { mainWindow.on("closed", function () {
closeSettingsWindow(); closeSettingsWindow();
@@ -83,9 +100,10 @@ app.on("ready", () => {
addMenu(); addMenu();
createSettingsWindow(); createSettingsWindow();
addGlobalShortcuts(); addGlobalShortcuts();
addTray({ icon }); store.get(settings.trayIcon) && addTray({ icon }) && refreshTray();
refreshTray();
store.get(settings.api) && expressModule.run(mainWindow); store.get(settings.api) && expressModule.run(mainWindow);
store.get(settings.enableDiscord) && discordModule.initRPC();
// mainWindow.webContents.openDevTools();
}); });
app.on("activate", function () { app.on("activate", function () {
@@ -97,7 +115,6 @@ app.on("activate", function () {
}); });
// IPC // IPC
ipcMain.on(globalEvents.updateInfo, (event, arg) => { ipcMain.on(globalEvents.updateInfo, (event, arg) => {
mediaInfoModule.update(arg); mediaInfoModule.update(arg);
}); });
@@ -109,11 +126,18 @@ ipcMain.on(globalEvents.showSettings, (event, arg) => {
showSettingsWindow(); showSettingsWindow();
}); });
ipcMain.on(globalEvents.updateStatus, (event, arg) => { ipcMain.on(globalEvents.refreshMenuBar, (event, arg) => {
mediaInfoModule.updateStatus(arg); syncMenuBarWithStore();
}); });
ipcMain.on(globalEvents.storeChanged, (event, arg) => { ipcMain.on(globalEvents.storeChanged, (event, arg) => {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar)); 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) => { ipcMain.on(globalEvents.error, (event, arg) => {

View File

@@ -17,7 +17,11 @@ function refreshSettings() {
api.checked = store.get(settings.api); api.checked = store.get(settings.api);
port.value = store.get(settings.apiSettings.port); port.value = store.get(settings.apiSettings.port);
menuBar.checked = store.get(settings.menuBar); menuBar.checked = store.get(settings.menuBar);
trayIcon.checked = store.get(settings.trayIcon);
mpris.checked = store.get(settings.mpris); mpris.checked = store.get(settings.mpris);
enableCustomHotkeys.checked = store.get(settings.enableCustomHotkeys);
enableDiscord.checked = store.get(settings.enableDiscord);
minimizeOnClose.checked = store.get(settings.minimizeOnClose);
} }
/** /**
@@ -76,7 +80,11 @@ window.addEventListener("DOMContentLoaded", () => {
api = get("apiCheckbox"); api = get("apiCheckbox");
port = get("port"); port = get("port");
menuBar = get("menuBar"); menuBar = get("menuBar");
trayIcon = get("trayIcon");
minimizeOnClose = get("minimizeOnClose");
mpris = get("mprisCheckbox"); mpris = get("mprisCheckbox");
enableCustomHotkeys = get("enableCustomHotkeys");
enableDiscord = get("enableDiscord");
refreshSettings(); refreshSettings();
@@ -85,5 +93,9 @@ window.addEventListener("DOMContentLoaded", () => {
addInputListener(api, settings.api); addInputListener(api, settings.api);
addInputListener(port, settings.apiSettings.port); addInputListener(port, settings.apiSettings.port);
addInputListener(menuBar, settings.menuBar); addInputListener(menuBar, settings.menuBar);
addInputListener(trayIcon, settings.trayIcon);
addInputListener(mpris, settings.mpris); addInputListener(mpris, settings.mpris);
addInputListener(enableCustomHotkeys, settings.enableCustomHotkeys);
addInputListener(enableDiscord, settings.enableDiscord);
addInputListener(minimizeOnClose, settings.minimizeOnClose);
}); });

View File

@@ -68,15 +68,45 @@
</label> </label>
</div> </div>
</div> </div>
<div class="section">
<h3>System</h3>
<div class="option">
<h4>Tray icon</h4>
<p>
Show Tidal-hifi's tray icon<br />
</p>
<label class="switch">
<input id="trayIcon" type="checkbox">
<span class="slider round"></span>
</label>
</div>
<div class="option">
<h4>Minimize on Close</h4>
<p>
Minimize window on close instead <br />
</p>
<label class="switch">
<input id="minimizeOnClose" type="checkbox">
<span class="slider round"></span>
</label>
</div>
<div class="option">
<h4>Hotkeys</h4>
<p>
Enables extra hotkeys to achieve feature parity with the <a href = "javascript:openExternal('https://defkey.com/tidal-desktop-shortcuts')">desktop apps</a><br />
</p>
<label class="switch">
<input id="enableCustomHotkeys" type="checkbox">
<span class="slider round"></span>
</label>
</div>
</div>
</section> </section>
<section id="api" class="tab-panel"> <section id="api" class="tab-panel">
<div class="section"> <div class="section">
<h3>Api</h3> <h3>Api</h3>
<p style="margin-bottom: 15px;"> <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. 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> </p>
<div class="option"> <div class="option">
@@ -104,18 +134,14 @@
</label> </label>
</div> </div>
</div> </div>
<button onClick="restart()">Restart Tidal-hifi</button>
</section> </section>
<section id="integrations" class="tab-panel"> <section id="integrations" class="tab-panel">
<div class="section"> <div class="section">
<h3>integrations</h3> <h3>integrations</h3>
<p style="margin-bottom: 15px;"> <p style="margin-bottom: 15px;">
Tidal-hifi is extensible trough the use of integrations. You can enable or disable integrations here Tidal-hifi is extensible trough the use of integrations. You can enable or disable integrations here
<br />
* not all integrations require restarting but some do, your best bet is to restart :)
</p> </p>
<!-- disabled until the 403 with mpris-service/dbus is fixed --> <div class="option">
<!-- <div class="option">
<h4>mpris-player</h4> <h4>mpris-player</h4>
<p> <p>
Whether to enable the mpris media player controls for Linux systems Whether to enable the mpris media player controls for Linux systems
@@ -124,8 +150,17 @@
<input id="mprisCheckbox" type="checkbox"> <input id="mprisCheckbox" type="checkbox">
<span class="slider round"></span> <span class="slider round"></span>
</label> </label>
</div> --> </div>
<button onClick="restart()">Restart Tidal-hifi</button> <div class="option">
<h4>Discord RPC</h4>
<p>
Show what you're listening to on Discord
</p>
<label class="switch">
<input id="enableDiscord" type="checkbox">
<span class="slider round"></span>
</label>
</div>
</div> </div>
</section> </section>
<section id="about" class="tab-panel"> <section id="about" class="tab-panel">
@@ -138,6 +173,9 @@
</div> </div>
</section> </section>
<small>Some settings require a restart of Tidal-hifi. To do so click the button below:</small>
<button onClick="restart()" style ="width: 100%">Restart Tidal-hifi</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,5 @@
const { setTitle, getTitle } = require("./scripts/window-functions"); const { setTitle } = require("./scripts/window-functions");
const { dialog, process } = require("electron").remote; const { dialog, process, Notification } = require("electron").remote;
const { store, settings } = require("./scripts/settings"); const { store, settings } = require("./scripts/settings");
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
const { app } = require("electron").remote; const { app } = require("electron").remote;
@@ -7,10 +7,14 @@ const { downloadFile } = require("./scripts/download");
const statuses = require("./constants/statuses"); const statuses = require("./constants/statuses");
const hotkeys = require("./scripts/hotkeys"); const hotkeys = require("./scripts/hotkeys");
const globalEvents = require("./constants/globalEvents"); const globalEvents = require("./constants/globalEvents");
const notifier = require("node-notifier");
const notificationPath = `${app.getPath("userData")}/notification.jpg`; const notificationPath = `${app.getPath("userData")}/notification.jpg`;
let currentSong = ""; let currentSong = "";
// let player; let player;
let currentPlayStatus = statuses.paused;
let progressBarTime;
let currentTimeChanged = false;
let currentTime;
let currentURL = undefined;
const elements = { const elements = {
play: '*[data-test="play"]', play: '*[data-test="play"]',
@@ -18,7 +22,7 @@ const elements = {
next: '*[data-test="next"]', next: '*[data-test="next"]',
previous: 'button[data-test="previous"]', previous: 'button[data-test="previous"]',
title: '*[data-test^="footer-track-title"]', title: '*[data-test^="footer-track-title"]',
artists: '*[class^="mediaArtists"]', artists: '*[data-test^="grid-item-detail-text-title-artist"]',
home: '*[data-test="menu--home"]', home: '*[data-test="menu--home"]',
back: '[class^="backwardButton"]', back: '[class^="backwardButton"]',
forward: '[class^="forwardButton"]', forward: '[class^="forwardButton"]',
@@ -29,7 +33,15 @@ const elements = {
account: '*[data-test^="profile-image-button"]', account: '*[data-test^="profile-image-button"]',
settings: '*[data-test^="open-settings"]', settings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]', media: '*[data-test="current-media-imagery"]',
image: '*[class^="image--"]', image: "img",
current: '*[data-test="current-time"]',
duration: '*[data-test="duration-time"]',
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"]',
/** /**
* Get an element from the dom * Get an element from the dom
@@ -48,7 +60,43 @@ const elements = {
if (figure) { if (figure) {
const mediaElement = figure.querySelector(this["image"]); const mediaElement = figure.querySelector(this["image"]);
if (mediaElement) { if (mediaElement) {
return mediaElement.src; return mediaElement.src.replace("80x80", "640x640");
}
}
return "";
},
getArtists: function () {
const footer = this.get("footer");
if (footer) {
const artists = footer.querySelector(this["artists"]);
if (artists) {
return artists.innerText;
}
}
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;
}
} }
} }
@@ -101,55 +149,41 @@ function playPause() {
* https://defkey.com/tidal-desktop-shortcuts * https://defkey.com/tidal-desktop-shortcuts
*/ */
function addHotKeys() { function addHotKeys() {
hotkeys.add("Control+p", function () { if (store.get(settings.enableCustomHotkeys)) {
elements.click("account").click("settings"); hotkeys.add("Control+p", function () {
}); elements.click("account").click("settings");
hotkeys.add("Control+l", function () { });
handleLogout(); hotkeys.add("Control+l", function () {
}); handleLogout();
});
hotkeys.add("Control+h", function () { hotkeys.add("Control+h", function () {
elements.click("home"); elements.click("home");
}); });
hotkeys.add("backspace", function () { hotkeys.add("backspace", function () {
elements.click("back"); elements.click("back");
}); });
hotkeys.add("shift+backspace", function () { hotkeys.add("shift+backspace", function () {
elements.click("forward"); elements.click("forward");
}); });
hotkeys.add("control+f", function () { hotkeys.add("control+u", function () {
elements.focus("search"); // reloading window without cache should show the update bar if applicable
}); window.location.reload(true);
});
hotkeys.add("control+u", function () { hotkeys.add("control+r", function () {
// reloading window without cache should show the update bar if applicable elements.click("repeat");
window.location.reload(true); });
}); }
hotkeys.add("control+left", function () { // always add the hotkey for the settings window
elements.click("previous"); hotkeys.add("control+=", function () {
ipcRenderer.send(globalEvents.showSettings);
}); });
hotkeys.add("control+0", function () {
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 () {
ipcRenderer.send(globalEvents.showSettings); ipcRenderer.send(globalEvents.showSettings);
}); });
} }
@@ -172,8 +206,8 @@ function handleLogout() {
}, },
function (response) { function (response) {
if (logoutOptions.indexOf("Yes, please") == response) { if (logoutOptions.indexOf("Yes, please") == response) {
for (i = 0; i < window.localStorage.length; i++) { for (let i = 0; i < window.localStorage.length; i++) {
key = window.localStorage.key(i); const key = window.localStorage.key(i);
if (key.startsWith("_TIDAL_activeSession")) { if (key.startsWith("_TIDAL_activeSession")) {
window.localStorage.removeItem(key); window.localStorage.removeItem(key);
i = window.localStorage.length + 1; i = window.localStorage.length + 1;
@@ -185,6 +219,12 @@ function handleLogout() {
); );
} }
function addFullScreenListeners() {
window.document.addEventListener("fullscreenchange", (event) => {
ipcRenderer.send(globalEvents.refreshMenuBar);
});
}
/** /**
* Add ipc event listeners. * Add ipc event listeners.
* Some actions triggered outside of the site need info from the site. * Some actions triggered outside of the site need info from the site.
@@ -216,9 +256,9 @@ function addIPCEventListeners() {
/** /**
* Update the current status of tidal (e.g playing or paused) * Update the current status of tidal (e.g playing or paused)
*/ */
function updateStatus() { function getCurrentlyPlayingStatus() {
let pause = elements.get("pause"); let pause = elements.get("pause");
let status; let status = undefined;
// if pause button is visible tidal is playing // if pause button is visible tidal is playing
if (pause) { if (pause) {
@@ -226,12 +266,59 @@ function updateStatus() {
} else { } else {
status = statuses.paused; status = statuses.paused;
} }
return status;
}
if (status) { /**
ipcRenderer.send(globalEvents.updateStatus, status); * Convert the duration from MM:SS to seconds
// if (player) { * @param {*} duration
// player.playbackStatus = status == statuses.paused ? "Paused" : "Playing"; */
// } 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.message, icon: options.icon }).show();
}
if (player) {
player.metadata = {
...player.metadata,
...{
"xesam:title": options.title,
"xesam:artist": [options.message],
"xesam:album": options.album,
"mpris:artUrl": options.image,
"mpris:length": convertDuration(options.duration) * 1000 * 1000,
},
};
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 sets the track URL as currentURL, If it's a video it will set currentURL to undefined.
*/
function updateURL() {
const URLelement = elements.get("title").querySelector("a");
switch (URLelement) {
case null:
currentURL = undefined;
break;
default:
const id = URLelement.href.replace(/[^0-9]/g, "");
currentURL = `https://tidal.com/browse/track/${id}`;
break;
} }
} }
@@ -240,109 +327,141 @@ function updateStatus() {
*/ */
setInterval(function () { setInterval(function () {
const title = elements.getText("title"); const title = elements.getText("title");
const artists = elements.getText("artists"); const artists = elements.getArtists();
const album = elements.getAlbumName();
const current = elements.getText("current");
const duration = elements.getText("duration");
const appName = "Tidal Hifi";
const progressBarcurrentTime = elements.get("bar").getAttribute("aria-valuenow");
const songDashArtistTitle = `${title} - ${artists}`; const songDashArtistTitle = `${title} - ${artists}`;
const currentStatus = getCurrentlyPlayingStatus();
const options = {
title,
message: artists,
album: album,
status: currentStatus,
url: currentURL,
current: current,
duration: duration,
"app-name": appName,
};
updateStatus(); const playStatusChanged = currentStatus !== currentPlayStatus;
const progressBarTimeChanged = progressBarcurrentTime !== progressBarTime;
const titleOrArtistChanged = currentSong !== songDashArtistTitle;
if (getTitle() !== songDashArtistTitle) { if (titleOrArtistChanged || playStatusChanged || progressBarTimeChanged || currentTimeChanged) {
// update title, url and play info with new info
setTitle(songDashArtistTitle); setTitle(songDashArtistTitle);
updateURL();
currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus;
if (currentSong !== songDashArtistTitle) { // check progress bar value and make sure current stays up to date after switch
currentSong = songDashArtistTitle; if (progressBarTime != progressBarcurrentTime && !titleOrArtistChanged) {
const image = elements.getSongIcon(); progressBarTime = progressBarcurrentTime;
currentTime = options.current;
const options = { options.duration = duration;
title, currentTimeChanged = true;
message: artists,
};
new Promise((resolve, reject) => {
if (image.startsWith("http")) {
downloadFile(image, notificationPath).then(
() => {
options.icon = notificationPath;
resolve();
},
() => {
reject();
}
);
} else {
reject();
}
}).then(
() => {
ipcRenderer.send(globalEvents.updateInfo, options);
store.get(settings.notifications) && notifier.notify(options);
// if (player) {
// player.metadata = {
// ...player.metadata,
// ...{
// "xesam:title": title,
// "xesam:artist": [artists],
// "mpris:artUrl": image,
// },
// };
// }
},
() => {}
);
} }
if (currentTimeChanged) {
if (options.current == currentTime && currentStatus != "paused") return;
currentTime = options.current;
currentTimeChanged = false;
}
// make sure current is set to 0 if title changes
if (titleOrArtistChanged) {
options.current = "0:00";
currentTime = options.current;
progressBarTime = progressBarcurrentTime;
}
const image = elements.getSongIcon();
new Promise((resolve) => {
if (image.startsWith("http")) {
options.image = image;
downloadFile(image, notificationPath).then(
() => {
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, titleOrArtistChanged);
},
() => {}
);
} }
}, 200); }, 200);
// if (process.platform === "linux" && store.get(settings.mpris)) { if (process.platform === "linux" && store.get(settings.mpris)) {
// try { try {
// const Player = require("mpris-service"); const Player = require("mpris-service");
// player = Player({ player = Player({
// name: "tidal-hifi", name: "tidal-hifi",
// identity: "tidal-hifi", identity: "tidal-hifi",
// supportedUriSchemes: ["file"], supportedUriSchemes: ["file"],
// supportedMimeTypes: [ supportedMimeTypes: [
// "audio/mpeg", "audio/mpeg",
// "audio/flac", "audio/flac",
// "audio/x-flac", "audio/x-flac",
// "application/ogg", "application/ogg",
// "audio/wav", "audio/wav",
// ], ],
// supportedInterfaces: ["player"], supportedInterfaces: ["player"],
// desktopEntry: "tidal-hifi", desktopEntry: "tidal-hifi",
// }); });
// // Events // Events
// var events = { var events = {
// next: "next", next: "next",
// previous: "previous", previous: "previous",
// pause: "pause", pause: "pause",
// playpause: "playpause", playpause: "playpause",
// stop: "stop", stop: "stop",
// play: "play", play: "play",
// loopStatus: "repeat", loopStatus: "repeat",
// shuffle: "shuffle", shuffle: "shuffle",
// seek: "seek", seek: "seek",
// }; };
// Object.keys(events).forEach(function (eventName) { Object.keys(events).forEach(function (eventName) {
// player.on(eventName, function () { player.on(eventName, function () {
// const eventValue = events[eventName]; const eventValue = events[eventName];
// switch (events[eventValue]) { switch (events[eventValue]) {
// case events.playpause: case events.playpause:
// playPause(); playPause();
// break; break;
// default: default:
// elements.click(eventValue); elements.click(eventValue);
// } }
// }); });
// }); });
// Override get position function
player.getPosition = function () {
return convertDuration(elements.getText("current")) * 1000 * 1000;
};
// player.on("quit", function () { player.on("quit", function () {
// app.quit(); app.quit();
// }); });
// } catch (exception) { } catch (exception) {
// console.log("player api not working"); console.log("player api not working");
// } }
// } }
addHotKeys(); addHotKeys();
addIPCEventListeners(); addIPCEventListeners();
addFullScreenListeners();

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

@@ -0,0 +1,85 @@
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.artist
? mediaInfoModule.mediaInfo.artist
: "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.artist,
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 }).catch(console.error);
discordModule.rpc = rpc;
rpc.on("ready", () => {
rpc.setActivity(idleStatus);
});
ipcMain.on(globalEvents.updateInfo, observer);
};
/**
* Remove any RPC connection with discord and remove the event listener on globalEvents.updateInfo
*/
discordModule.unRPC = function () {
rpc.clearActivity();
rpc.destroy();
rpc = false;
discordModule.rpc = rpc;
ipcMain.removeListener(globalEvents.updateInfo, observer);
};
module.exports = discordModule;

View File

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

View File

@@ -14,7 +14,11 @@ const store = new Store({
apiSettings: { apiSettings: {
port: 47836, port: 47836,
}, },
trayIcon: true,
minimizeOnClose : false,
mpris: false, mpris: false,
enableCustomHotkeys: false,
enableDiscord: false,
windowBounds: { width: 800, height: 600 }, windowBounds: { width: 800, height: 600 },
}, },
}); });

View File

@@ -1,19 +1,49 @@
const { Tray } = require("electron"); const { Tray, app } = require("electron");
const { getMenu } = require("./menu"); const { Menu } = require("electron");
const { getMenu, mainMenu } = require("./menu");
const { store, settings } = require("./settings");
const trayModule = {}; const trayModule = {};
let tray; let tray;
trayModule.addTray = function(options = { icon: "" }) { trayModule.addTray = function (options = { icon: "" }) {
tray = new Tray(options.icon); tray = new Tray(options.icon);
}; };
trayModule.refreshTray = function() { trayModule.refreshTray = function (mainWindow) {
tray.on("click", function(e) { if (!tray) {
// do nothing on click trayModule.addTray();
}
tray.on("click", function (e) {
if (mainWindow) {
mainWindow.show();
}
}); });
tray.setToolTip("Tidal-hifi"); tray.setToolTip("Tidal-hifi");
tray.setContextMenu(getMenu());
if (mainWindow && store.get(settings.minimizeOnClose)) {
tray.setContextMenu(
Menu.buildFromTemplate([
{
label: "Toggle Window",
click: function () {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
},
},
{
label: "Quit",
click: function () {
mainWindow.destroy();
app.quit();
},
},
...mainMenu, //we add menu items from the other context
])
);
} else {
tray.setContextMenu(getMenu());
}
}; };
module.exports = trayModule; module.exports = trayModule;