Compare commits

...

15 Commits
2.8.2 ... 4.3.0

Author SHA1 Message Date
d161a68c95 4.3.0 2022-10-05 19:44:04 +02:00
Cukmekerb
9de8cea50e Disable background throttling (#171)
* disable background throttling for consistent setInterval

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

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

* Redesign of the settings window (#168)

* Pr dest (#166)

* Update configuration of the desktop file (#165)

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

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

* Redesign of the settings window

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

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

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

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

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

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

* Fix inconsistent auto-muting (#159)

* fix muting sometimes not working

* fix inconsistent unmuting

* fix bad code in inconsistent muting fig

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

* Switched icon strategies to fix bugs with icons

* fixed tray icon issues

* fixed about :)

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

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

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

* Update functional

* Change to 15

* Replace window.hide, window.restart with counterparts

* Fix openexternal by doing default JS

* Change mouse to pointer for <a> elements

* turn mouse to pointer for exit button

* Fix deprecation of Audio for AudioVideo

* Made a small mistake

* Add Flatpak to readme

* 3.0.0 prep

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

fixes #121

* Added setting to disable HardwareMediaKeyHandling (defaults to false)

fixes #133

Co-authored-by: Marie <marie@kaifa.ch>
2022-04-23 22:59:32 +02:00
32 changed files with 6245 additions and 3113 deletions

View File

@@ -21,7 +21,7 @@ jobs:
- run: npm run build - run: npm run build
build_on_mac: build_on_mac:
runs-on: macOS-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- uses: actions/setup-node@master - uses: actions/setup-node@master

5
.gitignore vendored
View File

@@ -7,3 +7,8 @@ build/linux/arch/*
!build/linux/arch/.SRCINFO !build/linux/arch/.SRCINFO
!build/linux/arch/tidal-hifi.desktop !build/linux/arch/tidal-hifi.desktop
!build/linux/arch/install.sh !build/linux/arch/install.sh
*.css
*.css.map
# JetBrains IDE configuration
.idea

View File

@@ -4,6 +4,67 @@ 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).
## 4.3.0
- Added a setting to disable background throttling ([docs](https://www.electronjs.org/docs/latest/api/browser-window))
## 4.2.0
- New settings window by BlueManCZ
- Fixed the desktop files in electron-builder
- icon is set to new static path based on Arch/Debian
- Name has changed to Tidal-Hifi
-
## 4.1.2
- Changed the category of the desktop file to AudioVideo
- Changed desktop file name to "TIDAL Hi-Fi"
## 4.1.1
- Fixed `cannot read property of undefined` error because of not passing mainWindow around.
- vincens2005, fixed inconsistent auto muting
## 4.1.0
- Added `tidal://` protocol support
- Switched icon strategies to fix bugs with icons
- Fixed tray icon bugs
- Menu now shows in KDE as well
- Toggle window is supported from tray icon
- regular click is still ignored, see [this issue](https://github.com/electron/electron/issues/6773)
- Fixed about tab not showing
- Fixed playback, mpris and API issues
## 4.0.1
- Updated build config to make use of a base file that doesn't build anything.
- This fixes the issue of unwanted extra build targets that were introduced with the electron-builder update
## 4.0.0
- Updated to Electron 19.0.5
## 3.1.1
- Media update timeout set to 500 instead of 200
- Updated property name for duration because of a tidal update
- flag for "disable hardware media keys" now working again
## 3.1.0
- Added a separate advanced options settings panel with flags
- Added gpu-rasterization flag
- config setting `disableHardwareMediaKeys` moved to `flags.disableHardwareMediaKeys`, it will be migrated automatically
## 3.0.0
- Updated to Electron 15
- Fixed the develop "build-unpacked" command
- Added setting to disable multiple tidal-hifi windows (defaults to true)
- Added setting to disable HardwareMediaKeyHandling (defaults to false)
## 2.8.2 ## 2.8.2
- Updated dependencies - Updated dependencies

View File

@@ -12,22 +12,23 @@ The web version of [listen.tidal.com](https://listen.tidal.com) running in elect
<!-- toc --> <!-- toc -->
- [Installation](#installation) - [Installation](#installation)
- [Using releases](#using-releases) * [Using releases](#using-releases)
- [Snap install](#snap-install) * [Snap](#snap)
- [Arch Linux](#arch-linux) * [Arch Linux](#arch-linux)
- [Using source](#using-source) * [Flatpak](#flatpak)
- [features](#features) * [Nix](#nix)
* [Using source](#using-source)
- [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)
- [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) - [Buy me a coffee? Please don't](#buy-me-a-coffee-please-dont)
- [Images](#images) - [Images](#images)
- [settings window](#settings-window) * [Settings window](#settings-window)
- [user setups](#user-setups) * [User setups](#user-setups)
<!-- tocstop --> <!-- tocstop -->
@@ -37,7 +38,7 @@ The web version of [listen.tidal.com](https://listen.tidal.com) running in elect
Various packaged versions of the software are available on the [releases](https://github.com/Mastermindzh/tidal-hifi/releases) tab. Various packaged versions of the software are available on the [releases](https://github.com/Mastermindzh/tidal-hifi/releases) tab.
#### Snap install ### Snap
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:
@@ -61,6 +62,22 @@ Arch Linux users can use the AUR to install tidal-hifi:
trizen tidal-hifi-bin trizen tidal-hifi-bin
``` ```
### Flatpak
To install via [Flatpak](https://flathub.org/apps/details/com.mastermindzh.tidal-hifi) run the following command:
```sh
flatpak install flathub com.mastermindzh.tidal-hifi
```
### Nix
To install with Nix run the following command:
```sh
nix-env -iA nixpkgs.tidal-hifi
```
### Using source ### Using source
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:
@@ -70,7 +87,7 @@ To install and work with the code on this project follow these steps:
- npm install - npm install
- npm start - npm start
## features ## Features
- HiFi playback - HiFi playback
- Notifications - Notifications
@@ -93,7 +110,7 @@ It currently includes:
- MPRIS - MPRIS media player controls/status - MPRIS - MPRIS media player controls/status
- Discord - Shows what you're listening to on Discord. - Discord - Shows what you're listening to on Discord.
### not included 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)
@@ -134,11 +151,11 @@ Inspired by [haydenjames' issue](https://github.com/Mastermindzh/tidal-hifi/issu
## Images ## Images
### settings window ### Settings window
![settings window](./docs/settings-preview.png) ![settings window](./docs/settings-preview.png)
### user setups ### User setups
Some of our users are kind enough to share their usage pictures. 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). 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

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

View File

@@ -1,6 +1,4 @@
extends: ./build/electron-builder.yml extends: ./build/electron-builder.base.yml
linux: linux:
category: Audio
icon: ./assets/icon.png
target: target:
- deb - deb

View File

@@ -1,6 +1,5 @@
extends: ./build/electron-builder.yml extends: ./build/electron-builder.base.yml
linux: linux:
category: Audio icon: icon.png
icon: ./assets/icon.png
target: target:
- pacman - pacman

View File

@@ -1,6 +1,4 @@
extends: ./build/electron-builder.yml extends: ./build/electron-builder.base.yml
linux: linux:
category: Audio
icon: ./assets/TIDAL.icns
target: target:
- rpm - rpm

View File

@@ -1,6 +1,4 @@
extends: ./build/electron-builder.yml extends: ./build/electron-builder.base.yml
linux: linux:
category: Audio
icon: ./assets/icon.png
target: target:
- snap - snap

View File

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

View File

@@ -1,14 +1,5 @@
appId: com.rickvanlieshout.tidal-hifi extends: ./build/electron-builder.base.yml
electronVersion: 8.5.2
electronDownload:
version: 8.5.2-wvvmp
mirror: https://github.com/castlabs/electron-releases/releases/download/v
snap:
plugs:
- default
- screen-inhibit-control
linux: linux:
category: Audio
target: target:
- pacman - pacman
- tar.gz - tar.gz
@@ -17,24 +8,9 @@ linux:
- AppImage - AppImage
- snap - snap
- freebsd - freebsd
executableName: tidal-hifi
desktop:
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.
Icon: assets/icon.png
StartupNotify: true
Terminal: false
Type: Application
Categories: Network;Application;Audio;Video
StartupWMClass: tidal-hifi
X-PulseAudio-Properties: media.role=music
mac:
category: public.app-category.entertainment
win: win:
target: msi target: msi
icon: build/icon.png icon: icon.png
artifactName: "tidalhifi" artifactName: "tidalhifi"
appId: com.rickvanlieshout.tidalhifi appId: com.rickvanlieshout.tidalhifi
executableName: tidalhifi executableName: tidalhifi

BIN
build/icon-inverted.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
build/icon.icns Executable file

Binary file not shown.

7477
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,25 @@
{ {
"name": "tidal-hifi", "name": "tidal-hifi",
"version": "2.8.2", "version": "4.3.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": {
"start": "electron .", "start": "electron .",
"build": "electron-builder --publish=never -c ./build/electron-builder.yml", "build": "npm run builder -- -c ./build/electron-builder.yml",
"build-deb": "electron-builder --publish=never -c ./build/electron-builder.deb.yml", "build-deb": "npm run builder -- -c ./build/electron-builder.deb.yml",
"build-unpacked": "electron-builder --publish=never -c ./build/electron-builder.unpacked.yml", "build-unpacked": "npm run builder -- -c ./build/electron-builder.unpacked.yml",
"build-rpm": "electron-builder --publish=never -c ./build/electron-builder.rpm.yml", "build-rpm": "npm run builder -- -c ./build/electron-builder.rpm.yml",
"build-snap": "electron-builder --publish=never -c ./build/electron-builder.snap.yml", "build-snap": "npm run builder -- -c ./build/electron-builder.snap.yml",
"build-arch": "electron-builder --publish=never -c ./build/electron-builder.pacman.yml", "build-arch": "npm run builder -- -c ./build/electron-builder.pacman.yml",
"build-wl": "electron-builder --publish=never -c ./build/electron-builder.yml -wl", "build-wl": "npm run builder -- -c ./build/electron-builder.yml -wl",
"build-mac": "electron-builder --publish=never -c ./build/electron-builder.yml -m" "build-mac": "npm run builder -- -c ./build/electron-builder.yml -m",
"build-base": "npm run builder -- -c ./build/electron-builder.base.yml",
"prestart": "npm run sass",
"prebuilder": "npm run sass",
"builder": "electron-builder --publish=never",
"sass": "sass ./src/pages/settings/settings.scss ./src/pages/settings/settings.css",
"sass-lint": "sass-lint -vc ./sass-lint.yml ./src/pages/settings/settings.scss",
"sass-lint-fix": "sass-lint-auto-fix ./src/pages/settings/settings.scss --config-sass-lint ./sass-lint.yml"
}, },
"keywords": [ "keywords": [
"electron", "electron",
@@ -24,19 +31,23 @@
"homepage": "https://github.com/Mastermindzh/tidal-hifi", "homepage": "https://github.com/Mastermindzh/tidal-hifi",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@electron/remote": "^2.0.8",
"discord-rpc": "^4.0.1", "discord-rpc": "^4.0.1",
"electron-store": "^8.0.1", "electron-store": "^8.1.0",
"express": "^4.17.1", "express": "^4.18.1",
"hotkeys-js": "^3.8.7", "hotkeys-js": "^3.9.4",
"mpris-service": "^2.1.2", "mpris-service": "^2.1.2",
"request": "^2.88.2" "request": "^2.88.2",
"sass": "^1.54.9"
}, },
"devDependencies": { "devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0", "@mastermindzh/prettier-config": "^1.0.0",
"electron": "git+https://github.com/castlabs/electron-releases.git#15.5.2-wvvmp", "electron": "git+https://github.com/castlabs/electron-releases.git#v19.0.5+wvcus",
"electron-builder": "^22.14.5", "electron-builder": "^23.3.3",
"electron-reload": "^1.5.0", "js-yaml": "^3.14.1",
"prettier": "^2.5.0" "prettier": "^2.7.1",
"sass-lint": "^1.13.1",
"sass-lint-auto-fix": "^0.21.2"
}, },
"prettier": "@mastermindzh/prettier-config" "prettier": "@mastermindzh/prettier-config"
} }

21
sass-lint.yml Normal file
View File

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

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

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

View File

@@ -15,10 +15,17 @@ const settings = {
playBackControl: "playBackControl", playBackControl: "playBackControl",
muteArtists: "muteArtists", muteArtists: "muteArtists",
mutedArtists: "mutedArtists", mutedArtists: "mutedArtists",
disableBackgroundThrottle: "disableBackgroundThrottle",
apiSettings: { apiSettings: {
root: "apiSettings", root: "apiSettings",
port: "apiSettings.port", port: "apiSettings.port",
}, },
singleInstance: "singleInstance",
disableHardwareMediaKeys: "disableHardwareMediaKeys",
flags: {
disableHardwareMediaKeys: "flags.disableHardwareMediaKeys",
gpuRasterization: "flags.gpuRasterization",
},
mpris: "mpris", mpris: "mpris",
enableCustomHotkeys: "enableCustomHotkeys", enableCustomHotkeys: "enableCustomHotkeys",
trayIcon: "trayIcon", trayIcon: "trayIcon",

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

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

View File

@@ -1,4 +1,5 @@
const { app, BrowserWindow, globalShortcut, ipcMain } = require("electron"); require("@electron/remote/main").initialize();
const { app, BrowserWindow, components, globalShortcut, ipcMain, protocol } = require("electron");
const { const {
settings, settings,
store, store,
@@ -16,23 +17,32 @@ const mediaKeys = require("./constants/mediaKeys");
const mediaInfoModule = require("./scripts/mediaInfo"); 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");
const flagValues = require("./constants/flags");
let mainWindow; let mainWindow;
let icon = path.join(__dirname, "../assets/icon.png"); let icon = path.join(__dirname, "../assets/icon.png");
const PROTOCOL_PREFIX = "tidal";
/** setFlags();
* Enable live reload in development builds
*/ function setFlags() {
if (!app.isPackaged) { const flags = store.get().flags;
require("electron-reload")(`${__dirname}`, { if (flags) {
electron: require(`${__dirname}/../node_modules/electron`), for (const [key, value] of Object.entries(flags)) {
if (value) {
flagValues[key].forEach((flag) => {
console.log(`enabling command line switch ${flag.flag} with value ${flag.value}`);
app.commandLine.appendSwitch(flag.flag, flag.value);
}); });
} }
}
}
/** /**
* Fix Display Compositor issue. * Fix Display Compositor issue.
*/ */
app.commandLine.appendSwitch('disable-seccomp-filter-sandbox'); app.commandLine.appendSwitch("disable-seccomp-filter-sandbox");
}
/** /**
* Update the menuBarVisbility according to the store value * Update the menuBarVisbility according to the store value
@@ -42,6 +52,23 @@ function syncMenuBarWithStore() {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar)); mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
} }
/**
* Determine whether the current window is the main window
* if singleInstance is requested.
* If singleInstance isn't requested simply return true
* @returns true if singInstance is not requested, otherwise true/false based on whether the current window is the main window
*/
function isMainInstanceOrMultipleInstancesAllowed() {
if (store.get(settings.singleInstance)) {
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
return false;
}
}
return true;
}
function createWindow(options = {}) { function createWindow(options = {}) {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
@@ -50,22 +77,25 @@ function createWindow(options = {}) {
width: store && store.get(settings.windowBounds.width), width: store && store.get(settings.windowBounds.width),
height: store && store.get(settings.windowBounds.height), height: store && store.get(settings.windowBounds.height),
icon, icon,
tray: true,
backgroundColor: options.backgroundColor, backgroundColor: options.backgroundColor,
webPreferences: { webPreferences: {
affinity: "window",
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,
}, },
}); });
require("@electron/remote/main").enable(mainWindow.webContents);
registerHttpProtocols();
syncMenuBarWithStore(); syncMenuBarWithStore();
// load the Tidal website // load the Tidal website
mainWindow.loadURL(tidalUrl); mainWindow.loadURL(tidalUrl);
if (store.get(settings.disableBackgroundThrottle)) {
// prevent setInterval lag
mainWindow.webContents.setBackgroundThrottling(false);
}
// run stuff after first load // run stuff after first load
mainWindow.webContents.once("did-finish-load", () => {}); mainWindow.webContents.once("did-finish-load", () => {});
@@ -89,6 +119,13 @@ function createWindow(options = {}) {
}); });
} }
function registerHttpProtocols() {
protocol.registerHttpProtocol(PROTOCOL_PREFIX, (request, _callback) => {
mainWindow.loadURL(`${tidalUrl}/${request.url.substring(PROTOCOL_PREFIX.length + 3)}`);
});
app.setAsDefaultProtocolClient(PROTOCOL_PREFIX);
}
function addGlobalShortcuts() { function addGlobalShortcuts() {
Object.keys(mediaKeys).forEach((key) => { Object.keys(mediaKeys).forEach((key) => {
globalShortcut.register(`${key}`, () => { globalShortcut.register(`${key}`, () => {
@@ -100,16 +137,20 @@ function addGlobalShortcuts() {
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.on("ready", () => { app.on("ready", async () => {
app.allowRendererProcessReuse = true; if (isMainInstanceOrMultipleInstancesAllowed()) {
await components.whenReady();
createWindow(); createWindow();
addMenu(); addMenu(mainWindow);
createSettingsWindow(); createSettingsWindow();
addGlobalShortcuts(); addGlobalShortcuts();
store.get(settings.trayIcon) && addTray({ icon }) && refreshTray(); store.get(settings.trayIcon) && addTray(mainWindow, { icon }) && refreshTray();
store.get(settings.api) && expressModule.run(mainWindow); store.get(settings.api) && expressModule.run(mainWindow);
store.get(settings.enableDiscord) && discordModule.initRPC(); store.get(settings.enableDiscord) && discordModule.initRPC();
// mainWindow.webContents.openDevTools(); // mainWindow.webContents.openDevTools();
} else {
app.quit();
}
}); });
app.on("activate", function () { app.on("activate", function () {
@@ -120,23 +161,27 @@ app.on("activate", function () {
} }
}); });
app.on("browser-window-created", (_, window) => {
require("@electron/remote/main").enable(window.webContents);
});
// IPC // IPC
ipcMain.on(globalEvents.updateInfo, (event, arg) => { ipcMain.on(globalEvents.updateInfo, (_event, arg) => {
mediaInfoModule.update(arg); mediaInfoModule.update(arg);
}); });
ipcMain.on(globalEvents.hideSettings, (event, arg) => { ipcMain.on(globalEvents.hideSettings, (_event, _arg) => {
hideSettingsWindow(); hideSettingsWindow();
}); });
ipcMain.on(globalEvents.showSettings, (event, arg) => { ipcMain.on(globalEvents.showSettings, (_event, _arg) => {
showSettingsWindow(); showSettingsWindow();
}); });
ipcMain.on(globalEvents.refreshMenuBar, (event, arg) => { ipcMain.on(globalEvents.refreshMenuBar, (_event, _arg) => {
syncMenuBarWithStore(); syncMenuBarWithStore();
}); });
ipcMain.on(globalEvents.storeChanged, (event, arg) => { ipcMain.on(globalEvents.storeChanged, (_event, _arg) => {
syncMenuBarWithStore(); syncMenuBarWithStore();
if (store.get(settings.enableDiscord) && !discordModule.rpc) { if (store.get(settings.enableDiscord) && !discordModule.rpc) {
@@ -146,6 +191,6 @@ ipcMain.on(globalEvents.storeChanged, (event, arg) => {
} }
}); });
ipcMain.on(globalEvents.error, (event, arg) => { ipcMain.on(globalEvents.error, (event, _arg) => {
console.log(event); console.log(event);
}); });

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,12 +9,17 @@ let trayIcon,
api, api,
port, port,
menuBar, menuBar,
mutedArtists; mutedArtists,
disableBackgroundThrottle,
singleInstance,
disableHardwareMediaKeys,
gpuRasterization;
const { store, settings } = require("./../../scripts/settings"); const { store, settings } = require("./../../scripts/settings");
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
const globalEvents = require("./../../constants/globalEvents"); const globalEvents = require("./../../constants/globalEvents");
const remote = require("@electron/remote");
const { app } = remote;
/** /**
* Sync the UI forms with the current settings * Sync the UI forms with the current settings
*/ */
@@ -31,31 +36,34 @@ function refreshSettings() {
minimizeOnClose.checked = store.get(settings.minimizeOnClose); minimizeOnClose.checked = store.get(settings.minimizeOnClose);
muteArtists.checked = store.get(settings.muteArtists); muteArtists.checked = store.get(settings.muteArtists);
mutedArtists.value = store.get(settings.mutedArtists).join("\n"); mutedArtists.value = store.get(settings.mutedArtists).join("\n");
singleInstance.checked = store.get(settings.singleInstance);
disableHardwareMediaKeys.checked = store.get(settings.flags.disableHardwareMediaKeys);
gpuRasterization.checked = store.get(settings.flags.gpuRasterization);
disableBackgroundThrottle.checked = store.get("disableBackgroundThrottle");
} }
/** /**
* Open an url in the default browsers * Open an url in the default browsers
*/ */
window.openExternal = function (url) { function openExternal(url) {
const { shell } = require("electron"); const { shell } = require("electron");
shell.openExternal(url); shell.openExternal(url);
}; }
/** /**
* hide the settings window * hide the settings window
*/ */
window.hide = function () { function hide() {
ipcRenderer.send(globalEvents.hideSettings); ipcRenderer.send(globalEvents.hideSettings);
}; }
/** /**
* Restart tidal-hifi after changes * Restart tidal-hifi after changes
*/ */
window.restart = function () { function restart() {
const remote = require("electron").remote; app.relaunch();
remote.app.relaunch(); app.quit();
remote.app.exit(0); }
};
/** /**
* Bind UI components to functions after DOMContentLoaded * Bind UI components to functions after DOMContentLoaded
@@ -65,8 +73,16 @@ window.addEventListener("DOMContentLoaded", () => {
return document.getElementById(id); return document.getElementById(id);
} }
document.getElementById("close").addEventListener("click", hide);
document.getElementById("restart").addEventListener("click", restart);
document.querySelectorAll(".external-link").forEach((elem) =>
elem.addEventListener("click", function (event) {
openExternal(event.target.getAttribute("data-url"));
})
);
function addInputListener(source, key) { function addInputListener(source, key) {
source.addEventListener("input", function (event, data) { source.addEventListener("input", function (_event, _data) {
if (this.value === "on") { if (this.value === "on") {
store.set(key, source.checked); store.set(key, source.checked);
} else { } else {
@@ -77,7 +93,7 @@ window.addEventListener("DOMContentLoaded", () => {
} }
function addTextAreaListener(source, key) { function addTextAreaListener(source, key) {
source.addEventListener("input", function (event, data) { source.addEventListener("input", function (_event, _data) {
store.set(key, source.value.split("\n")); store.set(key, source.value.split("\n"));
ipcRenderer.send(globalEvents.storeChanged); ipcRenderer.send(globalEvents.storeChanged);
}); });
@@ -103,6 +119,10 @@ window.addEventListener("DOMContentLoaded", () => {
enableDiscord = get("enableDiscord"); enableDiscord = get("enableDiscord");
muteArtists = get("muteArtists"); muteArtists = get("muteArtists");
mutedArtists = get("mutedArtists"); mutedArtists = get("mutedArtists");
disableBackgroundThrottle = get("disableBackgroundThrottle");
singleInstance = get("singleInstance");
disableHardwareMediaKeys = get("disableHardwareMediaKeys");
gpuRasterization = get("gpuRasterization");
refreshSettings(); refreshSettings();
@@ -118,4 +138,8 @@ window.addEventListener("DOMContentLoaded", () => {
addInputListener(minimizeOnClose, settings.minimizeOnClose); addInputListener(minimizeOnClose, settings.minimizeOnClose);
addInputListener(muteArtists, settings.muteArtists); addInputListener(muteArtists, settings.muteArtists);
addTextAreaListener(mutedArtists, settings.mutedArtists); addTextAreaListener(mutedArtists, settings.mutedArtists);
addInputListener(disableBackgroundThrottle, settings.disableBackgroundThrottle);
addInputListener(singleInstance, settings.singleInstance);
addInputListener(disableHardwareMediaKeys, settings.flags.disableHardwareMediaKeys);
addInputListener(gpuRasterization, settings.flags.gpuRasterization);
}); });

View File

@@ -2,498 +2,257 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>Tidal-hifi settings</title> <title>Tidal Hi-Fi settings</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="./settings.css" />
</head> </head>
<body> <body class="settings-window">
<div class="header"> <div class="settings-window__wrapper">
<h1 class="title">Settings</h1> <div class="settings-window__drag-area"></div>
<a href="javascript:hide();" class="exitWindow"> <a id="close" class="settings-window__close-button" title="Close settings">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 348.333 348.334" class="settings-window__svg-icon">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 348.333 348.334">
<g>
<path fill="white" d="M336.559,68.611L231.016,174.165l105.543,105.549c15.699,15.705,15.699,41.145,0,56.85 <path fill="white" d="M336.559,68.611L231.016,174.165l105.543,105.549c15.699,15.705,15.699,41.145,0,56.85
c-7.844,7.844-18.128,11.769-28.407,11.769c-10.296,0-20.581-3.919-28.419-11.769L174.167,231.003L68.609,336.563 c-7.844,7.844-18.128,11.769-28.407,11.769c-10.296,0-20.581-3.919-28.419-11.769L174.167,231.003L68.609,336.563
c-7.843,7.844-18.128,11.769-28.416,11.769c-10.285,0-20.563-3.919-28.413-11.769c-15.699-15.698-15.699-41.139,0-56.85 c-7.843,7.844-18.128,11.769-28.416,11.769c-10.285,0-20.563-3.919-28.413-11.769c-15.699-15.698-15.699-41.139,0-56.85
l105.54-105.549L11.774,68.611c-15.699-15.699-15.699-41.145,0-56.844c15.696-15.687,41.127-15.687,56.829,0l105.563,105.554 l105.54-105.549L11.774,68.611c-15.699-15.699-15.699-41.145,0-56.844c15.696-15.687,41.127-15.687,56.829,0l105.563,105.554
L279.721,11.767c15.705-15.687,41.139-15.687,56.832,0C352.258,27.466,352.258,52.912,336.559,68.611z" /> L279.721,11.767c15.705-15.687,41.139-15.687,56.832,0C352.258,27.466,352.258,52.912,336.559,68.611z" />
</svg> </svg>
</a> </a>
</div>
<div class="body">
<div class="tabset">
<!-- Tab 1 -->
<input type="radio" name="tabset" id="general" checked />
<label for="general">General</label>
<!-- Tab 2 -->
<input type="radio" name="tabset" id="api" />
<label for="api">Api</label>
<!-- Integrations tab --> <main class="settings">
<input type="radio" name="tabset" id="integrations" /> <input type="radio" name="tab" id="general" checked />
<label for="general">General</label>
<input type="radio" name="tab" id="api" />
<label for="api">API</label>
<input type="radio" name="tab" id="integrations" />
<label for="integrations">Integrations</label> <label for="integrations">Integrations</label>
<!-- about tab --> <input type="radio" name="tab" id="advanced" />
<input type="radio" name="tabset" id="about" /> <label for="advanced">Advanced</label>
<input type="radio" name="tab" id="about" />
<label for="about">About</label> <label for="about">About</label>
<div class="tab-panels"> <div class="tabs">
<section id="general" class="tab-panel"> <section id="general-section" class="tabs__section">
<div class="section"> <div class="group">
<h3>Playback</h3> <p class="group__title">Playback</p>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Notifications</h4> <h4>Notifications</h4>
<p> <p>Show a notification when a new song starts.</p>
Whether to show a notification when a new song starts. </div>
</p>
<label class="switch"> <label class="switch">
<input id="notifications" type="checkbox"> <input id="notifications" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Mute Artists automatically</h4> <h4>Mute Artists automatically</h4>
<p> <p>The following list of artists (1 per line) will be muted automatically.</p>
The following list of artists (1 per line) will be muted automatically. </div>
</p> <label class="switch">
<label class="switch" style="margin-bottom:10px">
<input id="muteArtists" type="checkbox"> <input id="muteArtists" type="checkbox">
<span class="slider round" ></span> <span class="switch__slider"></span>
</label> </label>
<textarea id="mutedArtists" cols="40" rows="5"></textarea>
</div> </div>
<textarea id="mutedArtists" class="textarea" cols="40" rows="5" spellcheck="false"></textarea>
</div> </div>
<div class="section"> <div class="group">
<h3>UI</h3> <p class="group__title">UI</p>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Menubar</h4> <h4>Menubar</h4>
<p> <p>Show TIDAL Hi-Fi's menu bar.</p>
Show Tidal-hifi's menu bar </div>
</p>
<label class="switch"> <label class="switch">
<input id="menuBar" type="checkbox"> <input id="menuBar" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
</div> </div>
<div class="section"> <div class="group">
<h3>System</h3> <p class="group__title">System</p>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Tray icon</h4> <h4>Tray icon</h4>
<p> <p>Show TIDAL Hi-Fi's tray icon.</p>
Show Tidal-hifi's tray icon<br /> </div>
</p>
<label class="switch"> <label class="switch">
<input id="trayIcon" type="checkbox"> <input id="trayIcon" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Minimize on Close</h4> <h4>Minimize on Close</h4>
<p> <p>Minimize window on close instead.</p>
Minimize window on close instead <br /> </div>
</p>
<label class="switch"> <label class="switch">
<input id="minimizeOnClose" type="checkbox"> <input id="minimizeOnClose" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Hotkeys</h4> <h4>Hotkeys</h4>
<p> <p>
Enables extra hotkeys to achieve feature parity with the <a href = "javascript:openExternal('https://defkey.com/tidal-desktop-shortcuts')">desktop apps</a><br /> Enable extra hotkeys to achieve feature parity with the
<a class="external-link" data-url="https://defkey.com/tidal-desktop-shortcuts">desktop apps</a>.
</p> </p>
</div>
<label class="switch"> <label class="switch">
<input id="enableCustomHotkeys" type="checkbox"> <input id="enableCustomHotkeys" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Single instance</h4>
<p>Prevent opening multiple TIDAL Hi-Fi's instances.</p>
</div>
<label class="switch">
<input id="singleInstance" type="checkbox">
<span class="switch__slider"></span>
</label> </label>
</div> </div>
</div> </div>
</section> </section>
<section id="api" class="tab-panel">
<div class="section">
<h3>Api</h3>
<p style="margin-bottom: 15px;">
Tidal-hifi has a web api built in to allow users to get current song information. You can optionally enable playback control as well.
</p>
<div class="option"> <section id="api-section" class="tabs__section">
<h4>Web API</h4> <div class="group">
<p class="group__title">API</p>
<div class="group__description">
<p> <p>
Whether to enable the Tidal-hifi web api TIDAL Hi-Fi has a built-in web API to allow users to get current song information. You can optionally
enable playback control as well.
</p> </p>
</div>
<div class="group__option">
<div class="group__description">
<h4>Web API</h4>
<p>Enable the TIDAL Hi-Fi web API.</p>
</div>
<label class="switch"> <label class="switch">
<input id="apiCheckbox" type="checkbox"> <input id="apiCheckbox" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
<div class="option"> <div class="group__option">
<h4 style="margin-bottom: 5px;">API port</h4> <div class="group__description">
<input id="port" type="text" class="freeTextInput" name="port"> <label for="port">API port</label>
<input id="port" type="text" class="text-input" name="port">
</div> </div>
<div class="option"> </div>
<div class="group__option">
<div class="group__description">
<h4>Playback control</h4> <h4>Playback control</h4>
<p> <p>Enable playback control from the web API.</p>
Whether to enable playback control from the api </div>
</p>
<label class="switch"> <label class="switch">
<input id="playBackControl" type="checkbox"> <input id="playBackControl" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
</div> </div>
</section> </section>
<section id="integrations" class="tab-panel">
<div class="section"> <section id="integrations-section" class="tabs__section">
<h3>integrations</h3> <div class="group">
<p style="margin-bottom: 15px;"> <p class="group__title">Integrations</p>
Tidal-hifi is extensible through the use of integrations. You can enable or disable integrations here <div class="group__description">
</p>
<div class="option">
<h4>MPRIS-player</h4>
<p> <p>
Whether to enable the MPRIS media player controls for Linux systems TIDAL Hi-Fi is extensible through the use of integrations.
You can enable or disable them here.
</p> </p>
</div>
<div class="group__option">
<div class="group__description">
<h4>MPRIS</h4>
<p>Enable MPRIS interface which provides a mechanism for discovery, querying and basic playback control
on Linux systems.</p>
</div>
<label class="switch"> <label class="switch">
<input id="mprisCheckbox" type="checkbox"> <input id="mprisCheckbox" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
<div class="option"> <div class="group__option">
<div class="group__description">
<h4>Discord RPC</h4> <h4>Discord RPC</h4>
<p> <p>Show what you're listening to on Discord.</p>
Show what you're listening to on Discord </div>
</p>
<label class="switch"> <label class="switch">
<input id="enableDiscord" type="checkbox"> <input id="enableDiscord" type="checkbox">
<span class="slider round"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
</div> </div>
</section> </section>
<section id="about" class="tab-panel">
<div class="section">
<img alt="tidal icon" style="width: 100px; height: auto; display: block; margin: 0 auto; margin-bottom: 20px; margin-top: 20px;" src = "./icon.png">
<p style="max-width: 350px; display:block; margin: 0 auto; text-align: center;">
<a href ="javascript:openExternal('https://github.com/Mastermindzh/tidal-hifi');">Tidal-hifi</a> is made by <a href ="javascript:openExternal('https://www.rickvanlieshout.com')">Rick van Lieshout</a>.<br />
It uses <a href="javascript:openExternal('https://castlabs.com/');">Castlabs'</a> versions of Electron for widevine support.
</p>
<section id="advanced-section" class="tabs__section">
<div class="group">
<p class="group__title">Flags</p>
<div class="group__option">
<div class="group__description">
<h4>Disable hardware built-in media keys</h4>
<p>
Also prevents certain desktop environments from recognizing the chrome
MPRIS client separately from the custom MPRIS client.
</p>
</div>
<label class="switch">
<input id="disableHardwareMediaKeys" type="checkbox">
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Enable GPU rasterization</h4>
<p>Move a part of the rendering to the GPU for increased performance.</p>
</div>
<label class="switch">
<input id="gpuRasterization" type="checkbox">
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>Disable Background Throttling</h4>
<p>Makes app more responsive while in the background, at the cost of performance.</p>
</div>
<label class="switch">
<input id="disableBackgroundThrottle" type="checkbox">
<span class="switch__slider"></span>
</label>
</div>
</div> </div>
</section> </section>
<small>Some settings require a restart of Tidal-hifi. To do so, click the button below:</small> <section id="about-section" class="tabs__section about-section">
<button onClick="restart()" style ="width: 100%">Restart Tidal-hifi</button> <img alt="tidal icon" class="about-section__icon" src="./icon.png">
</div> <p class="about-section__text">
<a class="external-link" data-url="https://github.com/Mastermindzh/tidal-hifi">TIDAL Hi-Fi</a>
is made by <a class="external-link" data-url="https://www.rickvanlieshout.com">
Rick van Lieshout</a>. <br>It uses <a class="external-link" data-url="https://castlabs.com/">Castlabs'</a>
version of Electron for widevine support.
</p>
</section>
<footer class="footer">
<p class="footer__note">Some settings may require a restart of TIDAL Hi-Fi. To do so, click the button below:
</p>
<button class="footer__button" id="restart">Restart TIDAL Hi-Fi</button>
</footer>
</div> </div>
</main>
</div> </div>
</body> </body>
<style>
.header {
-webkit-user-select: none;
-webkit-app-region: drag;
}
.header a {
-webkit-app-region: no-drag;
}
* {
margin: 0%;
padding: 0%;
color: #ffffff;
font-weight: 400;
font-stretch: normal;
-webkit-font-smoothing: antialiased;
font-family: nationale, nationale-regular, Helvetica, sans-serif;
}
html,
body {
height: 100%;
background-color: black;
display: flex;
flex-direction: column;
}
h2 {
font-size: 1.2rem;
}
small {
font-style: italic;
color: #72777f;
}
.header {
background-color: #242528;
border-bottom: 1px solid #5a5a5a;
height: 50px;
}
.title {
float: left;
line-height: 50px;
margin-left: 15px;
}
.accent {
color: #0ff;
}
.exitWindow {
border: none;
outline: none;
text-decoration: none;
font-size: 1.4rem;
float: right;
margin-right: 15px;
height: 50px;
line-height: 50px;
}
.exitWindow:focus {
border: none;
outline: none;
}
.exitWindow svg {
height: 50px;
color: white;
}
.section {
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(246, 245, 255, .1);
}
.section .option {
margin-bottom: 15px;
}
.section .option p {
max-width: 75%;
float: left
}
.section .option label {
float: right;
}
.section:after,
.section .option:after {
content: "";
display: table;
clear: both;
}
.section h3 {
margin-bottom: 15px;
}
.section h4 {
font-size: 0.9rem;
}
.section p {
color: #72777f;
}
.bottom-border {
border-bottom: 1px solid #0ff;
}
.body {
padding: 15px;
flex: 1 1 auto;
position: relative;
overflow-y: auto;
}
.body::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-color: 2a2a2a;
}
.body::-webkit-scrollbar {
width: 10px;
background-color: #2a2a2a;
}
.body::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: #5a5a5a;
}
/* Tabs */
.tabset > input[type="radio"] {
position: absolute;
left: -200vw;
}
.tabset .tab-panel {
display: none;
}
.tabset > input:first-child:checked ~ .tab-panels > .tab-panel:first-child,
.tabset > input:nth-child(3):checked ~ .tab-panels > .tab-panel:nth-child(2),
.tabset > input:nth-child(5):checked ~ .tab-panels > .tab-panel:nth-child(3),
.tabset > input:nth-child(7):checked ~ .tab-panels > .tab-panel:nth-child(4)
{
display: block;
}
.tabset > label {
position: relative;
display: inline-block;
padding: 15px 0px 10px;
border-bottom: 0;
cursor: pointer;
}
.tabset > input + label {
color: #e0e0e0;
margin-right: 30px;
}
.tabset > input:checked + label {
color: #0ff;
border-bottom: 2px solid #0ff;
}
.tab-panel {
padding: 10px 0;
}
/* switches */
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(246, 245, 255, .1);
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 24px;
width: 24px;
left: 2px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #0ff;
}
input:focus + .slider {
box-shadow: 0 0 1px #0ff;
}
input:checked + .slider:before {
-webkit-transform: translateX(22px);
-ms-transform: translateX(22px);
transform: translateX(22px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/* input field */
input {
background: transparent;
border: 0;
border-bottom: 1px solid rgba(246, 245, 255, .1);
color: rgba(229, 238, 255, .6);
width: 100%;
display: block;
padding: 0 0 12px;
}
.freeTextInput:focus {
outline: none;
border-bottom: 1px solid #0ff;
}
/* buttons */
button{
border:none;
background:none;
align-items: center;
background-color: rgba(229,238,255,.2);
display: inline-flex;
justify-content: center;
border-radius: 12px;
height: 48px;
line-height: 49px;
padding: 0 24px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: background .35s ease;
min-height: 0;
min-width: 0;
font-size: 1.14286rem;
font-family: nationale,nationale-regular,Helvetica,sans-serif;
margin-top: 10px;
cursor: pointer;
}
button:hover{
background-color: rgba(229,238,255,.3);
}
textarea {
color: #72777f;
background: #242528;
border: 0;
width: 100%;
color: rgba(229, 238, 255, .6);
padding: 0 0 12px;
display:block;
}
textarea:focus {
outline: none;
border: 0;
border-bottom: 1px solid #0ff;
}
</style>
</html> </html>

View File

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

View File

@@ -1,8 +1,8 @@
const { setTitle } = require("./scripts/window-functions"); const { setTitle } = require("./scripts/window-functions");
const { dialog, process, Notification } = 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");
const { downloadFile } = require("./scripts/download"); 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");
@@ -12,11 +12,7 @@ const appName = "Tidal Hifi";
let currentSong = ""; let currentSong = "";
let player; let player;
let currentPlayStatus = statuses.paused; let currentPlayStatus = statuses.paused;
let progressBarTime; let isMutedArtist = true;
let currentTimeChanged = false;
let currentTime;
let currentURL = undefined;
let isMutedArtist = false;
const elements = { const elements = {
play: '*[data-test="play"]', play: '*[data-test="play"]',
@@ -37,7 +33,7 @@ const elements = {
media: '*[data-test="current-media-imagery"]', media: '*[data-test="current-media-imagery"]',
image: "img", image: "img",
current: '*[data-test="current-time"]', current: '*[data-test="current-time"]',
duration: '*[data-test="duration-time"]', duration: '*[data-test="duration"]',
bar: '*[data-test="progress-bar"]', bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer", footer: "#footerPlayer",
album_header_title: '.header-details [data-test="title"]', album_header_title: '.header-details [data-test="title"]',
@@ -313,19 +309,16 @@ function updateMediaInfo(options, notify) {
/** /**
* Checks if Tidal is playing a video or song by grabbing the "a" element from the title. * 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. * If it's a song it returns the track URL, if not it will return undefined
*/ */
function updateURL() { function getTrackURL() {
const URLelement = elements.get("title").querySelector("a"); const URLelement = elements.get("title").querySelector("a");
switch (URLelement) { if (URLelement !== null) {
case null:
currentURL = undefined;
break;
default:
const id = URLelement.href.replace(/[^0-9]/g, ""); const id = URLelement.href.replace(/[^0-9]/g, "");
currentURL = `https://tidal.com/browse/track/${id}`; return `https://tidal.com/browse/track/${id}`;
break;
} }
return window.location;
} }
/** /**
@@ -334,10 +327,11 @@ function updateURL() {
setInterval(function () { setInterval(function () {
const title = elements.getText("title"); const title = elements.getText("title");
const artists = elements.getArtists(); const artists = elements.getArtists();
muteArtistIfFoundInMutedArtistsList(); // doing this here so that nothing can possibly fail before we call this function
const album = elements.getAlbumName(); const album = elements.getAlbumName();
const current = elements.getText("current"); const current = elements.getText("current");
const duration = elements.getText("duration"); const duration = elements.getText("duration");
const progressBarcurrentTime = elements.get("bar").getAttribute("aria-valuenow");
const songDashArtistTitle = `${title} - ${artists}`; const songDashArtistTitle = `${title} - ${artists}`;
const currentStatus = getCurrentlyPlayingStatus(); const currentStatus = getCurrentlyPlayingStatus();
const options = { const options = {
@@ -345,46 +339,23 @@ setInterval(function () {
message: artists, message: artists,
album: album, album: album,
status: currentStatus, status: currentStatus,
url: currentURL, url: getTrackURL(),
current: current, current,
duration: duration, duration,
"app-name": appName, "app-name": appName,
}; };
const playStatusChanged = currentStatus !== currentPlayStatus;
const progressBarTimeChanged = progressBarcurrentTime !== progressBarTime;
const titleOrArtistChanged = currentSong !== songDashArtistTitle; const titleOrArtistChanged = currentSong !== songDashArtistTitle;
muteArtistIfFoundInMutedArtistsList();
if (titleOrArtistChanged || playStatusChanged || progressBarTimeChanged || currentTimeChanged) {
// update title, url and play info with new info // update title, url and play info with new info
setTitle(songDashArtistTitle); setTitle(songDashArtistTitle);
updateURL(); getTrackURL();
currentSong = songDashArtistTitle; currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus; 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(); const image = elements.getSongIcon();
new Promise((resolve) => { new Promise((resolve) => {
@@ -410,7 +381,6 @@ setInterval(function () {
}, },
() => {} () => {}
); );
}
/** /**
* Checks whether the current artist is included in the "muted artists" list and if so it will automatically mute the player * Checks whether the current artist is included in the "muted artists" list and if so it will automatically mute the player
@@ -423,13 +393,13 @@ setInterval(function () {
isMutedArtist = true; isMutedArtist = true;
elements.click("volume"); elements.click("volume");
} }
} else if (currentStatus === statuses.playing && isMutedArtist && elements.isMuted()) { } else if (isMutedArtist && elements.isMuted()) {
elements.click("volume"); elements.click("volume");
isMutedArtist = false; isMutedArtist = false;
} }
} }
} }
}, 200); }, 1000);
if (process.platform === "linux" && store.get(settings.mpris)) { if (process.platform === "linux" && store.get(settings.mpris)) {
try { try {

View File

@@ -1,6 +1,7 @@
const { Menu, app } = require("electron"); const { Menu, app } = require("electron");
const { showSettingsWindow } = require("./settings"); const { showSettingsWindow } = require("./settings");
const isMac = process.platform === "darwin"; const isMac = process.platform === "darwin";
const { name } = require("./../constants/values");
const settingsMenuEntry = { const settingsMenuEntry = {
label: "Settings", label: "Settings",
@@ -15,14 +16,24 @@ const quitMenuEntry = {
click() { click() {
app.exit(0); app.exit(0);
}, },
accelerator: "Control+Q" accelerator: "Control+Q",
}; };
const mainMenu = [ const menuModule = {};
menuModule.getMenu = function (mainWindow) {
const toggleWindow = {
label: "Toggle Window",
click: function () {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
},
};
const mainMenu = [
...(isMac ...(isMac
? [ ? [
{ {
label: app.name, label: name,
submenu: [ submenu: [
{ role: "about" }, { role: "about" },
settingsMenuEntry, settingsMenuEntry,
@@ -78,12 +89,14 @@ const mainMenu = [
{ role: "zoomout" }, { role: "zoomout" },
{ type: "separator" }, { type: "separator" },
{ role: "togglefullscreen" }, { role: "togglefullscreen" },
{ role: "toggledevtools" },
], ],
}, },
{ {
label: "Window", label: "Window",
submenu: [ submenu: [
{ role: "minimize" }, { role: "minimize" },
toggleWindow,
...(isMac ...(isMac
? [{ type: "separator" }, { role: "front" }, { type: "separator" }, { role: "window" }] ? [{ type: "separator" }, { role: "front" }, { type: "separator" }, { role: "window" }]
: [{ role: "close" }]), : [{ role: "close" }]),
@@ -96,16 +109,14 @@ const mainMenu = [
showSettingsWindow("about"); showSettingsWindow("about");
}, },
}, },
]; toggleWindow,
];
const menuModule = { mainMenu };
menuModule.getMenu = function () {
return Menu.buildFromTemplate(mainMenu); return Menu.buildFromTemplate(mainMenu);
}; };
menuModule.addMenu = function () { menuModule.addMenu = function (mainWindow) {
Menu.setApplicationMenu(menuModule.getMenu()); Menu.setApplicationMenu(menuModule.getMenu(mainWindow));
}; };
module.exports = menuModule; module.exports = menuModule;

View File

@@ -12,16 +12,32 @@ const store = new Store({
playBackControl: true, playBackControl: true,
muteArtists: false, muteArtists: false,
mutedArtists: ["TIDAL"], mutedArtists: ["TIDAL"],
disableBackgroundThrottle: true,
menuBar: true, menuBar: true,
apiSettings: { apiSettings: {
port: 47836, port: 47836,
}, },
singleInstance: true,
disableHardwareMediaKeys: false,
trayIcon: true, trayIcon: true,
minimizeOnClose: false, minimizeOnClose: false,
mpris: false, mpris: false,
enableCustomHotkeys: false, enableCustomHotkeys: false,
enableDiscord: false, enableDiscord: false,
windowBounds: { width: 800, height: 600 }, windowBounds: { width: 800, height: 600 },
flags: {
gpuRasterization: true,
disableHardwareMediaKeys: false,
},
},
migrations: {
"3.1.0": (migrationStore) => {
console.log("running migrations for 3.1.0");
migrationStore.set(
settings.flags.disableHardwareMediaKeys,
migrationStore.get("disableHardwareMediaKeys") ?? false
);
},
}, },
}); });
@@ -33,13 +49,14 @@ const settingsModule = {
settingsModule.createSettingsWindow = function () { settingsModule.createSettingsWindow = function () {
settingsWindow = new BrowserWindow({ settingsWindow = new BrowserWindow({
width: 500, width: 700,
height: 600, height: 600,
resizable: false,
show: false, show: false,
transparent: true,
frame: false, frame: false,
title: "Tidal-hifi - settings", title: "TIDAL Hi-Fi settings",
webPreferences: { webPreferences: {
affinity: "window",
preload: path.join(__dirname, "../pages/settings/preload.js"), preload: path.join(__dirname, "../pages/settings/preload.js"),
plugins: true, plugins: true,
nodeIntegration: true, nodeIntegration: true,

View File

@@ -1,48 +1,21 @@
const { Tray, app } = require("electron"); const { Tray } = require("electron");
const { Menu } = require("electron"); const { getMenu } = require("./menu");
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 (mainWindow, options = { icon: "" }) {
tray = new Tray(options.icon); tray = new Tray(options.icon);
tray.setIgnoreDoubleClickEvents(true);
tray.setToolTip("Tidal-hifi");
const menu = getMenu(mainWindow);
tray.setContextMenu(menu);
}; };
trayModule.refreshTray = function (mainWindow) { trayModule.refreshTray = function (mainWindow) {
if (!tray) { if (!tray) {
trayModule.addTray(); trayModule.addTray(mainWindow);
}
tray.on("click", function (e) {
if (mainWindow) {
mainWindow.show();
}
});
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());
} }
}; };