mirror of
https://github.com/Mastermindzh/tidal-hifi.git
synced 2024-11-24 06:23:15 +01:00
did some more work. Mainly worked on code cleanliness and hotkey support (https://defkey.com/tidal-desktop-shortcuts)
This commit is contained in:
parent
cbf15965e4
commit
9c3ac88d12
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
dist
|
||||||
|
23
LICENSE
Normal file
23
LICENSE
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Rick van Lieshout (Mastermindzh)
|
||||||
|
|
||||||
|
Online version: https://choosealicense.com/licenses/mit/
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
48
README.md
48
README.md
@ -1,23 +1,59 @@
|
|||||||
# tidal-hifi-electron
|
<img src = "./build/icon.png" height="50" style="float:right; margin-top: 29px;" />
|
||||||
|
# Tidal-hifi
|
||||||
|
|
||||||
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](listen.tidal.com) running in electron with hifi support thanks to widevine.
|
||||||
|
|
||||||
|
![tidal-hifi preview](./docs/preview.png)
|
||||||
|
|
||||||
<!-- toc -->
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Using releases](#using-releases)
|
||||||
|
- [Using source](#using-source)
|
||||||
- [Why](#why)
|
- [Why](#why)
|
||||||
- [Requirements](#requirements)
|
|
||||||
- [Integrations](#integrations)
|
- [Integrations](#integrations)
|
||||||
|
- [Why not extend existing projects?](#why-not-extend-existing-projects)
|
||||||
|
- [Special thanks to..](#special-thanks-to)
|
||||||
|
|
||||||
<!-- tocstop -->
|
<!-- tocstop -->
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Using releases
|
||||||
|
|
||||||
|
Various packaged versions of the software are available on the [releases](https://github.com/Mastermindzh/tidal-hifi/releases) tab.
|
||||||
|
|
||||||
|
### Using source
|
||||||
|
|
||||||
|
To install and work with the code on this project follow these steps:
|
||||||
|
|
||||||
|
- git clone https://github.com/Mastermindzh/tidal-hifi.git
|
||||||
|
- cd tidal-hifi
|
||||||
|
- npm install
|
||||||
|
- npm start
|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
|
I moved from Spotify over to Tidal and found Linux support to be lacking.
|
||||||
|
|
||||||
When I started this project there weren't any Linux apps that offered Tidal's "hifi" options nor any scripts to control it.
|
When I started this project there weren't any Linux apps that offered Tidal's "hifi" options nor any scripts to control it.
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Internet connection
|
|
||||||
|
|
||||||
## Integrations
|
## Integrations
|
||||||
|
|
||||||
- [i3 blocks config]() - My dotfiles where I use this app to fetch currently playing music
|
- [i3 blocks config]() - My dotfiles where I use this app to fetch currently playing music
|
||||||
|
|
||||||
|
## Why not extend existing projects?
|
||||||
|
|
||||||
|
Whilst there are a handful of projects attempting to run Tidal on Electron they are all unappealing to me because of various reasons:
|
||||||
|
|
||||||
|
- Lack of a maintainers/developers. (no hotfixes, no issues being handled etc)
|
||||||
|
- Most are simple web wrappers, not my cup of tea.
|
||||||
|
- Some are DE oriented. I want this to work on WM's too.
|
||||||
|
- None have widevine working at the moment and that is really the hardest part..
|
||||||
|
|
||||||
|
Sometimes it's just easier to start over, cover my own needs and then making it available to the public :)
|
||||||
|
|
||||||
|
## Special thanks to..
|
||||||
|
|
||||||
|
- [Castlabs](https://castlabs.com/)
|
||||||
|
For maintaining Electron with Widevine CDM installation, Verified Media Path (VMP), and persistent licenses (StorageID)
|
||||||
|
17
build/electron-builder.yml
Normal file
17
build/electron-builder.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
appId: com.rickvanlieshout.tidal-hifi
|
||||||
|
snap:
|
||||||
|
plugs:
|
||||||
|
- default
|
||||||
|
- screen-inhibit-control
|
||||||
|
linux:
|
||||||
|
category: Audio
|
||||||
|
target:
|
||||||
|
- pacman
|
||||||
|
- tar.gz
|
||||||
|
- deb
|
||||||
|
- AppImage
|
||||||
|
- snap
|
||||||
|
mac:
|
||||||
|
category: public.app-category.entertainment
|
||||||
|
win:
|
||||||
|
target: msi
|
BIN
build/icon.ico
Normal file
BIN
build/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
BIN
build/icon.png
Normal file
BIN
build/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
docs/preview.png
Normal file
BIN
docs/preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 800 KiB |
1417
package-lock.json
generated
1417
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "tidal-hifi-electron",
|
"name": "tidal-hifi",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Tidal on Electron with widevine(hifi) support",
|
"description": "Tidal on Electron with widevine(hifi) support",
|
||||||
"main": "main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron ."
|
"start": "electron .",
|
||||||
|
"build": "electron-builder -c ./build/electron-builder.yml"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"electron",
|
"electron",
|
||||||
@ -13,13 +14,14 @@
|
|||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"author": "Rick van Lieshout <info@rickvanlieshout.com> (http://rickvanlieshout.com)",
|
"author": "Rick van Lieshout <info@rickvanlieshout.com> (http://rickvanlieshout.com)",
|
||||||
"license": "ISC",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron": "https://github.com/castlabs/electron-releases#v6.0.9-wvvmp",
|
|
||||||
"hotkeys-js": "^3.7.1"
|
"hotkeys-js": "^3.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mastermindzh/prettier-config": "^1.0.0",
|
"@mastermindzh/prettier-config": "^1.0.0",
|
||||||
|
"electron": "https://github.com/castlabs/electron-releases#v6.0.12-wvvmp",
|
||||||
|
"electron-builder": "^21.2.0",
|
||||||
"electron-reload": "^1.5.0",
|
"electron-reload": "^1.5.0",
|
||||||
"prettier": "^1.18.2"
|
"prettier": "^1.18.2"
|
||||||
},
|
},
|
||||||
|
68
preload.js
68
preload.js
@ -1,68 +0,0 @@
|
|||||||
const hotkeys = require("hotkeys-js");
|
|
||||||
|
|
||||||
function getNode(string) {
|
|
||||||
return window.document.querySelectorAll(string)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Play or pause the current song
|
|
||||||
*/
|
|
||||||
function playPause() {
|
|
||||||
let controls = getNode('*[class^="playbackControls"]');
|
|
||||||
|
|
||||||
let play = controls.querySelectorAll('*[data-test^="play"]')[0];
|
|
||||||
let pause = controls.querySelectorAll('*[data-test^="pause"]')[0];
|
|
||||||
|
|
||||||
if (play) {
|
|
||||||
play.click();
|
|
||||||
} else {
|
|
||||||
pause.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Click a button
|
|
||||||
* @param {*} dataTestValue
|
|
||||||
*/
|
|
||||||
function clickButton(dataTestValue) {
|
|
||||||
getNode(`*[data-test^="${dataTestValue}"]`).click();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the title of the current song
|
|
||||||
*/
|
|
||||||
function getTitle() {
|
|
||||||
return getNode('*[data-test^="footer-track-title"]').textContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getArtists() {
|
|
||||||
return getNode('*[class^="mediaArtists"]').textContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add hotkeys
|
|
||||||
*/
|
|
||||||
function addHotKeys() {
|
|
||||||
hotkeys("f4", function(event, handler) {
|
|
||||||
// Prevent the default refresh event under WINDOWS system
|
|
||||||
event.preventDefault();
|
|
||||||
playPause();
|
|
||||||
// clickButton("next");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add ipc event listeners
|
|
||||||
*/
|
|
||||||
function addIPCEventListeners() {
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
const { ipcRenderer } = require("electron");
|
|
||||||
|
|
||||||
ipcRenderer.on("getPlayInfo", (event, col) => {
|
|
||||||
window.document.querySelectorAll('*[data-test^="next"]')[0].click();
|
|
||||||
alert(`${getTitle()} - ${getArtists()}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addHotKeys();
|
|
||||||
addIPCEventListeners();
|
|
@ -1,4 +1,4 @@
|
|||||||
const { app, globalShortcut, BrowserWindow, process } = require("electron");
|
const { app, BrowserWindow } = require("electron");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const tidalUrl = "https://listen.tidal.com";
|
const tidalUrl = "https://listen.tidal.com";
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
@ -6,9 +6,11 @@ let mainWindow;
|
|||||||
/**
|
/**
|
||||||
* Enable live reload in development builds
|
* Enable live reload in development builds
|
||||||
*/
|
*/
|
||||||
require("electron-reload")(__dirname, {
|
if (!app.isPackaged) {
|
||||||
electron: require(`${__dirname}/node_modules/electron`),
|
require("electron-reload")(`${__dirname}`, {
|
||||||
});
|
electron: require(`${__dirname}/../node_modules/electron`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function createWindow(options = {}) {
|
function createWindow(options = {}) {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
@ -25,6 +27,8 @@ function createWindow(options = {}) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mainWindow.setMenuBarVisibility(false);
|
||||||
|
|
||||||
// load the Tidal website
|
// load the Tidal website
|
||||||
mainWindow.loadURL(tidalUrl);
|
mainWindow.loadURL(tidalUrl);
|
||||||
|
|
||||||
@ -38,9 +42,10 @@ function createWindow(options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addGlobalShortcuts() {
|
function addGlobalShortcuts() {
|
||||||
globalShortcut.register("Control+A", () => {
|
// globalShortcut.register("Control+A", () => {
|
||||||
mainWindow.webContents.send("getPlayInfo");
|
// dialog.showErrorBox("test", "test");
|
||||||
});
|
// // mainWindow.webContents.send("getPlayInfo");
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
165
src/preload.js
Normal file
165
src/preload.js
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
const { setTitle, getTitle } = require("./scripts/window-functions");
|
||||||
|
const { dialog } = require("electron").remote;
|
||||||
|
const hotkeys = require("./scripts/hotkeys");
|
||||||
|
|
||||||
|
const elements = {
|
||||||
|
play: '*[data-test="play"]',
|
||||||
|
pause: '*[data-test="pause"]',
|
||||||
|
next: '*[data-test="next"]',
|
||||||
|
previous: 'button[data-test="previous"]',
|
||||||
|
title: '*[data-test^="footer-track-title"]',
|
||||||
|
artists: '*[class^="mediaArtists"]',
|
||||||
|
home: '*[data-test="menu--home"]',
|
||||||
|
back: '[class^="backwardButton"]',
|
||||||
|
forward: '[class^="forwardButton"]',
|
||||||
|
search: '[class^="searchField"]',
|
||||||
|
shuffle: '*[data-test="shuffle"]',
|
||||||
|
repeat: '*[data-test="repeat"]',
|
||||||
|
block: '[class="blockButton"]',
|
||||||
|
account: '*[data-test^="profile-image-button"]',
|
||||||
|
settings: '*[data-test^="open-settings"]',
|
||||||
|
|
||||||
|
get: function(key) {
|
||||||
|
return window.document.querySelector(this[key.toLowerCase()]);
|
||||||
|
},
|
||||||
|
|
||||||
|
getText: function(key) {
|
||||||
|
return this.get(key).textContent;
|
||||||
|
},
|
||||||
|
|
||||||
|
click: function(key) {
|
||||||
|
this.get(key).click();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
focus: function(key) {
|
||||||
|
return this.get(key).focus();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play or pause the current song
|
||||||
|
*/
|
||||||
|
function playPause() {
|
||||||
|
const play = elements.get("play");
|
||||||
|
|
||||||
|
if (play) {
|
||||||
|
elements.click("play");
|
||||||
|
} else {
|
||||||
|
elements.click("pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add hotkeys for when tidal is focused
|
||||||
|
* Reflects the desktop hotkeys found on:
|
||||||
|
* https://defkey.com/tidal-desktop-shortcuts
|
||||||
|
*/
|
||||||
|
function addHotKeys() {
|
||||||
|
hotkeys.add("Control+p", function() {
|
||||||
|
elements.click("account").click("settings");
|
||||||
|
});
|
||||||
|
hotkeys.add("Control+l", function() {
|
||||||
|
handleLogout();
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("Control+h", function() {
|
||||||
|
elements.click("home");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("backspace", function() {
|
||||||
|
elements.click("back");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("shift+backspace", function() {
|
||||||
|
elements.click("forward");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+f", function() {
|
||||||
|
elements.focus("search");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+u", function() {
|
||||||
|
// reloading window without cache should show the update bar if applicable
|
||||||
|
window.location.reload(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+left", function() {
|
||||||
|
elements.click("previous");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+right", function() {
|
||||||
|
elements.click("next");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+right", function() {
|
||||||
|
elements.click("next");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+s", function() {
|
||||||
|
elements.click("shuffle");
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys.add("control+r", function() {
|
||||||
|
elements.click("repeat");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will ask the user whether he/she wants to log out.
|
||||||
|
* It will log the user out if he/she selects "yes"
|
||||||
|
*/
|
||||||
|
function handleLogout() {
|
||||||
|
const logoutOptions = ["Cancel", "Yes, please", "No, thanks"];
|
||||||
|
|
||||||
|
dialog.showMessageBox(
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
type: "question",
|
||||||
|
title: "Logging out",
|
||||||
|
message: "Are you sure you want to log out?",
|
||||||
|
buttons: logoutOptions,
|
||||||
|
defaultId: 2,
|
||||||
|
},
|
||||||
|
function(response) {
|
||||||
|
if (logoutOptions.indexOf("Yes, please") == response) {
|
||||||
|
for (i = 0; i < window.localStorage.length; i++) {
|
||||||
|
key = window.localStorage.key(i);
|
||||||
|
if (key.startsWith("_TIDAL_activeSession")) {
|
||||||
|
window.localStorage.removeItem(key);
|
||||||
|
i = window.localStorage.length + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add ipc event listeners.
|
||||||
|
* Some actions triggered outside of the site need info from the site.
|
||||||
|
*/
|
||||||
|
function addIPCEventListeners() {
|
||||||
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const { ipcRenderer } = require("electron");
|
||||||
|
|
||||||
|
ipcRenderer.on("getPlayInfo", (event, col) => {
|
||||||
|
alert(`${elements.getText("title")} - ${elements.getText("artists")}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update window title
|
||||||
|
*/
|
||||||
|
setInterval(function() {
|
||||||
|
const title = elements.getText("title");
|
||||||
|
const artists = elements.getText("artists");
|
||||||
|
const songDashArtistTitle = `${title} - ${artists}`;
|
||||||
|
if (getTitle() !== songDashArtistTitle) {
|
||||||
|
setTitle(songDashArtistTitle);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
addHotKeys();
|
||||||
|
addIPCEventListeners();
|
11
src/scripts/hotkeys.js
Normal file
11
src/scripts/hotkeys.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const hotkeyjs = require("hotkeys-js");
|
||||||
|
const hotkeys = {};
|
||||||
|
|
||||||
|
hotkeys.add = function(keys, func) {
|
||||||
|
hotkeyjs(keys, function(event, args) {
|
||||||
|
event.preventDefault();
|
||||||
|
func(event, args);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = hotkeys;
|
11
src/scripts/window-functions.js
Normal file
11
src/scripts/window-functions.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const windowFunctions = {};
|
||||||
|
|
||||||
|
windowFunctions.setTitle = function(title) {
|
||||||
|
window.document.title = title;
|
||||||
|
};
|
||||||
|
|
||||||
|
windowFunctions.getTitle = function() {
|
||||||
|
return window.document.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = windowFunctions;
|
Loading…
Reference in New Issue
Block a user