mirror of
https://github.com/Mastermindzh/tidal-hifi.git
synced 2025-09-11 22:35:05 +02:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
d67f62c0dc | |||
ae699887b2 | |||
b2afd44dd6 | |||
a8c635932f | |||
66d0d004bf | |||
d1e0321058 | |||
2ab5a556ab | |||
baf719fc60 | |||
15c8f6a418 | |||
f73521e2e5 | |||
|
0f5e00c4df | ||
|
974877ea4f | ||
|
6be5774001 | ||
|
f96cf2e8da | ||
|
f832bd2712 | ||
|
cb8667bd41 | ||
91156e5936 | |||
|
57e9a2dcb8 | ||
|
21edcd6ad5 | ||
|
3dc42eceb0 | ||
|
b1830f5684 | ||
|
1a0c15e17f | ||
6e59d59a1d | |||
|
fab7497311 | ||
fccbcc77ea | |||
|
041c19fb52 | ||
e36c562afa | |||
1589aa5251 | |||
8c672dd1eb | |||
|
3769550f24 | ||
|
5a06b6c53f | ||
|
1d4ef66d27 | ||
e02f07401b | |||
118f92e75a | |||
|
788d302ce8 | ||
|
cc09f35b49 | ||
ef13933c66 | |||
4f72e1b35d | |||
2c1c76d2d0 | |||
e2a84e119a | |||
5b85e59fc3 | |||
c3b772919a | |||
|
70f2f5c248 | ||
|
ffcb563b35 | ||
|
2dd96dd48e |
@@ -1,13 +1,7 @@
|
||||
{
|
||||
"plugins": [
|
||||
"stylelint-prettier"
|
||||
],
|
||||
"extends": [
|
||||
"stylelint-config-standard-scss"
|
||||
],
|
||||
"ignoreFiles": [
|
||||
"src/themes/**.scss"
|
||||
],
|
||||
"plugins": ["stylelint-prettier"],
|
||||
"extends": ["stylelint-config-standard-scss"],
|
||||
"ignoreFiles": ["src/themes/**.scss"],
|
||||
"rules": {
|
||||
"prettier/prettier": true,
|
||||
"scss/at-extend-no-missing-placeholder": null,
|
||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -14,7 +14,6 @@
|
||||
"rescrobbler",
|
||||
"scrobble",
|
||||
"scrobbling",
|
||||
"Songwhip",
|
||||
"trackid",
|
||||
"tracklist",
|
||||
"widevine",
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@@ -4,10 +4,24 @@ 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).
|
||||
|
||||
## [5.17.0]
|
||||
|
||||
- Added an option to disable the dynamic title and set it to a static one, [#491](https://github.com/Mastermindzh/tidal-hifi/pull/491)
|
||||
- Discord integration now says "Listening to" instead of "playing" [#488](https://github.com/Mastermindzh/tidal-hifi/pull/488) && [#454](https://github.com/Mastermindzh/tidal-hifi/pull/454)
|
||||
- Fixed several element names in the dom scraper
|
||||
- Removed the Songwhip (they shut down) integration and replaced it with TIDAL's universal link system
|
||||
|
||||
## [5.16.0]
|
||||
|
||||
- Fix issue #449 Discord RPC stuck on "Browsing Tidal".
|
||||
- Fix issue #448 Add option to disable the discord rpc idle text
|
||||
- Notifications are now send at the end of the update process, allowing other events to happen sooner.
|
||||
|
||||
## [5.15.0]
|
||||
|
||||
- Added all missing swagger/openApi info with the help of [Times-Z](https://github.com/Times-Z)
|
||||
- Updated most dependency versions
|
||||
|
||||
- This includes Electron 31!
|
||||
|
||||
- Added a channel selector so we can now use Tidal's staging environment directly from the app
|
||||
@@ -99,10 +113,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Updated Electron to 28.1.1 (fixes [325](https://github.com/Mastermindzh/tidal-hifi/issues/325))
|
||||
- Updated dependencies to latest
|
||||
|
||||
- added theme files to stylelint ignore
|
||||
- fixed other stylelint errors
|
||||
|
||||
- Added functionality to favorite a song (fixes [#323](https://github.com/Mastermindzh/tidal-hifi/issues/323))
|
||||
|
||||
- Added a hotkey to favorite ("Add to collection") songs: Control+a
|
||||
- Added the "favorite" field in the `mediaInfo` and the API `/current` endpoint
|
||||
- Added an endpoint to toggle favoriting a song: `http://localhost:47836/favorite/toggle`
|
||||
|
@@ -48,7 +48,6 @@ The web version of [listen.tidal.com](https://listen.tidal.com) running in elect
|
||||
- AlbumArt in integrations ([best-effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847))
|
||||
- Custom [integrations](#integrations)
|
||||
- [ListenBrainz](https://listenbrainz.org/?redirect=false) integration
|
||||
- Songwhip.com integration (hotkey `ctrl + w`)
|
||||
- Discord RPC integration (showing "now listening", "Browsing", etc)
|
||||
- Flatpak version only works if both Discord and Tidal-HiFi are flatpaks
|
||||
- MPRIS integration
|
||||
|
@@ -8,4 +8,3 @@ Only the very latest 😄.
|
||||
|
||||
If you find a vulnerability just add it as an issue.
|
||||
If there's an especially bad vulnerability that you don't want to make public just send me a private message (email, discord, wherever).
|
||||
|
||||
|
707
package-lock.json
generated
707
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tidal-hifi",
|
||||
"version": "5.15.0",
|
||||
"version": "5.17.0",
|
||||
"description": "Tidal on Electron with widevine(hifi) support",
|
||||
"main": "ts-dist/main.js",
|
||||
"scripts": {
|
||||
@@ -23,6 +23,7 @@
|
||||
"build-mac": "npm run builder -- -c ./build/electron-builder.yml -m",
|
||||
"build-base": "npm run builder -- -c ./build/electron-builder.base.yml",
|
||||
"prebuilder": "npm run compile",
|
||||
"prettier": "prettier . --write",
|
||||
"builder": "electron-builder --publish=never",
|
||||
"sass": "sass ./src/pages/settings/settings.scss ./src/pages/settings/settings.css && sass --no-source-map src/themes:themes",
|
||||
"style-lint": "npx stylelint **/*.scss",
|
||||
@@ -42,21 +43,20 @@
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.1.2",
|
||||
"@types/swagger-jsdoc": "^6.0.4",
|
||||
"axios": "^1.7.2",
|
||||
"@xhayper/discord-rpc": "^1.2.0",
|
||||
"axios": "^1.7.7",
|
||||
"cors": "^2.8.5",
|
||||
"discord-rpc": "^4.0.1",
|
||||
"electron-store": "^8.2.0",
|
||||
"express": "^4.19.2",
|
||||
"express": "^4.21.1",
|
||||
"hotkeys-js": "^3.13.7",
|
||||
"mpris-service": "^2.1.2",
|
||||
"request": "^2.88.2",
|
||||
"sass": "^1.77.7",
|
||||
"sass": "^1.79.4",
|
||||
"swagger-ui-express": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mastermindzh/prettier-config": "^1.0.0",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/discord-rpc": "^4.0.8",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.14.10",
|
||||
"@types/request": "^2.48.12",
|
||||
|
@@ -16,8 +16,9 @@
|
||||
search: '[class^="searchField"]',
|
||||
shuffle: '*[data-test="shuffle"]',
|
||||
repeat: '*[data-test="repeat"]',
|
||||
account: '*[class^="profileOptions"]',
|
||||
settings: '*[data-test^="open-settings"]',
|
||||
account: '*[data-test^="profile-image-button"]',
|
||||
settings: '*[data-test^="sidebar-menu-button"]',
|
||||
openSettings: '*[data-test^="open-settings"]',
|
||||
media: '*[data-test="current-media-imagery"]',
|
||||
image: "img",
|
||||
current: '*[data-test="current-time"]',
|
||||
|
@@ -10,7 +10,7 @@ export const globalEvents = {
|
||||
showSettings: "showSettings",
|
||||
storeChanged: "storeChanged",
|
||||
error: "error",
|
||||
whip: "whip",
|
||||
getUniversalLink: "getUniversalLink",
|
||||
log: "log",
|
||||
toggleFavorite: "toggleFavorite",
|
||||
toggleShuffle: "toggleShuffle",
|
||||
|
@@ -30,6 +30,7 @@ export const settings = {
|
||||
buttonText: "discord.buttonText",
|
||||
includeTimestamps: "discord.includeTimestamps",
|
||||
showSong: "discord.showSong",
|
||||
showIdle: "discord.showIdle",
|
||||
idleText: "discord.idleText",
|
||||
usingText: "discord.usingText",
|
||||
},
|
||||
@@ -54,6 +55,7 @@ export const settings = {
|
||||
singleInstance: "singleInstance",
|
||||
skipArtists: "skipArtists",
|
||||
skippedArtists: "skippedArtists",
|
||||
staticWindowTitle: "staticWindowTitle",
|
||||
theme: "theme",
|
||||
trayIcon: "trayIcon",
|
||||
updateFrequency: "updateFrequency",
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "TIDAL Hi-Fi API",
|
||||
"version": "5.15.0",
|
||||
"version": "5.17.0",
|
||||
"description": "",
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
@@ -21,9 +21,7 @@
|
||||
"/current": {
|
||||
"get": {
|
||||
"summary": "Get current media info",
|
||||
"tags": [
|
||||
"current"
|
||||
],
|
||||
"tags": ["current"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Current media info",
|
||||
@@ -41,9 +39,7 @@
|
||||
"/current/image": {
|
||||
"get": {
|
||||
"summary": "Get current media image",
|
||||
"tags": [
|
||||
"current"
|
||||
],
|
||||
"tags": ["current"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Current media image",
|
||||
@@ -65,9 +61,7 @@
|
||||
"/player/play": {
|
||||
"post": {
|
||||
"summary": "Play the current media",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -85,9 +79,7 @@
|
||||
"/player/favorite/toggle": {
|
||||
"post": {
|
||||
"summary": "Add the current media to your favorites, or remove it if its already added to your favorites",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -105,9 +97,7 @@
|
||||
"/player/pause": {
|
||||
"post": {
|
||||
"summary": "Pause the current media",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -125,9 +115,7 @@
|
||||
"/player/next": {
|
||||
"post": {
|
||||
"summary": "Play the next song",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -145,9 +133,7 @@
|
||||
"/player/previous": {
|
||||
"post": {
|
||||
"summary": "Play the previous song",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -165,9 +151,7 @@
|
||||
"/player/shuffle/toggle": {
|
||||
"post": {
|
||||
"summary": "Play the previous song",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -185,9 +169,7 @@
|
||||
"/player/repeat/toggle": {
|
||||
"post": {
|
||||
"summary": "Toggle the repeat status, toggles between \"off\" , \"single\" and \"all\"",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -205,9 +187,7 @@
|
||||
"/player/playpause": {
|
||||
"post": {
|
||||
"summary": "Start playing the media if paused, or pause the media if playing",
|
||||
"tags": [
|
||||
"player"
|
||||
],
|
||||
"tags": ["player"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
@@ -225,9 +205,7 @@
|
||||
"/settings/skipped-artists": {
|
||||
"get": {
|
||||
"summary": "get a list of artists that TIDAL Hi-Fi will skip if skipping is enabled",
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"tags": ["settings"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list book.",
|
||||
@@ -243,9 +221,7 @@
|
||||
},
|
||||
"post": {
|
||||
"summary": "Add new artists to the list of skipped artists",
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"tags": ["settings"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@@ -266,9 +242,7 @@
|
||||
"/settings/skipped-artists/delete": {
|
||||
"post": {
|
||||
"summary": "Remove artists from the list of skipped artists",
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"tags": ["settings"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@@ -289,9 +263,7 @@
|
||||
"/settings/skipped-artists/current": {
|
||||
"post": {
|
||||
"summary": "Add the current artist to the list of skipped artists",
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"tags": ["settings"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok"
|
||||
@@ -300,9 +272,7 @@
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Remove the current artist from the list of skipped artists",
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"tags": ["settings"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok"
|
||||
@@ -313,9 +283,7 @@
|
||||
"/image": {
|
||||
"get": {
|
||||
"summary": "Get current image",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -338,9 +306,7 @@
|
||||
"/play": {
|
||||
"get": {
|
||||
"summary": "Play the current media",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -359,9 +325,7 @@
|
||||
"/favorite/toggle": {
|
||||
"get": {
|
||||
"summary": "Add the current media to your favorites, or remove it if its already added to your favorites",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -380,9 +344,7 @@
|
||||
"/pause": {
|
||||
"get": {
|
||||
"summary": "Pause the current media",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -401,9 +363,7 @@
|
||||
"/next": {
|
||||
"get": {
|
||||
"summary": "Play the next song",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -422,9 +382,7 @@
|
||||
"/previous": {
|
||||
"get": {
|
||||
"summary": "Play the previous song",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -443,9 +401,7 @@
|
||||
"/playpause": {
|
||||
"get": {
|
||||
"summary": "Toggle play/pause",
|
||||
"tags": [
|
||||
"legacy"
|
||||
],
|
||||
"tags": ["legacy"],
|
||||
"deprecated": true,
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -558,10 +514,7 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": [
|
||||
"Artist1",
|
||||
"Artist2"
|
||||
]
|
||||
"example": ["Artist1", "Artist2"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
10
src/features/sharingService/sharingService.ts
Normal file
10
src/features/sharingService/sharingService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export class SharingService {
|
||||
/**
|
||||
* Retrieve the universal link given a regular track link
|
||||
* @param currentUrl
|
||||
* @returns
|
||||
*/
|
||||
public static getUniversalLink(currentUrl: string): string {
|
||||
return `${currentUrl}?u`;
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
import { ServiceLinks } from "./ServiceLinks";
|
||||
|
||||
export interface Artist {
|
||||
type: string;
|
||||
id: number;
|
||||
path: string;
|
||||
name: string;
|
||||
sourceUrl: string;
|
||||
sourceCountry: string;
|
||||
url: string;
|
||||
image: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
refreshedAt: string;
|
||||
serviceIds: { [key: string]: string };
|
||||
orchardId: string;
|
||||
spotifyId: string;
|
||||
links: { [key: string]: ServiceLinks[] };
|
||||
linksCountries: string[];
|
||||
description: string;
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
export interface ServiceLinks {
|
||||
link: string;
|
||||
countries: string[];
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
import { Artist } from "./Artist";
|
||||
import { ServiceLinks } from "./ServiceLinks";
|
||||
|
||||
export interface WhippedResult {
|
||||
status: string;
|
||||
data: {
|
||||
item: {
|
||||
type: string;
|
||||
id: number;
|
||||
path: string;
|
||||
name: string;
|
||||
url: string;
|
||||
sourceUrl: string;
|
||||
sourceCountry: string;
|
||||
releaseDate: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
refreshedAt: string;
|
||||
image: string;
|
||||
isrc: string;
|
||||
isExplicit: boolean;
|
||||
links: { [key: string]: ServiceLinks[] };
|
||||
linksCountries: string[];
|
||||
artists: Artist[];
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
import { WhippedResult } from "./models/whip";
|
||||
import axios from "axios";
|
||||
|
||||
export class Songwhip {
|
||||
/**
|
||||
* Call the songwhip API and create a shareable songwhip page
|
||||
* @param currentUrl
|
||||
* @returns
|
||||
*/
|
||||
public static async whip(currentUrl: string): Promise<WhippedResult> {
|
||||
try {
|
||||
const response = await axios.post("https://songwhip.com/api/songwhip/create", {
|
||||
url: currentUrl,
|
||||
// doesn't actually matter.. returns everything the same way anyway
|
||||
country: "NL",
|
||||
});
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(JSON.stringify(error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a songwhip response into a shareable url
|
||||
* @param response
|
||||
* @returns
|
||||
*/
|
||||
public static getWhipUrl(response: WhippedResult) {
|
||||
return `https://songwhip.com${response.data.item.url}`;
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ import {
|
||||
releaseInhibitorIfActive,
|
||||
} from "./features/idleInhibitor/idleInhibitor";
|
||||
import { Logger } from "./features/logger";
|
||||
import { Songwhip } from "./features/songwhip/songwhip";
|
||||
import { SharingService } from "./features/sharingService/sharingService";
|
||||
import { MediaInfo } from "./models/mediaInfo";
|
||||
import { MediaStatus } from "./models/mediaStatus";
|
||||
import { initRPC, rpc, unRPC } from "./scripts/discord";
|
||||
@@ -250,8 +250,8 @@ ipcMain.on(globalEvents.error, (event) => {
|
||||
console.log(event);
|
||||
});
|
||||
|
||||
ipcMain.handle(globalEvents.whip, async (event, url) => {
|
||||
return Songwhip.whip(url);
|
||||
ipcMain.handle(globalEvents.getUniversalLink, async (event, url) => {
|
||||
return SharingService.getUniversalLink(url);
|
||||
});
|
||||
|
||||
Logger.watch(ipcMain);
|
||||
|
@@ -46,6 +46,7 @@ let adBlock: HTMLInputElement,
|
||||
singleInstance: HTMLInputElement,
|
||||
skipArtists: HTMLInputElement,
|
||||
skippedArtists: HTMLInputElement,
|
||||
staticWindowTitle: HTMLInputElement,
|
||||
theme: HTMLSelectElement,
|
||||
trayIcon: HTMLInputElement,
|
||||
updateFrequency: HTMLInputElement,
|
||||
@@ -58,6 +59,7 @@ let adBlock: HTMLInputElement,
|
||||
discord_include_timestamps: HTMLInputElement,
|
||||
discord_button_text: HTMLInputElement,
|
||||
discord_show_song: HTMLInputElement,
|
||||
discord_show_idle: HTMLInputElement,
|
||||
discord_idle_text: HTMLInputElement,
|
||||
discord_using_text: HTMLInputElement;
|
||||
|
||||
@@ -139,8 +141,9 @@ function refreshSettings() {
|
||||
port.value = settingsStore.get(settings.apiSettings.port);
|
||||
singleInstance.checked = settingsStore.get(settings.singleInstance);
|
||||
skipArtists.checked = settingsStore.get(settings.skipArtists);
|
||||
theme.value = settingsStore.get(settings.theme);
|
||||
skippedArtists.value = settingsStore.get<string, string[]>(settings.skippedArtists).join("\n");
|
||||
staticWindowTitle.checked = settingsStore.get(settings.staticWindowTitle);
|
||||
theme.value = settingsStore.get(settings.theme);
|
||||
trayIcon.checked = settingsStore.get(settings.trayIcon);
|
||||
updateFrequency.value = settingsStore.get(settings.updateFrequency);
|
||||
enableListenBrainz.checked = settingsStore.get(settings.ListenBrainz.enabled);
|
||||
@@ -151,6 +154,7 @@ function refreshSettings() {
|
||||
discord_include_timestamps.checked = settingsStore.get(settings.discord.includeTimestamps);
|
||||
discord_button_text.value = settingsStore.get(settings.discord.buttonText);
|
||||
discord_show_song.checked = settingsStore.get(settings.discord.showSong);
|
||||
discord_show_idle.checked = settingsStore.get(settings.discord.showIdle);
|
||||
discord_idle_text.value = settingsStore.get(settings.discord.idleText);
|
||||
discord_using_text.value = settingsStore.get(settings.discord.usingText);
|
||||
|
||||
@@ -259,6 +263,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||
trayIcon = get("trayIcon");
|
||||
skipArtists = get("skipArtists");
|
||||
skippedArtists = get("skippedArtists");
|
||||
staticWindowTitle = get("staticWindowTitle");
|
||||
singleInstance = get("singleInstance");
|
||||
updateFrequency = get("updateFrequency");
|
||||
enableListenBrainz = get("enableListenBrainz");
|
||||
@@ -269,6 +274,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||
listenbrainz_delay = get("listenbrainz_delay");
|
||||
discord_button_text = get("discord_button_text");
|
||||
discord_show_song = get("discord_show_song");
|
||||
discord_show_idle = get("discord_show_idle");
|
||||
discord_using_text = get("discord_using_text");
|
||||
discord_idle_text = get("discord_idle_text");
|
||||
|
||||
@@ -292,6 +298,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||
addInputListener(port, settings.apiSettings.port);
|
||||
addInputListener(skipArtists, settings.skipArtists);
|
||||
addTextAreaListener(skippedArtists, settings.skippedArtists);
|
||||
addInputListener(staticWindowTitle, settings.staticWindowTitle);
|
||||
addInputListener(singleInstance, settings.singleInstance);
|
||||
addSelectListener(theme, settings.theme);
|
||||
addInputListener(trayIcon, settings.trayIcon);
|
||||
@@ -312,6 +319,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||
settings.discord.showSong,
|
||||
switchesWithSettings.discord_show_song
|
||||
);
|
||||
addInputListener(discord_show_idle, settings.discord.showIdle);
|
||||
addInputListener(discord_idle_text, settings.discord.idleText);
|
||||
addInputListener(discord_using_text, settings.discord.usingText);
|
||||
});
|
||||
|
@@ -1,25 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<head>
|
||||
<title>Tidal Hi-Fi settings</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<link rel="stylesheet" href="./settings.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
</head>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body class="settings-window">
|
||||
<body class="settings-window">
|
||||
<div class="settings-window__wrapper">
|
||||
<div class="settings-window__drag-area"></div>
|
||||
<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">
|
||||
<path fill="white" d="M336.559,68.611L231.016,174.165l105.543,105.549c15.699,15.705,15.699,41.145,0,56.85
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 348.333 348.334"
|
||||
class="settings-window__svg-icon"
|
||||
>
|
||||
<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.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
|
||||
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>
|
||||
</a>
|
||||
|
||||
@@ -66,7 +75,13 @@
|
||||
<span class="switch__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<textarea id="skippedArtists" class="textarea" cols="40" rows="5" spellcheck="false"></textarea>
|
||||
<textarea
|
||||
id="skippedArtists"
|
||||
class="textarea"
|
||||
cols="40"
|
||||
rows="5"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
<div class="group__option">
|
||||
<div class="group__description">
|
||||
<h4>Block ads</h4>
|
||||
@@ -106,6 +121,19 @@
|
||||
<span class="switch__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="group__option">
|
||||
<div class="group__description">
|
||||
<h4>Static Window Title</h4>
|
||||
<p>
|
||||
Makes the window title "TIDAL Hi-Fi" instead of changing to the currently
|
||||
playing song.
|
||||
</p>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input id="staticWindowTitle" type="checkbox" />
|
||||
<span class="switch__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="group__option">
|
||||
<div class="group__description">
|
||||
<h4>Minimize on Close</h4>
|
||||
@@ -121,7 +149,9 @@
|
||||
<h4>Hotkeys</h4>
|
||||
<p>
|
||||
Enable extra hotkeys to achieve feature parity with the
|
||||
<a class="external-link" data-url="https://defkey.com/tidal-desktop-shortcuts">desktop apps</a>.
|
||||
<a class="external-link" data-url="https://defkey.com/tidal-desktop-shortcuts"
|
||||
>desktop apps</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
<label class="switch">
|
||||
@@ -147,8 +177,8 @@
|
||||
<p class="group__title">API</p>
|
||||
<div class="group__description">
|
||||
<p>
|
||||
TIDAL Hi-Fi has a built-in web API to allow users to get current media information.
|
||||
You can optionally enable playback control as well.
|
||||
TIDAL Hi-Fi has a built-in web API to allow users to get current media
|
||||
information. You can optionally enable playback control as well.
|
||||
</p>
|
||||
</div>
|
||||
<div class="group__option">
|
||||
@@ -170,7 +200,8 @@
|
||||
<div class="group__option">
|
||||
<div class="group__description">
|
||||
<h4>API hostname</h4>
|
||||
<p>By default (127.0.0.1) only local apps can interface with the API. <br />
|
||||
<p>
|
||||
By default (127.0.0.1) only local apps can interface with the API. <br />
|
||||
Change to 0.0.0.0 to allow <strong>anyone</strong> to interact with it. <br />
|
||||
Other options are available
|
||||
</p>
|
||||
@@ -226,20 +257,42 @@
|
||||
</label>
|
||||
</div>
|
||||
<div id="discord_options">
|
||||
<div class="group__option" class="hidden">
|
||||
<div class="group__description">
|
||||
<h4>Show Idle Text</h4>
|
||||
<p>Should the idle text be shown when idle?</p>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input id="discord_show_idle" type="checkbox" />
|
||||
<span class="switch__slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="group__option" class="hidden">
|
||||
<div class="group__description">
|
||||
<h4>Idle Text</h4>
|
||||
<p>The text displayed on Discord's rich presence while idling in the app.</p>
|
||||
<input id="discord_idle_text" type="text" class="text-input" name="discord_idle_text" />
|
||||
<input
|
||||
id="discord_idle_text"
|
||||
type="text"
|
||||
class="text-input"
|
||||
name="discord_idle_text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group__option" class="hidden">
|
||||
<div class="group__description">
|
||||
<h4>Using Tidal Text</h4>
|
||||
<p>The text displayed on Discord's rich presence while "showSong" is turned off</p>
|
||||
<input id="discord_using_text" type="text" class="text-input" name="discord_using_text" />
|
||||
<p>
|
||||
The text displayed on Discord's rich presence while "showSong" is turned off
|
||||
</p>
|
||||
<input
|
||||
id="discord_using_text"
|
||||
type="text"
|
||||
class="text-input"
|
||||
name="discord_using_text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -255,7 +308,6 @@
|
||||
</div>
|
||||
|
||||
<div id="discord_show_song_options" class="hidden">
|
||||
|
||||
<div class="group__option" class="hidden">
|
||||
<div class="group__description">
|
||||
<h4>Include timestamps</h4>
|
||||
@@ -271,7 +323,12 @@
|
||||
<div class="group__description">
|
||||
<h4>Details prefix</h4>
|
||||
<p>Prefix for the "details" field of Discord's rich presence.</p>
|
||||
<input id="discord_details_prefix" type="text" class="text-input" name="discord_details_prefix" />
|
||||
<input
|
||||
id="discord_details_prefix"
|
||||
type="text"
|
||||
class="text-input"
|
||||
name="discord_details_prefix"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -279,11 +336,15 @@
|
||||
<div class="group__description">
|
||||
<h4>Button text</h4>
|
||||
<p>Text to display on the button below the media information.</p>
|
||||
<input id="discord_button_text" type="text" class="text-input" name="discord_button_text" />
|
||||
<input
|
||||
id="discord_button_text"
|
||||
type="text"
|
||||
class="text-input"
|
||||
name="discord_button_text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="group">
|
||||
@@ -302,23 +363,43 @@
|
||||
<div class="group__option">
|
||||
<div class="group__description">
|
||||
<h4>ListenBrainz API Url</h4>
|
||||
<p>There are multiple instances for ListenBrainz you can set the corresponding API url below.</p>
|
||||
<input id="ListenBrainzAPI" type="text" class="text-input" name="ListenBrainzAPI" />
|
||||
<p>
|
||||
There are multiple instances for ListenBrainz you can set the corresponding
|
||||
API url below.
|
||||
</p>
|
||||
<input
|
||||
id="ListenBrainzAPI"
|
||||
type="text"
|
||||
class="text-input"
|
||||
name="ListenBrainzAPI"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group__option">
|
||||
<div class="group__description">
|
||||
<h4>ListenBrainz User Token</h4>
|
||||
<p>Provide the user token you can get from the settings page.</p>
|
||||
<input id="ListenBrainzToken" type="text" class="text-input" name="ListenBrainzToken" />
|
||||
<input
|
||||
id="ListenBrainzToken"
|
||||
type="text"
|
||||
class="text-input"
|
||||
name="ListenBrainzToken"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group__description">
|
||||
<h4>ScrobbleDelay</h4>
|
||||
<p>The delay (in ms) to send a listen to ListenBrainz. Prevents spamming the API when you fast forward
|
||||
immediately</p>
|
||||
<input id="listenbrainz_delay" type="number" class="text-input" name="listenbrainz_delay" />
|
||||
<p>
|
||||
The delay (in ms) to send a listen to ListenBrainz. Prevents spamming the API when
|
||||
you fast forward immediately
|
||||
</p>
|
||||
<input
|
||||
id="listenbrainz_delay"
|
||||
type="number"
|
||||
class="text-input"
|
||||
name="listenbrainz_delay"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -330,12 +411,16 @@
|
||||
<div class="group__description">
|
||||
<h4>Update frequency</h4>
|
||||
<p>
|
||||
The amount of time, in milliseconds, that TIDAL Hi-Fi will refresh its playback info by scraping the
|
||||
website.
|
||||
The default of 500 seems to work in more cases but if you are fine with a bit more resource usage you
|
||||
can decrease it as well.
|
||||
The amount of time, in milliseconds, that TIDAL Hi-Fi will refresh its playback
|
||||
info by scraping the website. The default of 500 seems to work in more cases but
|
||||
if you are fine with a bit more resource usage you can decrease it as well.
|
||||
</p>
|
||||
<input id="updateFrequency" type="number" class="text-input" name="updateFrequency" />
|
||||
<input
|
||||
id="updateFrequency"
|
||||
type="number"
|
||||
class="text-input"
|
||||
name="updateFrequency"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -348,7 +433,9 @@
|
||||
</p>
|
||||
<select class="select-input" id="channel" name="channel">
|
||||
<option value="https://listen.tidal.com">Stable (listen.tidal.com)</option>
|
||||
<option value="https://listen.stage.tidal.com">Staging (listen.stage.tidal.com)</option>
|
||||
<option value="https://listen.stage.tidal.com">
|
||||
Staging (listen.stage.tidal.com)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -394,7 +481,8 @@
|
||||
<div class="group__description">
|
||||
<h4>Wayland support</h4>
|
||||
<p>
|
||||
Adds a couple of Electron flags to help TIDAL Hi-Fi run smoothly on the Wayland window system.
|
||||
Adds a couple of Electron flags to help TIDAL Hi-Fi run smoothly on the Wayland
|
||||
window system.
|
||||
</p>
|
||||
</div>
|
||||
<label class="switch">
|
||||
@@ -412,12 +500,19 @@
|
||||
<div class="group__description">
|
||||
<h4>Custom CSS</h4>
|
||||
<p>
|
||||
The css that you put in here will be injected into a style tag in the head of the document.
|
||||
The css that you put in here will be injected into a style tag in the head of
|
||||
the document.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="customCSS" class="textarea" cols="40" rows="8" spellcheck="false"></textarea>
|
||||
<textarea
|
||||
id="customCSS"
|
||||
class="textarea"
|
||||
cols="40"
|
||||
rows="8"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
|
||||
<div class="group">
|
||||
<p class="group__title">Theme files</p>
|
||||
@@ -427,9 +522,7 @@
|
||||
<p>
|
||||
Select a theme below or "Tidal - Default" to return to the original Tidal look.
|
||||
</p>
|
||||
<select class="select-input" id="themesList" name="themesList">
|
||||
|
||||
</select>
|
||||
<select class="select-input" id="themesList" name="themesList"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -437,14 +530,20 @@
|
||||
<div class="group__description">
|
||||
<h4>Upload new themes</h4>
|
||||
<p>
|
||||
Click the button and select the css files to import. They will be added to the theme list
|
||||
automatically.
|
||||
Click the button and select the css files to import. They will be added to the
|
||||
theme list automatically.
|
||||
</p>
|
||||
<div class="file-drop-area">
|
||||
<div>
|
||||
<span class="file-btn">Choose files</span>
|
||||
<span id="file-message" class="file-msg">or drag and drop files here</span>
|
||||
<input id="theme-files" class="file-input" type="file" accept=".css" multiple>
|
||||
<input
|
||||
id="theme-files"
|
||||
class="file-input"
|
||||
type="file"
|
||||
accept=".css"
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -456,17 +555,35 @@
|
||||
<img alt="tidal icon" class="about-section__icon" src="./icon.png" />
|
||||
<h4>TIDAL Hi-Fi</h4>
|
||||
<div class="about-section__version">
|
||||
<a target="_blank" rel="noopener"
|
||||
href="https://github.com/Mastermindzh/tidal-hifi/releases/tag/5.15.0">5.15.0</a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://github.com/Mastermindzh/tidal-hifi/releases/tag/5.17.0"
|
||||
>5.17.0</a
|
||||
>
|
||||
</div>
|
||||
<div class="about-section__links">
|
||||
<a target="_blank" rel="noopener" href="https://github.com/mastermindzh/tidal-hifi/"
|
||||
class="about-section__button">Github
|
||||
<i class="fa fa-external-link"></i></a>
|
||||
<a target="_blank" rel="noopener" href="https://github.com/Mastermindzh/tidal-hifi/issues"
|
||||
class="about-section__button">Report an issue <i class="fa fa-external-link"></i></a>
|
||||
<a target="_blank" rel="noopener" href="https://github.com/Mastermindzh/tidal-hifi/graphs/contributors"
|
||||
class="about-section__button">Contributors <i class="fa fa-external-link"></i></a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://github.com/mastermindzh/tidal-hifi/"
|
||||
class="about-section__button"
|
||||
>Github <i class="fa fa-external-link"></i
|
||||
></a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://github.com/Mastermindzh/tidal-hifi/issues"
|
||||
class="about-section__button"
|
||||
>Report an issue <i class="fa fa-external-link"></i
|
||||
></a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://github.com/Mastermindzh/tidal-hifi/graphs/contributors"
|
||||
class="about-section__button"
|
||||
>Contributors <i class="fa fa-external-link"></i
|
||||
></a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -478,6 +595,5 @@
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -10,7 +10,7 @@ import {
|
||||
} from "./features/listenbrainz/listenbrainz";
|
||||
import { StoreData } from "./features/listenbrainz/models/storeData";
|
||||
import { Logger } from "./features/logger";
|
||||
import { Songwhip } from "./features/songwhip/songwhip";
|
||||
import { SharingService } from "./features/sharingService/sharingService";
|
||||
import { addCustomCss } from "./features/theming/theming";
|
||||
import { convertDurationToSeconds } from "./features/time/parse";
|
||||
import { MediaInfo } from "./models/mediaInfo";
|
||||
@@ -48,8 +48,9 @@ const elements = {
|
||||
search: '[class^="searchField"]',
|
||||
shuffle: '*[data-test="shuffle"]',
|
||||
repeat: '*[data-test="repeat"]',
|
||||
account: '*[class^="profileOptions"]',
|
||||
settings: '*[data-test^="open-settings"]',
|
||||
account: '*[data-test^="profile-image-button"]',
|
||||
settings: '*[data-test^="sidebar-menu-button"]',
|
||||
openSettings: '*[data-test^="open-settings"]',
|
||||
media: '*[data-test="current-media-imagery"]',
|
||||
image: "img",
|
||||
current: '*[data-test="current-time"]',
|
||||
@@ -220,9 +221,9 @@ ListenBrainzStore.clear();
|
||||
function addHotKeys() {
|
||||
if (settingsStore.get(settings.enableCustomHotkeys)) {
|
||||
addHotkey("Control+p", function () {
|
||||
elements.click("account");
|
||||
setTimeout(() => {
|
||||
elements.click("settings");
|
||||
setTimeout(() => {
|
||||
elements.click("openSettings");
|
||||
}, 100);
|
||||
});
|
||||
addHotkey("Control+l", function () {
|
||||
@@ -254,11 +255,10 @@ function addHotKeys() {
|
||||
elements.click("repeat");
|
||||
});
|
||||
addHotkey("control+w", async function () {
|
||||
const result = await ipcRenderer.invoke(globalEvents.whip, getTrackURL());
|
||||
const url = Songwhip.getWhipUrl(result);
|
||||
const url = SharingService.getUniversalLink(getTrackURL());
|
||||
clipboard.writeText(url);
|
||||
new Notification({
|
||||
title: `Successfully whipped: `,
|
||||
title: `Universal link generated: `,
|
||||
body: `URL copied to clipboard: ${url}`,
|
||||
}).show();
|
||||
});
|
||||
@@ -394,8 +394,24 @@ function updateMediaInfo(mediaInfo: MediaInfo, notify: boolean) {
|
||||
if (mediaInfo) {
|
||||
currentMediaInfo = mediaInfo;
|
||||
ipcRenderer.send(globalEvents.updateInfo, mediaInfo);
|
||||
if (settingsStore.get(settings.notifications) && notify) {
|
||||
if (currentNotification) currentNotification.close();
|
||||
updateMpris(mediaInfo);
|
||||
updateListenBrainz(mediaInfo);
|
||||
if (notify) {
|
||||
sendNotification(mediaInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send a desktop notification if enabled in settings
|
||||
* @param mediaInfo
|
||||
* @param notify Whether to notify
|
||||
*/
|
||||
async function sendNotification(mediaInfo: MediaInfo) {
|
||||
if (settingsStore.get(settings.notifications)) {
|
||||
if (currentNotification) {
|
||||
currentNotification.close();
|
||||
}
|
||||
currentNotification = new Notification({
|
||||
title: mediaInfo.title,
|
||||
body: mediaInfo.artists,
|
||||
@@ -403,10 +419,6 @@ function updateMediaInfo(mediaInfo: MediaInfo, notify: boolean) {
|
||||
});
|
||||
currentNotification.show();
|
||||
}
|
||||
|
||||
updateMpris(mediaInfo);
|
||||
updateListenBrainz(mediaInfo);
|
||||
}
|
||||
}
|
||||
|
||||
function addMPRIS() {
|
||||
@@ -538,6 +550,7 @@ setInterval(function () {
|
||||
const artistsArray = elements.getArtistsArray();
|
||||
const artistsString = elements.getArtistsString(artistsArray);
|
||||
const songDashArtistTitle = `${title} - ${artistsString}`;
|
||||
const staticTitle = "TIDAL Hi-Fi";
|
||||
const titleOrArtistsChanged = currentSong !== songDashArtistTitle;
|
||||
const current = elements.getText("current");
|
||||
const currentStatus = getCurrentlyPlayingStatus();
|
||||
@@ -582,7 +595,9 @@ setInterval(function () {
|
||||
};
|
||||
|
||||
// update title, url and play info with new info
|
||||
setTitle(songDashArtistTitle);
|
||||
settingsStore.get(settings.staticWindowTitle)
|
||||
? setTitle(staticTitle)
|
||||
: setTitle(songDashArtistTitle);
|
||||
getTrackURL();
|
||||
currentSong = songDashArtistTitle;
|
||||
currentPlayStatus = currentStatus;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Client, Presence } from "discord-rpc";
|
||||
import { Client, SetActivity } from "@xhayper/discord-rpc";
|
||||
import { app, ipcMain } from "electron";
|
||||
import { globalEvents } from "../constants/globalEvents";
|
||||
import { settings } from "../constants/settings";
|
||||
@@ -12,9 +12,13 @@ const clientId = "833617820704440341";
|
||||
|
||||
export let rpc: Client;
|
||||
|
||||
const ACTIVITY_LISTENING = 2;
|
||||
const MAX_RETRIES = 5;
|
||||
const RETRY_DELAY = 10000;
|
||||
|
||||
const observer = () => {
|
||||
if (rpc) {
|
||||
rpc.setActivity(getActivity());
|
||||
updateActivity();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,10 +26,20 @@ const defaultPresence = {
|
||||
largeImageKey: "tidal-hifi-icon",
|
||||
largeImageText: `TIDAL Hi-Fi ${app.getVersion()}`,
|
||||
instance: false,
|
||||
type: ACTIVITY_LISTENING,
|
||||
};
|
||||
|
||||
const getActivity = (): Presence => {
|
||||
const presence: Presence = { ...defaultPresence };
|
||||
const updateActivity = () => {
|
||||
const showIdle = settingsStore.get<string, boolean>(settings.discord.showIdle) ?? true;
|
||||
if (mediaInfo.status === MediaStatus.paused && !showIdle) {
|
||||
rpc.user?.clearActivity();
|
||||
} else {
|
||||
rpc.user?.setActivity(getActivity());
|
||||
}
|
||||
};
|
||||
|
||||
const getActivity = (): SetActivity => {
|
||||
const presence: SetActivity = { ...defaultPresence };
|
||||
|
||||
if (mediaInfo.status === MediaStatus.paused) {
|
||||
presence.details =
|
||||
@@ -41,6 +55,7 @@ const getActivity = (): Presence => {
|
||||
settingsStore.get<string, string>(settings.discord.usingText) ?? "Playing media on TIDAL";
|
||||
}
|
||||
}
|
||||
|
||||
return presence;
|
||||
|
||||
function getFromStore() {
|
||||
@@ -54,18 +69,33 @@ const getActivity = (): Presence => {
|
||||
return { includeTimestamps, detailsPrefix, buttonText };
|
||||
}
|
||||
|
||||
function setPresenceFromMediaInfo(detailsPrefix: string, buttonText: string) {
|
||||
if (mediaInfo.url) {
|
||||
presence.details = `${detailsPrefix}${mediaInfo.title}`;
|
||||
presence.state = mediaInfo.artists ? mediaInfo.artists : "unknown artist(s)";
|
||||
presence.largeImageKey = mediaInfo.image;
|
||||
if (mediaInfo.album) {
|
||||
presence.largeImageText = mediaInfo.album;
|
||||
/**
|
||||
* Pad a string using spaces to at least 2 characters
|
||||
* @param input string to pad with 2 characters
|
||||
* @returns
|
||||
*/
|
||||
function pad(input: string): string {
|
||||
return input.padEnd(2, " ");
|
||||
}
|
||||
|
||||
function setPresenceFromMediaInfo(detailsPrefix: string, buttonText: string) {
|
||||
// discord requires a minimum of 2 characters
|
||||
const title = pad(mediaInfo.title);
|
||||
const album = pad(mediaInfo.album);
|
||||
const artists = pad(mediaInfo.artists);
|
||||
|
||||
if (mediaInfo.url) {
|
||||
presence.details = `${detailsPrefix}${title}`;
|
||||
presence.state = artists ? artists : "unknown artist(s)";
|
||||
presence.largeImageKey = mediaInfo.image;
|
||||
if (album) {
|
||||
presence.largeImageText = album;
|
||||
}
|
||||
|
||||
presence.buttons = [{ label: buttonText, url: mediaInfo.url }];
|
||||
} else {
|
||||
presence.details = `Watching ${mediaInfo.title}`;
|
||||
presence.state = mediaInfo.artists;
|
||||
presence.details = `Watching ${title}`;
|
||||
presence.state = artists;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +104,31 @@ const getActivity = (): Presence => {
|
||||
const currentSeconds = convertDurationToSeconds(mediaInfo.current);
|
||||
const durationSeconds = convertDurationToSeconds(mediaInfo.duration);
|
||||
const date = new Date();
|
||||
const now = (date.getTime() / 1000) | 0;
|
||||
const remaining = date.setSeconds(date.getSeconds() + (durationSeconds - currentSeconds));
|
||||
presence.startTimestamp = now;
|
||||
presence.endTimestamp = remaining;
|
||||
const now = Math.floor(date.getTime() / 1000);
|
||||
presence.startTimestamp = now - currentSeconds;
|
||||
presence.endTimestamp = presence.startTimestamp + durationSeconds;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to login to RPC and retry if it errors
|
||||
* @param retryCount Max retry count
|
||||
*/
|
||||
const connectWithRetry = async (retryCount = 0) => {
|
||||
try {
|
||||
await rpc.login();
|
||||
Logger.log("Connected to Discord");
|
||||
rpc.on("ready", updateActivity);
|
||||
Object.values(globalEvents).forEach((event) => ipcMain.on(event, observer));
|
||||
} catch (error) {
|
||||
if (retryCount < MAX_RETRIES) {
|
||||
Logger.log(
|
||||
`Failed to connect to Discord, retrying in ${RETRY_DELAY / 1000} seconds... (Attempt ${retryCount + 1}/${MAX_RETRIES})`
|
||||
);
|
||||
setTimeout(() => connectWithRetry(retryCount + 1), RETRY_DELAY);
|
||||
} else {
|
||||
Logger.log("Failed to connect to Discord after maximum retry attempts");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -86,18 +137,8 @@ const getActivity = (): Presence => {
|
||||
* Set up the discord rpc and listen on globalEvents.updateInfo
|
||||
*/
|
||||
export const initRPC = () => {
|
||||
rpc = new Client({ transport: "ipc" });
|
||||
rpc.login({ clientId }).then(
|
||||
() => {
|
||||
rpc.on("ready", () => {
|
||||
rpc.setActivity(getActivity());
|
||||
});
|
||||
ipcMain.on(globalEvents.updateInfo, observer);
|
||||
},
|
||||
() => {
|
||||
Logger.log("Can't connect to Discord, is it running?");
|
||||
}
|
||||
);
|
||||
rpc = new Client({ transport: { type: "ipc" }, clientId });
|
||||
connectWithRetry();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,7 +146,7 @@ export const initRPC = () => {
|
||||
*/
|
||||
export const unRPC = () => {
|
||||
if (rpc) {
|
||||
rpc.clearActivity();
|
||||
rpc.user?.clearActivity();
|
||||
rpc.destroy();
|
||||
rpc = null;
|
||||
ipcMain.removeListener(globalEvents.updateInfo, observer);
|
||||
|
@@ -43,6 +43,7 @@ export const settingsStore = new Store({
|
||||
enableDiscord: false,
|
||||
discord: {
|
||||
showSong: true,
|
||||
showIdle: true,
|
||||
idleText: "Browsing Tidal",
|
||||
usingText: "Playing media on TIDAL",
|
||||
includeTimestamps: true,
|
||||
@@ -68,6 +69,7 @@ export const settingsStore = new Store({
|
||||
singleInstance: true,
|
||||
skipArtists: false,
|
||||
skippedArtists: [""],
|
||||
staticWindowTitle: false,
|
||||
theme: "none",
|
||||
trayIcon: true,
|
||||
updateFrequency: 500,
|
||||
@@ -115,6 +117,9 @@ export const settingsStore = new Store({
|
||||
{ key: settings.advanced.tidalUrl, value: "https://listen.tidal.com" },
|
||||
]);
|
||||
},
|
||||
"5.16.0": (migrationStore) => {
|
||||
buildMigration("5.16.0", migrationStore, [{ key: settings.discord.showIdle, value: "true" }]);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user