Compare commits

...

6 Commits
2.0.0 ... 2.1.1

Author SHA1 Message Date
148d1746ad Merge pull request #42 from Mastermindzh/feature/improving-discord-integration-with-maroxy
Discord improvements with Mar0xy
2021-04-19 23:18:41 +02:00
ae51f9610c - The discord integration now doesn't send an update every 15 seconds it sends an update whenever the media info changes
- consolidated updating the media info changes with the status changes into a single global event
2021-04-19 23:08:19 +02:00
5eb3b8d95f released version 2.1.0 on the AUR version as well. 2021-04-19 21:31:12 +02:00
ebdae6bc88 bumped a few packages and added a changelog 2021-04-19 20:59:37 +02:00
Marie
ab25bf16b2 Add Discord RPC & fix artist in mediainfo (#40) 2021-04-19 20:43:25 +02:00
31670d0c2b updated aur info 2021-01-10 14:11:45 +01:00
14 changed files with 240 additions and 90 deletions

View File

@@ -4,6 +4,16 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 2.1.1
- The discord integration now doesn't send an update every 15 seconds it sends an update whenever the media info changes
- consolidated updating the media info changes with the status changes into a single global event
## 2.1.0
- [Mar0xy](https://github.com/Mar0xy) added Discord integration.
- Several versions have been bumped to fix vulnerabilities
## 2.0.0
### Breaking changes

View File

@@ -1,6 +1,6 @@
pkgbase = tidal-hifi-git
pkgdesc = The web version of listen.tidal.com running in electron with hifi support thanks to widevine.
pkgver = 2.0.0
pkgver = 2.1.0
pkgrel = 1
url = https://github.com/Mastermindzh/tidal-hifi
arch = x86_64
@@ -11,9 +11,9 @@ pkgbase = tidal-hifi-git
depends = nss
depends = gtk3
provides = tidal-hifi
source = https://github.com/Mastermindzh/tidal-hifi/archive/2.0.0.zip
source = https://github.com/Mastermindzh/tidal-hifi/archive/2.1.0.zip
source = tidal-hifi.desktop
sha512sums = e1c0e47257cebe6b2609131964f0971c3eb70fc28c86f3d7177cb3cea09a3311236f4147bafa91347210fe2c96e6bd9d2e4ac7ca5b105e0c899657645705913d
sha512sums = 30b50243e7033219351f7d7f318757e4590e5c78a7031762cf846ae3652b424ab3f54388cba44209e49434d8534edb198e1c65d0b53450b4367636ef0f4653e0
sha512sums = 35f38ac308b871c1822d7f6f760f2fb54c3748cf769822cb0f0dfb90f0f5754ba9316da5e903a0d2e9839de3a43ec76f238f3f2e44021956fa1da19142081349
pkgname = tidal-hifi-git

View File

@@ -2,7 +2,7 @@
_pkgname=tidal-hifi
pkgname="$_pkgname-git"
pkgver=2.0.0
pkgver=2.1.0
pkgrel=1
pkgdesc="The web version of listen.tidal.com running in electron with hifi support thanks to widevine."
arch=("x86_64")
@@ -15,7 +15,7 @@ provides=("$_pkgname")
source=("https://github.com/Mastermindzh/tidal-hifi/archive/$pkgver.zip"
"${_pkgname}.desktop")
sha512sums=('e1c0e47257cebe6b2609131964f0971c3eb70fc28c86f3d7177cb3cea09a3311236f4147bafa91347210fe2c96e6bd9d2e4ac7ca5b105e0c899657645705913d'
sha512sums=('30b50243e7033219351f7d7f318757e4590e5c78a7031762cf846ae3652b424ab3f54388cba44209e49434d8534edb198e1c65d0b53450b4367636ef0f4653e0'
'35f38ac308b871c1822d7f6f760f2fb54c3748cf769822cb0f0dfb90f0f5754ba9316da5e903a0d2e9839de3a43ec76f238f3f2e44021956fa1da19142081349')
cdToPkg(){

88
package-lock.json generated
View File

@@ -972,6 +972,15 @@
"dev": true,
"optional": true
},
"discord-rpc": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-3.2.0.tgz",
"integrity": "sha512-KJv0EVbGMlr04HoG6f5b3wD7X9kSHzQ2Ed2qfHSDvYJ1MkE8RbCQmMcQQrSvAxpfsqZgUjB/bsfi/mjyicCH+A==",
"requires": {
"node-fetch": "^2.6.1",
"ws": "^7.3.1"
}
},
"dmg-builder": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-21.2.0.tgz",
@@ -1775,6 +1784,11 @@
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
},
"is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1855,9 +1869,12 @@
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"is-wsl": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz",
"integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"requires": {
"is-docker": "^2.0.0"
}
},
"is-yarn-global": {
"version": "0.3.0",
@@ -2160,22 +2177,57 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-notifier": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz",
"integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz",
"integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==",
"requires": {
"growly": "^1.3.0",
"is-wsl": "^2.1.1",
"semver": "^6.3.0",
"is-wsl": "^2.2.0",
"semver": "^7.3.2",
"shellwords": "^0.1.1",
"which": "^1.3.1"
"uuid": "^8.3.0",
"which": "^2.0.2"
},
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"requires": {
"lru-cache": "^6.0.0"
}
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"requires": {
"isexe": "^2.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
@@ -3251,6 +3303,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
@@ -3365,6 +3418,11 @@
"signal-exit": "^3.0.2"
}
},
"ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
},
"xdg-basedir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
@@ -3386,9 +3444,9 @@
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
},
"yallist": {

View File

@@ -1,6 +1,6 @@
{
"name": "tidal-hifi",
"version": "2.0.0",
"version": "2.1.1",
"description": "Tidal on Electron with widevine(hifi) support",
"main": "src/main.js",
"scripts": {
@@ -23,11 +23,12 @@
"homepage": "https://github.com/Mastermindzh/tidal-hifi",
"license": "MIT",
"dependencies": {
"discord-rpc": "^3.2.0",
"electron-store": "^5.1.1",
"express": "^4.17.1",
"hotkeys-js": "^3.7.6",
"mpris-service": "^2.1.0",
"node-notifier": "^6.0.0",
"node-notifier": "^9.0.1",
"request": "^2.88.2"
},
"devDependencies": {

View File

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

View File

@@ -20,6 +20,7 @@ const settings = {
mpris: "mpris",
enableCustomHotkeys: "enableCustomHotkeys",
trayIcon: "trayIcon",
enableDiscord: "enableDiscord",
windowBounds: {
root: "windowBounds",
width: "windowBounds.width",

View File

@@ -9,12 +9,12 @@ const {
} = require("./scripts/settings");
const { addTray, refreshTray } = require("./scripts/tray");
const { addMenu } = require("./scripts/menu");
const path = require("path");
const tidalUrl = "https://listen.tidal.com";
const expressModule = require("./scripts/express");
const mediaKeys = require("./constants/mediaKeys");
const mediaInfoModule = require("./scripts/mediaInfo");
const discordModule = require("./scripts/discord");
const globalEvents = require("./constants/globalEvents");
let mainWindow;
@@ -85,6 +85,7 @@ app.on("ready", () => {
addGlobalShortcuts();
store.get(settings.trayIcon) && addTray({ icon }) && refreshTray();
store.get(settings.api) && expressModule.run(mainWindow);
store.get(settings.enableDiscord) && discordModule.initRPC();
});
app.on("activate", function () {
@@ -96,7 +97,6 @@ app.on("activate", function () {
});
// IPC
ipcMain.on(globalEvents.updateInfo, (event, arg) => {
mediaInfoModule.update(arg);
});
@@ -108,11 +108,14 @@ ipcMain.on(globalEvents.showSettings, (event, arg) => {
showSettingsWindow();
});
ipcMain.on(globalEvents.updateStatus, (event, arg) => {
mediaInfoModule.updateStatus(arg);
});
ipcMain.on(globalEvents.storeChanged, (event, arg) => {
mainWindow.setMenuBarVisibility(store.get(settings.menuBar));
if (store.get(settings.enableDiscord) && !discordModule.rpc) {
discordModule.initRPC();
} else if (!store.get(settings.enableDiscord) && discordModule.rpc) {
discordModule.unRPC();
}
});
ipcMain.on(globalEvents.error, (event, arg) => {

View File

@@ -20,6 +20,7 @@ function refreshSettings() {
trayIcon.checked = store.get(settings.trayIcon);
mpris.checked = store.get(settings.mpris);
enableCustomHotkeys.checked = store.get(settings.enableCustomHotkeys);
enableDiscord.checked = store.get(settings.enableDiscord);
}
/**
@@ -81,6 +82,7 @@ window.addEventListener("DOMContentLoaded", () => {
trayIcon = get("trayIcon");
mpris = get("mprisCheckbox");
enableCustomHotkeys = get("enableCustomHotkeys");
enableDiscord = get("enableDiscord");
refreshSettings();
@@ -92,4 +94,5 @@ window.addEventListener("DOMContentLoaded", () => {
addInputListener(trayIcon, settings.trayIcon);
addInputListener(mpris, settings.mpris);
addInputListener(enableCustomHotkeys, settings.enableCustomHotkeys);
addInputListener(enableDiscord, settings.enableDiscord);
});

View File

@@ -141,6 +141,16 @@
<span class="slider round"></span>
</label>
</div>
<div class="option">
<h4>Discord RPC</h4>
<p>
Show what you're listening to on Discord
</p>
<label class="switch">
<input id="enableDiscord" type="checkbox">
<span class="slider round"></span>
</label>
</div>
</div>
</section>
<section id="about" class="tab-panel">

View File

@@ -1,4 +1,4 @@
const { setTitle, getTitle } = require("./scripts/window-functions");
const { setTitle } = require("./scripts/window-functions");
const { dialog, process } = require("electron").remote;
const { store, settings } = require("./scripts/settings");
const { ipcRenderer } = require("electron");
@@ -11,6 +11,7 @@ const notifier = require("node-notifier");
const notificationPath = `${app.getPath("userData")}/notification.jpg`;
let currentSong = "";
let player;
let currentPlayStatus = statuses.paused;
const elements = {
play: '*[data-test="play"]',
@@ -18,7 +19,7 @@ const elements = {
next: '*[data-test="next"]',
previous: 'button[data-test="previous"]',
title: '*[data-test^="footer-track-title"]',
artists: '*[class^="mediaArtists"]',
artists: '*[data-test^="grid-item-detail-text-title-artist"]',
home: '*[data-test="menu--home"]',
back: '[class^="backwardButton"]',
forward: '[class^="forwardButton"]',
@@ -30,6 +31,7 @@ const elements = {
settings: '*[data-test^="open-settings"]',
media: '*[data-test="current-media-imagery"]',
image: "img",
url: 'a[href*="/track/"]',
/**
* Get an element from the dom
@@ -155,8 +157,8 @@ function handleLogout() {
},
function (response) {
if (logoutOptions.indexOf("Yes, please") == response) {
for (i = 0; i < window.localStorage.length; i++) {
key = window.localStorage.key(i);
for (let i = 0; i < window.localStorage.length; i++) {
const key = window.localStorage.key(i);
if (key.startsWith("_TIDAL_activeSession")) {
window.localStorage.removeItem(key);
i = window.localStorage.length + 1;
@@ -199,9 +201,9 @@ function addIPCEventListeners() {
/**
* Update the current status of tidal (e.g playing or paused)
*/
function updateStatus() {
function getCurrentlyPlayingStatus() {
let pause = elements.get("pause");
let status;
let status = undefined;
// if pause button is visible tidal is playing
if (pause) {
@@ -209,11 +211,29 @@ function updateStatus() {
} else {
status = statuses.paused;
}
return status;
}
/**
* Update Tidal-hifi's media info
*
* @param {*} options
*/
function updateMediaInfo(options, notify) {
if (options) {
ipcRenderer.send(globalEvents.updateInfo, options);
store.get(settings.notifications) && notify && notifier.notify(options);
if (status) {
ipcRenderer.send(globalEvents.updateStatus, status);
if (player) {
player.playbackStatus = status == statuses.paused ? "Paused" : "Playing";
player.metadata = {
...player.metadata,
...{
"xesam:title": options.title,
"xesam:artist": [options.artists],
"mpris:artUrl": options.image,
},
};
player.playbackStatus = options.status == statuses.paused ? "Paused" : "Playing";
}
}
}
@@ -223,23 +243,29 @@ function updateStatus() {
*/
setInterval(function () {
const title = elements.getText("title");
const url = elements.get("url").href.replace(/[^0-9]/g, "");
const artists = elements.getText("artists");
const songDashArtistTitle = `${title} - ${artists}`;
updateStatus();
if (getTitle() !== songDashArtistTitle) {
setTitle(songDashArtistTitle);
if (currentSong !== songDashArtistTitle) {
currentSong = songDashArtistTitle;
const image = elements.getSongIcon();
const currentStatus = getCurrentlyPlayingStatus();
const options = {
title,
message: artists,
status: currentStatus,
url: `https://tidal.com/browse/track/${url}`,
};
new Promise((resolve, reject) => {
const playStatusChanged = currentStatus !== currentPlayStatus;
const titleOrArtistChanged = currentSong !== songDashArtistTitle;
if (titleOrArtistChanged || playStatusChanged) {
// update title and play info with new info
setTitle(songDashArtistTitle);
currentSong = songDashArtistTitle;
currentPlayStatus = currentStatus;
const image = elements.getSongIcon();
new Promise((resolve) => {
if (image.startsWith("http")) {
downloadFile(image, notificationPath).then(
() => {
@@ -257,24 +283,11 @@ setInterval(function () {
}
}).then(
() => {
ipcRenderer.send(globalEvents.updateInfo, options);
store.get(settings.notifications) && notifier.notify(options);
if (player) {
player.metadata = {
...player.metadata,
...{
"xesam:title": title,
"xesam:artist": [artists],
"mpris:artUrl": image,
},
};
}
updateMediaInfo(options, titleOrArtistChanged);
},
() => {}
);
}
}
}, 200);
if (process.platform === "linux" && store.get(settings.mpris)) {

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

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

View File

@@ -5,6 +5,7 @@ const mediaInfo = {
artist: "",
icon: "",
status: statuses.paused,
url: "",
};
const mediaInfoModule = {
mediaInfo,
@@ -17,15 +18,8 @@ mediaInfoModule.update = function(arg) {
mediaInfo.title = propOrDefault(arg.title);
mediaInfo.artist = propOrDefault(arg.message);
mediaInfo.icon = propOrDefault(arg.icon);
};
/**
* Update tidal's status in the mediaInfo constant
*/
mediaInfoModule.updateStatus = function(status) {
if (Object.values(statuses).includes(status)) {
mediaInfo.status = status;
}
mediaInfo.url = propOrDefault(arg.url);
mediaInfo.status = propOrDefault(arg.status);
};
/**

View File

@@ -17,6 +17,7 @@ const store = new Store({
trayIcon: true,
mpris: false,
enableCustomHotkeys: false,
enableDiscord: false,
windowBounds: { width: 800, height: 600 },
},
});