Compare commits

...

12 Commits
2.3.0 ... 2.7.2

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

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

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

Co-authored-by: Vinay V <cool00geek@yahoo.com>
2021-11-29 22:43:51 +01:00
12 changed files with 3190 additions and 2287 deletions

View File

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

View File

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

View File

@@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 2.7.2
- Disabled sanboxing to fix a display compositor issue on Linux.
## 2.7.1
- Fixed bug: Triggering fullscreen from the Tidal web app would cause the menubar to be visible even if it was disabled in the settings
## 2.7.0
- Switched to the native Notifier (removed node-notifier)
- Album art now also has a name, based on [best effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847)
## 2.6.0
- Add album images to mediainfo and discord
## 2.5.0
- Notify-send now correctly shows "Tidal Hifi" as the program name
- Updated dependencies (including electron itself)
### known issues
- Requires older version of nodejs due to electron-builder (use lts/gallium)
### builds
updated to nodejs 16 in actions
## 2.4.0
- Added more mpris settings
- Added instruction for rescrobbler to get last.fm working without sandbox mode
## 2.3.0 ## 2.3.0
- Added a setting to minimize to tray on app close (off by default) - Added a setting to minimize to tray on app close (off by default)
@@ -21,7 +56,7 @@ moved to: [https://github.com/Mastermindzh/tidal-hifi-aur](https://github.com/Ma
## 2.2.0 ## 2.2.0
- The discord integration now adds a time remaining field based on the song duration - 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*) - All fields (current, remaining, and url are also available in the API\*)
- the artist field is now correctly identified - the artist field is now correctly identified
* current time only updates on play/pause. * current time only updates on play/pause.

View File

@@ -20,6 +20,7 @@ The web version of [listen.tidal.com](https://listen.tidal.com) running in elect
- [Integrations](#integrations) - [Integrations](#integrations)
- [not included](#not-included) - [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)
@@ -76,7 +77,8 @@ To install and work with the code on this project follow these steps:
- 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) - Custom [integrations](#integrations)
- [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+=`) - [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+=` or `ctrl+0`)
- AlbumArt in integrations ([best-effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847))
## Integrations ## Integrations
@@ -96,7 +98,11 @@ It currently includes:
### 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

View File

@@ -10,7 +10,7 @@ snap:
linux: linux:
category: Audio category: Audio
target: target:
# - pacman - pacman
- tar.gz - tar.gz
- deb - deb
- rpm - rpm
@@ -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

5290
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.3.0", "version": "2.7.2",
"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,19 @@
"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",
"request": "^2.88.2" "request": "^2.88.2"
}, },
"devDependencies": { "devDependencies": {
"@mastermindzh/prettier-config": "^1.0.0", "@mastermindzh/prettier-config": "^1.0.0",
"dot-prop": ">=4.2.1", "electron": "git+https://github.com/castlabs/electron-releases.git#v16.0.4+wvcus",
"electron": "git+https://github.com/castlabs/electron-releases.git#v10.4.3-wvvmp", "electron-builder": "^22.14.5",
"electron-builder": "^21.2.0",
"electron-reload": "^1.5.0", "electron-reload": "^1.5.0",
"prettier": "^2.0.4" "prettier": "^2.5.0"
}, },
"prettier": "@mastermindzh/prettier-config" "prettier": "@mastermindzh/prettier-config"
} }

View File

@@ -6,6 +6,7 @@ const globalEvents = {
previous: "previous", previous: "previous",
updateInfo: "update-info", updateInfo: "update-info",
hideSettings: "hideSettings", hideSettings: "hideSettings",
refreshMenuBar: "refreshMenubar",
showSettings: "showSettings", showSettings: "showSettings",
storeChanged: "storeChanged", storeChanged: "storeChanged",
error: "error", error: "error",

View File

@@ -29,6 +29,19 @@ if (!app.isPackaged) {
}); });
} }
/**
* Fix Display Compositor issue.
*/
app.commandLine.appendSwitch('disable-seccomp-filter-sandbox');
/**
* Update the menuBarVisbility according to the store value
*
*/
function syncMenuBarWithStore() {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
}
function createWindow(options = {}) { function createWindow(options = {}) {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
@@ -48,7 +61,7 @@ function createWindow(options = {}) {
}, },
}); });
mainWindow.setMenuBarVisibility(store.get(settings.menuBar)); syncMenuBarWithStore();
// load the Tidal website // load the Tidal website
mainWindow.loadURL(tidalUrl); mainWindow.loadURL(tidalUrl);
@@ -95,6 +108,7 @@ app.on("ready", () => {
store.get(settings.trayIcon) && addTray({ icon }) && refreshTray(); store.get(settings.trayIcon) && addTray({ 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();
}); });
app.on("activate", function () { app.on("activate", function () {
@@ -117,8 +131,12 @@ ipcMain.on(globalEvents.showSettings, (event, arg) => {
showSettingsWindow(); showSettingsWindow();
}); });
ipcMain.on(globalEvents.refreshMenuBar, (event, arg) => {
syncMenuBarWithStore();
});
ipcMain.on(globalEvents.storeChanged, (event, arg) => { ipcMain.on(globalEvents.storeChanged, (event, arg) => {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar)); syncMenuBarWithStore();
if (store.get(settings.enableDiscord) && !discordModule.rpc) { if (store.get(settings.enableDiscord) && !discordModule.rpc) {
discordModule.initRPC(); discordModule.initRPC();

View File

@@ -1,5 +1,5 @@
const { setTitle } = require("./scripts/window-functions"); const { setTitle } = require("./scripts/window-functions");
const { dialog, process } = require("electron").remote; const { dialog, process, Notification } = require("electron").remote;
const { store, settings } = require("./scripts/settings"); const { store, settings } = require("./scripts/settings");
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
const { app } = require("electron").remote; const { app } = require("electron").remote;
@@ -7,7 +7,6 @@ const { downloadFile } = require("./scripts/download");
const statuses = require("./constants/statuses"); const statuses = require("./constants/statuses");
const hotkeys = require("./scripts/hotkeys"); const hotkeys = require("./scripts/hotkeys");
const globalEvents = require("./constants/globalEvents"); const globalEvents = require("./constants/globalEvents");
const notifier = require("node-notifier");
const notificationPath = `${app.getPath("userData")}/notification.jpg`; const notificationPath = `${app.getPath("userData")}/notification.jpg`;
let currentSong = ""; let currentSong = "";
let player; let player;
@@ -39,6 +38,10 @@ const elements = {
duration: '*[data-test="duration-time"]', duration: '*[data-test="duration-time"]',
bar: '*[data-test="progress-bar"]', bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer", footer: "#footerPlayer",
album_header_title: '.header-details [data-test="title"]',
playing_title: 'span[data-test="table-cell-title"].css-geqnfr',
album_name_cell: '[data-test="table-cell-album"]',
tracklist_row: '[data-test="tracklist-row"]',
/** /**
* Get an element from the dom * Get an element from the dom
@@ -57,7 +60,7 @@ const elements = {
if (figure) { if (figure) {
const mediaElement = figure.querySelector(this["image"]); const mediaElement = figure.querySelector(this["image"]);
if (mediaElement) { if (mediaElement) {
return mediaElement.src; return mediaElement.src.replace("80x80", "640x640");
} }
} }
@@ -77,6 +80,29 @@ const elements = {
return "unknown artist(s)"; return "unknown artist(s)";
}, },
getAlbumName: function () {
//If listening to an album, get its name from the header title
if (window.location.href.includes("/album/")) {
const albumName = window.document.querySelector(this.album_header_title);
if (albumName) {
return albumName.textContent;
}
//If listening to a playlist or a mix, get album name from the list
} else if (
window.location.href.includes("/playlist/") ||
window.location.href.includes("/mix/")
) {
if (currentPlayStatus === statuses.playing) {
const row = window.document.querySelector(this.playing_title).closest(this.tracklist_row);
if (row) {
return row.querySelector(this.album_name_cell).textContent;
}
}
}
return "";
},
/** /**
* 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
@@ -157,6 +183,9 @@ function addHotKeys() {
hotkeys.add("control+=", function () { hotkeys.add("control+=", function () {
ipcRenderer.send(globalEvents.showSettings); ipcRenderer.send(globalEvents.showSettings);
}); });
hotkeys.add("control+0", function () {
ipcRenderer.send(globalEvents.showSettings);
});
} }
/** /**
@@ -190,6 +219,12 @@ function handleLogout() {
); );
} }
function addFullScreenListeners() {
window.document.addEventListener("fullscreenchange", (event) => {
ipcRenderer.send(globalEvents.refreshMenuBar);
});
}
/** /**
* Add ipc event listeners. * Add ipc event listeners.
* Some actions triggered outside of the site need info from the site. * Some actions triggered outside of the site need info from the site.
@@ -234,6 +269,15 @@ function getCurrentlyPlayingStatus() {
return status; return status;
} }
/**
* Convert the duration from MM:SS to seconds
* @param {*} duration
*/
function convertDuration(duration) {
const parts = duration.split(":");
return parseInt(parts[1]) + 60 * parseInt(parts[0]);
}
/** /**
* Update Tidal-hifi's media info * Update Tidal-hifi's media info
* *
@@ -242,15 +286,18 @@ function getCurrentlyPlayingStatus() {
function updateMediaInfo(options, notify) { function updateMediaInfo(options, notify) {
if (options) { if (options) {
ipcRenderer.send(globalEvents.updateInfo, options); ipcRenderer.send(globalEvents.updateInfo, options);
store.get(settings.notifications) && notify && notifier.notify(options); if (store.get(settings.notifications) && notify) {
new Notification({ title: options.title, body: options.message, icon: options.icon }).show();
}
if (player) { if (player) {
player.metadata = { player.metadata = {
...player.metadata, ...player.metadata,
...{ ...{
"xesam:title": options.title, "xesam:title": options.title,
"xesam:artist": [options.artists], "xesam:artist": [options.message],
"xesam:album": options.album,
"mpris:artUrl": options.image, "mpris:artUrl": options.image,
"mpris:length": convertDuration(options.duration) * 1000 * 1000,
}, },
}; };
player.playbackStatus = options.status == statuses.paused ? "Paused" : "Playing"; player.playbackStatus = options.status == statuses.paused ? "Paused" : "Playing";
@@ -281,18 +328,22 @@ function updateURL() {
setInterval(function () { setInterval(function () {
const title = elements.getText("title"); const title = elements.getText("title");
const artists = elements.getArtists(); const artists = elements.getArtists();
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 appName = "Tidal Hifi";
const progressBarcurrentTime = elements.get("bar").getAttribute("aria-valuenow"); 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 = {
title, title,
message: artists, message: artists,
album: album,
status: currentStatus, status: currentStatus,
url: currentURL, url: currentURL,
current: current, current: current,
duration: duration, duration: duration,
"app-name": appName,
}; };
const playStatusChanged = currentStatus !== currentPlayStatus; const playStatusChanged = currentStatus !== currentPlayStatus;
@@ -331,6 +382,7 @@ setInterval(function () {
new Promise((resolve) => { 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;
@@ -397,6 +449,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();
@@ -408,3 +464,4 @@ if (process.platform === "linux" && store.get(settings.mpris)) {
addHotKeys(); addHotKeys();
addIPCEventListeners(); addIPCEventListeners();
addFullScreenListeners();

View File

@@ -31,6 +31,8 @@ const observer = (event, arg) => {
: "unknown artist(s)", : "unknown artist(s)",
startTimestamp: parseInt(now), startTimestamp: parseInt(now),
endTimestamp: parseInt(remaining), endTimestamp: parseInt(remaining),
largeImageKey: mediaInfoModule.mediaInfo.image,
largeImageText: (mediaInfoModule.mediaInfo.album) ? mediaInfoModule.mediaInfo.album : `${idleStatus.largeImageText}`,
buttons: [{ label: "Play on Tidal", url: mediaInfoModule.mediaInfo.url }], buttons: [{ label: "Play on Tidal", url: mediaInfoModule.mediaInfo.url }],
}, },
}); });

View File

@@ -3,11 +3,13 @@ const statuses = require("./../constants/statuses");
const mediaInfo = { const mediaInfo = {
title: "", title: "",
artist: "", artist: "",
album: "",
icon: "", icon: "",
status: statuses.paused, status: statuses.paused,
url: "", url: "",
current: "", current: "",
duration: "" duration: "",
image: "tidal-hifi-icon"
}; };
const mediaInfoModule = { const mediaInfoModule = {
mediaInfo, mediaInfo,
@@ -19,11 +21,13 @@ const mediaInfoModule = {
mediaInfoModule.update = function (arg) { mediaInfoModule.update = function (arg) {
mediaInfo.title = propOrDefault(arg.title); mediaInfo.title = propOrDefault(arg.title);
mediaInfo.artist = propOrDefault(arg.message); mediaInfo.artist = propOrDefault(arg.message);
mediaInfo.album = propOrDefault(arg.album);
mediaInfo.icon = propOrDefault(arg.icon); mediaInfo.icon = propOrDefault(arg.icon);
mediaInfo.url = propOrDefault(arg.url); mediaInfo.url = propOrDefault(arg.url);
mediaInfo.status = propOrDefault(arg.status); mediaInfo.status = propOrDefault(arg.status);
mediaInfo.current = propOrDefault(arg.current); mediaInfo.current = propOrDefault(arg.current);
mediaInfo.duration = propOrDefault(arg.duration); mediaInfo.duration = propOrDefault(arg.duration);
mediaInfo.image = propOrDefault(arg.image);
}; };
/** /**