Compare commits

...

36 Commits
2.1.0 ... 2.5.0

Author SHA1 Message Date
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
22 changed files with 6581 additions and 1278 deletions

View File

@@ -12,7 +12,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
@@ -22,7 +22,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 +32,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

@@ -12,7 +12,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
@@ -26,7 +26,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 +40,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,52 @@ 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.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 ## 2.1.0
- [Mar0xy](https://github.com/Mar0xy) added Discord integration. - [Mar0xy](https://github.com/Mar0xy) added Discord integration.

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)
@@ -18,7 +18,9 @@ The web version of [listen.tidal.com](listen.tidal.com) running in electron with
- [Using source](#using-source) - [Using source](#using-source)
- [features](#features) - [features](#features)
- [Integrations](#integrations) - [Integrations](#integrations)
- [not included](#not-included)
- [Known bugs](#known-bugs) - [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)
@@ -39,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
@@ -63,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
@@ -74,15 +76,32 @@ To install and work with the code on this project follow these steps:
- Notifications - Notifications
- Custom hotkeys ([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
- Custom [integrations](#integrations)
- [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+=`) - [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+=`)
## 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
@@ -108,7 +127,7 @@ Sometimes it's just easier to start over, cover my own needs and then making it
## Buy me a coffee? Please don't ## Buy me a coffee? Please don't
Instead spend some money on a charity I care for: [kwf.nl](secure.kwf.nl/donation). 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) Inspired by [haydenjames' issue](https://github.com/Mastermindzh/tidal-hifi/issues/27#issuecomment-704198429)
## Images ## Images

View File

@@ -34,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 = 2.0.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/2.0.0.zip
source = tidal-hifi.desktop
sha512sums = 68fce020128b637fd37383a5afae71d2e4f3eec2d6b4bfcd7f410dcec18c91327d163deb21750e2251967a31f83b2b16092d11d3ff500a1fecd8c595448f380e
sha512sums = 35f38ac308b871c1822d7f6f760f2fb54c3748cf769822cb0f0dfb90f0f5754ba9316da5e903a0d2e9839de3a43ec76f238f3f2e44021956fa1da19142081349
pkgname = tidal-hifi-git

View File

@@ -1,58 +0,0 @@
# Maintainer: Rick van Lieshout <info@rickvanlieshout.com>
_pkgname=tidal-hifi
pkgname="$_pkgname-git"
pkgver=2.0.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=('68fce020128b637fd37383a5afae71d2e4f3eec2d6b4bfcd7f410dcec18c91327d163deb21750e2251967a31f83b2b16092d11d3ff500a1fecd8c595448f380e'
'35f38ac308b871c1822d7f6f760f2fb54c3748cf769822cb0f0dfb90f0f5754ba9316da5e903a0d2e9839de3a43ec76f238f3f2e44021956fa1da19142081349')
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.
npm run build-arch
}
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

7208
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": "2.1.0", "version": "2.5.0",
"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": {
@@ -23,21 +23,20 @@
"homepage": "https://github.com/Mastermindzh/tidal-hifi", "homepage": "https://github.com/Mastermindzh/tidal-hifi",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"discord-rpc": "^3.2.0", "discord-rpc": "^4.0.1",
"electron-store": "^5.1.1", "electron-store": "^8.0.1",
"express": "^4.17.1", "express": "^4.17.1",
"hotkeys-js": "^3.7.6", "hotkeys-js": "^3.8.7",
"mpris-service": "^2.1.0", "mpris-service": "^2.1.2",
"node-notifier": "^9.0.1", "node-notifier": "^10.0.0",
"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

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

View File

@@ -26,6 +26,7 @@ const settings = {
width: "windowBounds.width", width: "windowBounds.width",
height: "windowBounds.height", height: "windowBounds.height",
}, },
minimizeOnClose: "minimizeOnClose",
}; };
module.exports = settings; module.exports = settings;

View File

@@ -17,7 +17,6 @@ const mediaInfoModule = require("./scripts/mediaInfo");
const discordModule = require("./scripts/discord"); const discordModule = require("./scripts/discord");
const globalEvents = require("./constants/globalEvents"); const globalEvents = require("./constants/globalEvents");
let mainWindow; let mainWindow;
let icon = path.join(__dirname, "../assets/icon.png"); let icon = path.join(__dirname, "../assets/icon.png");
@@ -45,6 +44,7 @@ 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,
}, },
}); });
@@ -56,6 +56,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();
@@ -98,7 +106,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);
}); });
@@ -110,15 +117,12 @@ ipcMain.on(globalEvents.showSettings, (event, arg) => {
showSettingsWindow(); showSettingsWindow();
}); });
ipcMain.on(globalEvents.updateStatus, (event, arg) => {
mediaInfoModule.updateStatus(arg);
});
ipcMain.on(globalEvents.storeChanged, (event, arg) => { ipcMain.on(globalEvents.storeChanged, (event, arg) => {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar)); mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
if(store.get(settings.enableDiscord) && !discordModule.rpc) { if (store.get(settings.enableDiscord) && !discordModule.rpc) {
discordModule.initRPC(); discordModule.initRPC();
} else if(!store.get(settings.enableDiscord) && discordModule.rpc) { } else if (!store.get(settings.enableDiscord) && discordModule.rpc) {
discordModule.unRPC(); discordModule.unRPC();
} }
}); });

View File

@@ -21,6 +21,7 @@ function refreshSettings() {
mpris.checked = store.get(settings.mpris); mpris.checked = store.get(settings.mpris);
enableCustomHotkeys.checked = store.get(settings.enableCustomHotkeys); enableCustomHotkeys.checked = store.get(settings.enableCustomHotkeys);
enableDiscord.checked = store.get(settings.enableDiscord); enableDiscord.checked = store.get(settings.enableDiscord);
minimizeOnClose.checked = store.get(settings.minimizeOnClose);
} }
/** /**
@@ -80,6 +81,7 @@ window.addEventListener("DOMContentLoaded", () => {
port = get("port"); port = get("port");
menuBar = get("menuBar"); menuBar = get("menuBar");
trayIcon = get("trayIcon"); trayIcon = get("trayIcon");
minimizeOnClose = get("minimizeOnClose");
mpris = get("mprisCheckbox"); mpris = get("mprisCheckbox");
enableCustomHotkeys = get("enableCustomHotkeys"); enableCustomHotkeys = get("enableCustomHotkeys");
enableDiscord = get("enableDiscord"); enableDiscord = get("enableDiscord");
@@ -95,4 +97,5 @@ window.addEventListener("DOMContentLoaded", () => {
addInputListener(mpris, settings.mpris); addInputListener(mpris, settings.mpris);
addInputListener(enableCustomHotkeys, settings.enableCustomHotkeys); addInputListener(enableCustomHotkeys, settings.enableCustomHotkeys);
addInputListener(enableDiscord, settings.enableDiscord); addInputListener(enableDiscord, settings.enableDiscord);
addInputListener(minimizeOnClose, settings.minimizeOnClose);
}); });

View File

@@ -80,6 +80,16 @@
<span class="slider round"></span> <span class="slider round"></span>
</label> </label>
</div> </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"> <div class="option">
<h4>Hotkeys</h4> <h4>Hotkeys</h4>
<p> <p>

View File

@@ -1,4 +1,4 @@
const { setTitle, getTitle } = require("./scripts/window-functions"); const { setTitle } = require("./scripts/window-functions");
const { dialog, process } = require("electron").remote; const { dialog, process } = require("electron").remote;
const { store, settings } = require("./scripts/settings"); const { store, settings } = require("./scripts/settings");
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
@@ -11,6 +11,11 @@ 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 +23,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^="css-14o5h2y"]', 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"]',
@@ -30,7 +35,10 @@ const elements = {
settings: '*[data-test^="open-settings"]', settings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]', media: '*[data-test="current-media-imagery"]',
image: "img", image: "img",
url: 'a[href*="/track/"]', current: '*[data-test="current-time"]',
duration: '*[data-test="duration-time"]',
bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer",
/** /**
* Get an element from the dom * Get an element from the dom
@@ -56,6 +64,19 @@ const elements = {
return ""; 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)";
},
/** /**
* Shorthand function to get the text of a dom element * Shorthand function to get the text of a dom element
* @param {*} key key in elements object to fetch * @param {*} key key in elements object to fetch
@@ -200,9 +221,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) {
@@ -210,40 +231,118 @@ 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
* @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);
store.get(settings.notifications) && notify && notifier.notify(options);
if (player) { if (player) {
player.playbackStatus = status == statuses.paused ? "Paused" : "Playing"; player.metadata = {
...player.metadata,
...{
"xesam:title": options.title,
"xesam:artist": [options.message],
"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;
}
}
/** /**
* Watch for song changes and update title + notify * Watch for song changes and update title + notify
*/ */
setInterval(function () { setInterval(function () {
const title = elements.getText("title"); const title = elements.getText("title");
const url = elements.get("url").href.replace(/[^0-9]/g, ""); const artists = elements.getArtists();
const artists = elements.getText("artists"); 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();
updateStatus();
if (getTitle() !== songDashArtistTitle) {
setTitle(songDashArtistTitle);
if (currentSong !== songDashArtistTitle) {
currentSong = songDashArtistTitle;
const image = elements.getSongIcon();
const options = { const options = {
title, title,
message: artists, message: artists,
url: `https://tidal.com/browse/track/${url}`, status: currentStatus,
url: currentURL,
current: current,
duration: duration,
'app-name': appName,
}; };
new Promise((resolve, reject) => {
const playStatusChanged = currentStatus !== currentPlayStatus;
const progressBarTimeChanged = progressBarcurrentTime !== progressBarTime;
const titleOrArtistChanged = currentSong !== songDashArtistTitle;
if (titleOrArtistChanged || playStatusChanged || progressBarTimeChanged || currentTimeChanged) {
// update title, url and play info with new info
setTitle(songDashArtistTitle);
updateURL();
currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus;
// check progress bar value and make sure current stays up to date after switch
if (progressBarTime != progressBarcurrentTime && !titleOrArtistChanged) {
progressBarTime = progressBarcurrentTime;
currentTime = options.current;
options.duration = duration;
currentTimeChanged = true;
}
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")) { if (image.startsWith("http")) {
options.image = image;
downloadFile(image, notificationPath).then( downloadFile(image, notificationPath).then(
() => { () => {
options.icon = notificationPath; options.icon = notificationPath;
@@ -260,24 +359,11 @@ setInterval(function () {
} }
}).then( }).then(
() => { () => {
ipcRenderer.send(globalEvents.updateInfo, options); updateMediaInfo(options, titleOrArtistChanged);
store.get(settings.notifications) && notifier.notify(options);
if (player) {
player.metadata = {
...player.metadata,
...{
"xesam:title": title,
"xesam:artist": [artists],
"mpris:artUrl": image,
},
};
}
}, },
() => {} () => {}
); );
} }
}
}, 200); }, 200);
if (process.platform === "linux" && store.get(settings.mpris)) { if (process.platform === "linux" && store.get(settings.mpris)) {
@@ -323,6 +409,10 @@ if (process.platform === "linux" && store.get(settings.mpris)) {
} }
}); });
}); });
// 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();

View File

@@ -1,52 +1,83 @@
const discordrpc = require("discord-rpc"); const discordrpc = require("discord-rpc");
const clientId = '833617820704440341'; const { app, ipcMain } = require("electron");
const globalEvents = require("../constants/globalEvents");
const clientId = "833617820704440341";
const mediaInfoModule = require("./mediaInfo"); const mediaInfoModule = require("./mediaInfo");
const discordModule = []; const discordModule = [];
let discord; function timeToSeconds(timeArray) {
let minutes = timeArray[0] * 1;
let seconds = minutes * 60 + timeArray[1] * 1;
return seconds;
}
let rpc; 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),
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 = { const idleStatus = {
details: `Browsing Tidal`, details: `Browsing Tidal`,
largeImageKey: 'tidal-hifi-icon', largeImageKey: "tidal-hifi-icon",
largeImageText: 'Tidal HiFi 2.0.0', largeImageText: `Tidal HiFi ${app.getVersion()}`,
instance: false, instance: false,
} };
/**
* Set up the discord rpc and listen on globalEvents.updateInfo
*/
discordModule.initRPC = function () { discordModule.initRPC = function () {
rpc = new discordrpc.Client({ transport: 'ipc' }); rpc = new discordrpc.Client({ transport: "ipc" });
rpc.login({ clientId }).catch(console.error); rpc.login({ clientId }).catch(console.error);
discordModule.rpc = rpc; discordModule.rpc = rpc;
rpc.on('ready', () => { rpc.on("ready", () => {
rpc.setActivity(idleStatus); rpc.setActivity(idleStatus);
})
discord = setInterval(() => {
if (mediaInfoModule.mediaInfo.status == 'paused' && rpc) {
rpc.setActivity(idleStatus);
} else if (rpc) {
rpc.setActivity({
details: `Listening to ${mediaInfoModule.mediaInfo.title}`,
state: mediaInfoModule.mediaInfo.artist,
largeImageKey: 'tidal-hifi-icon',
largeImageText: 'Tidal HiFi 2.0.0',
buttons: [
{ label: "Play on Tidal", url: mediaInfoModule.mediaInfo.url }
],
instance: false,
}); });
} ipcMain.on(globalEvents.updateInfo, observer);
}, 15e3); };
}
/**
* Remove any RPC connection with discord and remove the event listener on globalEvents.updateInfo
*/
discordModule.unRPC = function () { discordModule.unRPC = function () {
clearInterval(discord);
rpc.clearActivity(); rpc.clearActivity();
rpc.destroy(); rpc.destroy();
rpc = false; rpc = false;
discordModule.rpc = rpc; discordModule.rpc = rpc;
} ipcMain.removeListener(globalEvents.updateInfo, observer);
};
module.exports = discordModule; module.exports = discordModule;

View File

@@ -5,7 +5,9 @@ const mediaInfo = {
artist: "", artist: "",
icon: "", icon: "",
status: statuses.paused, status: statuses.paused,
url: "" url: "",
current: "",
duration: ""
}; };
const mediaInfoModule = { const mediaInfoModule = {
mediaInfo, mediaInfo,
@@ -14,20 +16,14 @@ 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.icon = propOrDefault(arg.icon); mediaInfo.icon = propOrDefault(arg.icon);
mediaInfo.url = propOrDefault(arg.url); mediaInfo.url = propOrDefault(arg.url);
}; mediaInfo.status = propOrDefault(arg.status);
mediaInfo.current = propOrDefault(arg.current);
/** mediaInfo.duration = propOrDefault(arg.duration);
* Update tidal's status in the mediaInfo constant
*/
mediaInfoModule.updateStatus = function(status) {
if (Object.values(statuses).includes(status)) {
mediaInfo.status = status;
}
}; };
/** /**

View File

@@ -15,6 +15,7 @@ const store = new Store({
port: 47836, port: 47836,
}, },
trayIcon: true, trayIcon: true,
minimizeOnClose : false,
mpris: false, mpris: false,
enableCustomHotkeys: false, enableCustomHotkeys: false,
enableDiscord: false, enableDiscord: false,

View File

@@ -1,5 +1,7 @@
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;
@@ -7,16 +9,41 @@ trayModule.addTray = function (options = { icon: "" }) {
tray = new Tray(options.icon); tray = new Tray(options.icon);
}; };
trayModule.refreshTray = function () { trayModule.refreshTray = function (mainWindow) {
if (!tray) { if (!tray) {
trayModule.addTray(); trayModule.addTray();
} }
tray.on("click", function (e) { tray.on("click", function (e) {
// do nothing on click if (mainWindow) {
mainWindow.show();
}
}); });
tray.setToolTip("Tidal-hifi"); tray.setToolTip("Tidal-hifi");
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()); tray.setContextMenu(getMenu());
}
}; };
module.exports = trayModule; module.exports = trayModule;