From a4104d60390a089975e4866155077ef7cc17fbe0 Mon Sep 17 00:00:00 2001 From: Nick Oates Date: Thu, 25 Jul 2024 22:19:13 -0700 Subject: [PATCH 1/5] "Listening to TIDAL" status --- .../native/discordRPC/updateRPC.native.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts index 3a014a90..faee1469 100644 --- a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts +++ b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts @@ -17,7 +17,11 @@ export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: Play const _rpcClient = await rpcClient.ensureRPC(); if (_rpcClient === undefined) throw new Error("Failed to obtain RPC client"); - const activityState: Presence = {}; + const activityState: Presence = { + // TODO: Import this as a constant from `discord-rpc` once https://github.com/discordjs/RPC/pull/149 is merged + // @ts-expect-error Not added to library yet + type: 2, + }; const { keepRpcOnPause, displayPlayButton } = settings; @@ -41,14 +45,10 @@ export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: Play } // Title/Artist - const artist = `by ${currentlyPlaying?.artist?.name ?? currentlyPlaying.artists?.[0]?.name ?? "Unknown Artist"}`; - const desc = `${currentlyPlaying.title} ${artist}`; - if (desc.length >= 32) { - activityState.details = formatLongString(currentlyPlaying.title); - activityState.state = formatLongString(artist); - } else { - activityState.details = formatLongString(desc); - } + const artist = currentlyPlaying.artists?.map((a) => a.name).join(", ") ?? "Unknown Artist"; + + activityState.details = formatLongString(currentlyPlaying.title); + activityState.state = formatLongString(artist); return _rpcClient.setActivity(activityState); }; From 839527c710a40c16db4231e1f29c1375a5a164f9 Mon Sep 17 00:00:00 2001 From: Nick Oates Date: Thu, 25 Jul 2024 23:19:38 -0700 Subject: [PATCH 2/5] Show artist image --- plugins/DiscordRPC/src/Settings.ts | 9 +++++++++ .../native/discordRPC/updateRPC.native.ts | 18 +++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/plugins/DiscordRPC/src/Settings.ts b/plugins/DiscordRPC/src/Settings.ts index ff408deb..b177ff42 100644 --- a/plugins/DiscordRPC/src/Settings.ts +++ b/plugins/DiscordRPC/src/Settings.ts @@ -6,6 +6,7 @@ import { onTimeUpdate } from "."; export const settings = getSettings({ keepRpcOnPause: true, displayPlayButton: true, + displayArtistImage: true, }); export const Settings = () => html`<${SwitchSetting} @@ -23,4 +24,12 @@ export const Settings = () => html`<${SwitchSetting} settings.displayPlayButton = !settings.displayPlayButton; }} title="Display play button" + /> + <${SwitchSetting} + checked=${settings.displayArtistImage} + onClick=${() => { + onTimeUpdate(!settings).catch(() => {}); + settings.displayArtistImage = !settings.displayArtistImage; + }} + title="Display artist image" />`; diff --git a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts index faee1469..ef6b6f47 100644 --- a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts +++ b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts @@ -13,7 +13,7 @@ const formatLongString = (s?: string) => { const getMediaURLFromID = (id?: string, path = "/1280x1280.jpg") => (id ? "https://resources.tidal.com/images/" + id.split("-").join("/") + path : undefined); export const onRpcCleanup = () => rpcClient.cleanp(); -export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: PlaybackState, settings: { keepRpcOnPause: boolean; displayPlayButton: boolean }, currentTime?: number) => { +export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: PlaybackState, settings: { keepRpcOnPause: boolean; displayPlayButton: boolean; displayArtistImage: boolean }, currentTime?: number) => { const _rpcClient = await rpcClient.ensureRPC(); if (_rpcClient === undefined) throw new Error("Failed to obtain RPC client"); @@ -32,11 +32,19 @@ export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: Play if (keepRpcOnPause === false) return _rpcClient.clearActivity(); activityState.smallImageKey = "paused-icon"; activityState.smallImageText = "Paused"; - } else if (currentlyPlaying.duration !== undefined && currentTime !== undefined) { + } else { // Playback/Time - activityState.startTimestamp = Math.floor(Date.now() / 1000); - activityState.endTimestamp = Math.floor((Date.now() + (currentlyPlaying.duration - currentTime) * 1000) / 1000); - } + if (currentlyPlaying.duration !== undefined && currentTime !== undefined) { + activityState.startTimestamp = Math.floor(Date.now() / 1000); + activityState.endTimestamp = Math.floor((Date.now() + (currentlyPlaying.duration - currentTime) * 1000) / 1000); + } + + // Artist image + if (currentlyPlaying.artist) { + activityState.smallImageKey = getMediaURLFromID(currentlyPlaying.artist.picture, "/320x320.jpg"); + activityState.smallImageText = formatLongString(currentlyPlaying.artist.name); + } + } // Album if (currentlyPlaying.album !== undefined) { From 03d1e7f1d4aec5a4a9d9b13f7d8eade6b1df8003 Mon Sep 17 00:00:00 2001 From: Nick Oates Date: Thu, 25 Jul 2024 23:26:22 -0700 Subject: [PATCH 3/5] Actually check if the setting is enabled --- plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts index ef6b6f47..5941ca3b 100644 --- a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts +++ b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts @@ -40,7 +40,7 @@ export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: Play } // Artist image - if (currentlyPlaying.artist) { + if (currentlyPlaying.artist && settings.displayArtistImage) { activityState.smallImageKey = getMediaURLFromID(currentlyPlaying.artist.picture, "/320x320.jpg"); activityState.smallImageText = formatLongString(currentlyPlaying.artist.name); } From e5771598e78e81a30872ce389c26f09427bb4c80 Mon Sep 17 00:00:00 2001 From: Nick Oates Date: Fri, 26 Jul 2024 12:09:26 -0700 Subject: [PATCH 4/5] Check for `currentTime` before clearing activity or showing pause icon --- plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts index 5941ca3b..eadea4d9 100644 --- a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts +++ b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts @@ -28,7 +28,7 @@ export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: Play if (displayPlayButton) activityState.buttons = [{ url: currentlyPlaying.url ?? `https://tidal.com/browse/track/${currentlyPlaying.id}?u`, label: "Play on Tidal" }]; // Pause indicator - if (playbackState === "NOT_PLAYING") { + if (playbackState === "NOT_PLAYING" && currentTime) { if (keepRpcOnPause === false) return _rpcClient.clearActivity(); activityState.smallImageKey = "paused-icon"; activityState.smallImageText = "Paused"; From 42bd66e64b78e4402a47811f83c0cecff581183f Mon Sep 17 00:00:00 2001 From: Nick Oates Date: Sun, 28 Jul 2024 19:53:06 -0700 Subject: [PATCH 5/5] Switch to @xhayper/discord-rpc --- package-lock.json | 242 +++++++++--------- package.json | 5 +- plugins/DiscordRPC/src/index.ts | 28 +- .../native/discordRPC/DiscordRPC.native.ts | 59 +++-- .../native/discordRPC/updateRPC.native.ts | 94 ++++--- 5 files changed, 245 insertions(+), 183 deletions(-) diff --git a/package-lock.json b/package-lock.json index 701d9301..1945f874 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,14 +8,13 @@ "hasInstallScript": true, "dependencies": { "@inrixia/helpers": "^2.0.11", + "@xhayper/discord-rpc": "^1.1.4", "dasha": "^3.0.3", - "discord-rpc": "^4.0.1", "flac-stream-tagger": "^1.0.9", "idb": "^8.0.0", "music-metadata": "^10.0.0" }, "devDependencies": { - "@types/discord-rpc": "^4.0.8", "@types/node": "^20.14.12", "concurrently": "^8.2.2", "electron": "^31.3.0", @@ -528,23 +527,6 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/discord-rpc": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-4.0.8.tgz", - "integrity": "sha512-1tZf217Natkj+TziNXRRLwNmdm5GNa1bnrQr8VWowquo/Su5hMjdhobj8URxW1COMk2da28XCU1ahsYCAlxirA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/events": "*" - } - }, - "node_modules/@types/events": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", - "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -607,6 +589,22 @@ "npm": ">=5" } }, + "node_modules/@xhayper/discord-rpc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@xhayper/discord-rpc/-/discord-rpc-1.1.4.tgz", + "integrity": "sha512-yq2ybstOWsfAN6LP5vogUpnxkaiKV1yXQ+8N4Sgo8YbE+3atgxuNiZWnGG6yO+XdxswvbD04AkLhnq7f9r8h3w==", + "dependencies": { + "axios": "^1.7.2", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=14.18.0" + }, + "optionalDependencies": { + "bufferutil": "^4.0.8", + "utf-8-validate": "^6.0.4" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -657,6 +655,21 @@ "lodash": "^4.17.14" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -690,16 +703,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -742,6 +745,19 @@ "node": "*" } }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -904,6 +920,17 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1109,6 +1136,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -1117,19 +1152,6 @@ "license": "MIT", "optional": true }, - "node_modules/discord-rpc": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-4.0.1.tgz", - "integrity": "sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.1", - "ws": "^7.3.1" - }, - "optionalDependencies": { - "register-scheme": "github:devsnek/node-register-scheme" - } - }, "node_modules/dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", @@ -1331,13 +1353,6 @@ "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT", - "optional": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1364,7 +1379,6 @@ "version": "1.15.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, "funding": [ { "type": "individual", @@ -1381,6 +1395,19 @@ } } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -1947,6 +1974,25 @@ "node": ">=4" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -2052,31 +2098,15 @@ "zod": "^3.2.2" } }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "license": "MIT", - "optional": true - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, "node_modules/nodemon": { @@ -2264,6 +2294,11 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -2338,17 +2373,6 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, - "node_modules/register-scheme": { - "version": "0.0.2", - "resolved": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "bindings": "^1.3.0", - "node-addon-api": "^1.3.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2679,12 +2703,6 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -2787,6 +2805,19 @@ "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz", "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==" }, + "node_modules/utf-8-validate": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.4.tgz", + "integrity": "sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/voby": { "version": "0.54.0", "resolved": "https://registry.npmjs.org/voby/-/voby-0.54.0.tgz", @@ -2797,12 +2828,6 @@ "oby": "^14.3.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -2816,16 +2841,6 @@ "node": ">=12" } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2852,16 +2867,15 @@ "license": "ISC" }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { diff --git a/package.json b/package.json index 06b864e3..105121b6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "postinstall": "node postinstall.js" }, "devDependencies": { - "@types/discord-rpc": "^4.0.8", "@types/node": "^20.14.12", "concurrently": "^8.2.2", "electron": "^31.3.0", @@ -20,8 +19,8 @@ }, "dependencies": { "@inrixia/helpers": "^2.0.11", + "@xhayper/discord-rpc": "^1.1.4", "dasha": "^3.0.3", - "discord-rpc": "^4.0.1", "flac-stream-tagger": "^1.0.9", "idb": "^8.0.0", "music-metadata": "^10.0.0" @@ -34,4 +33,4 @@ "ext": "*", "exec": "npm run build" } -} \ No newline at end of file +} diff --git a/plugins/DiscordRPC/src/index.ts b/plugins/DiscordRPC/src/index.ts index 8e18b454..758a598d 100644 --- a/plugins/DiscordRPC/src/index.ts +++ b/plugins/DiscordRPC/src/index.ts @@ -16,19 +16,31 @@ export const onTimeUpdate = async (settings: any, newTime?: number) => { let { playbackContext, playbackState } = getPlaybackControl(); if (!playbackState) return; - const currentlyPlaying = await TrackItemCache.ensure((currentPlaybackContext ?? playbackContext)?.actualProductId); + const currentlyPlaying = await TrackItemCache.ensure( + (currentPlaybackContext ?? playbackContext)?.actualProductId + ); if (currentlyPlaying === undefined) return; updateRPC(currentlyPlaying, playbackState, { ...settings }, newTime); }; -const onUnloadTimeUpdate = intercept("playbackControls/TIME_UPDATE", ([newTime]) => { - onTimeUpdate(settings, newTime).catch(trace.msg.err.withContext("Failed to update")); -}); -const onUnloadNewTrack = intercept("playbackControls/MEDIA_PRODUCT_TRANSITION", ([{ playbackContext }]) => { - currentPlaybackContext = playbackContext; - onTimeUpdate(settings).catch(trace.msg.err.withContext("Failed to update")); -}); +const onUnloadTimeUpdate = intercept( + "playbackControls/TIME_UPDATE", + ([newTime]) => { + onTimeUpdate(settings, newTime).catch( + trace.msg.err.withContext("Failed to update") + ); + } +); +const onUnloadNewTrack = intercept( + "playbackControls/MEDIA_PRODUCT_TRANSITION", + ([{ playbackContext }]) => { + currentPlaybackContext = playbackContext; + onTimeUpdate(settings).catch( + trace.msg.err.withContext("Failed to update") + ); + } +); onTimeUpdate(settings).catch(trace.msg.err.withContext("Failed to update")); export const onUnload = () => { onUnloadTimeUpdate(); diff --git a/plugins/_lib/nativeBridge/native/discordRPC/DiscordRPC.native.ts b/plugins/_lib/nativeBridge/native/discordRPC/DiscordRPC.native.ts index 5a7bee52..e52b4fd8 100644 --- a/plugins/_lib/nativeBridge/native/discordRPC/DiscordRPC.native.ts +++ b/plugins/_lib/nativeBridge/native/discordRPC/DiscordRPC.native.ts @@ -1,37 +1,42 @@ -import { Client } from "discord-rpc"; -const onCleanupErr = (err: Error) => console.warn("Encountered error while cleaning up DiscordRPC", err); +import { Client } from "@xhayper/discord-rpc"; +const onCleanupErr = (err: Error) => + console.warn("Encountered error while cleaning up DiscordRPC", err); + +type ClientWithUser = Client & { user: NonNullable }; + export class DiscordRPC { - public rpcClient?: Client; - constructor(private readonly clientId: string) {} + private rpcClient: Client; - public isConnected() { - // @ts-expect-error Types dont include internals like transport - return !!this.rpcClient?.transport?.socket; + constructor(private readonly clientId: string) { + this.rpcClient = new Client({ + transport: { type: "ipc" }, + clientId, + }); } - async ensureRPC(): Promise { + + async getClient(): Promise { try { - if (this.rpcClient && this.isConnected()) return this.rpcClient; - await this.cleanp(true); - this.rpcClient = new Client({ transport: "ipc" }); - const ready = new Promise((res, rej) => { - const rejTimeout = setTimeout(() => rej(new Error("Timed out waiting for RPC to be ready")), 5000); - this.rpcClient!.once("ready", () => { - clearTimeout(rejTimeout); - res(); - }); - }); - this.rpcClient = await this.rpcClient.login({ clientId: this.clientId }); - await ready; - if (!this.isConnected()) return this.ensureRPC(); - return this.rpcClient; + if (this.rpcClient.transport.isConnected && this.rpcClient.user) + return this.rpcClient as ClientWithUser; + + await this.rpcClient.connect(); + + if (!this.rpcClient.user || !this.rpcClient.transport.isConnected) { + throw new Error("Failed to obtain RPC client or user"); + } + + return this.rpcClient as ClientWithUser; } catch (err) { - await this.cleanp(true); + await this.cleanup(true); throw err; } } - async cleanp(clearActivity: boolean = true) { - if (clearActivity) await this.rpcClient?.clearActivity().catch(onCleanupErr); - await this.rpcClient?.destroy().catch(onCleanupErr); - delete this.rpcClient; + + async cleanup(clearActivity: boolean = true) { + if (clearActivity) { + await this.rpcClient.user?.clearActivity().catch(onCleanupErr); + } + + await this.rpcClient.destroy().catch(onCleanupErr); } } diff --git a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts index eadea4d9..1b4a27e0 100644 --- a/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts +++ b/plugins/_lib/nativeBridge/native/discordRPC/updateRPC.native.ts @@ -1,8 +1,8 @@ -import { Presence } from "discord-rpc"; +import type { SetActivity } from "@xhayper/discord-rpc"; import { DiscordRPC } from "./DiscordRPC.native"; import { PlaybackState, TrackItem } from "neptune-types/tidal"; -const rpcClient = new DiscordRPC("1130698654987067493"); +const rpc = new DiscordRPC("1130698654987067493"); const STR_MAX_LEN = 127; const formatLongString = (s?: string) => { @@ -10,53 +10,85 @@ const formatLongString = (s?: string) => { if (s.length < 2) s += " "; return s.length >= STR_MAX_LEN ? s.slice(0, STR_MAX_LEN - 3) + "..." : s; }; -const getMediaURLFromID = (id?: string, path = "/1280x1280.jpg") => (id ? "https://resources.tidal.com/images/" + id.split("-").join("/") + path : undefined); +const getMediaURLFromID = (id?: string, path = "/1280x1280.jpg") => + id + ? "https://resources.tidal.com/images/" + id.split("-").join("/") + path + : undefined; -export const onRpcCleanup = () => rpcClient.cleanp(); -export const updateRPC = async (currentlyPlaying: TrackItem, playbackState: PlaybackState, settings: { keepRpcOnPause: boolean; displayPlayButton: boolean; displayArtistImage: boolean }, currentTime?: number) => { - const _rpcClient = await rpcClient.ensureRPC(); - if (_rpcClient === undefined) throw new Error("Failed to obtain RPC client"); +export const onRpcCleanup = () => rpc.cleanup(); +export const updateRPC = async ( + currentlyPlaying: TrackItem, + playbackState: PlaybackState, + settings: { + keepRpcOnPause: boolean; + displayPlayButton: boolean; + displayArtistImage: boolean; + }, + currentTime?: number +) => { + const client = await rpc.getClient(); - const activityState: Presence = { - // TODO: Import this as a constant from `discord-rpc` once https://github.com/discordjs/RPC/pull/149 is merged - // @ts-expect-error Not added to library yet - type: 2, - }; + const activity: SetActivity = { type: 2 }; // Listening type - const { keepRpcOnPause, displayPlayButton } = settings; - - if (displayPlayButton) activityState.buttons = [{ url: currentlyPlaying.url ?? `https://tidal.com/browse/track/${currentlyPlaying.id}?u`, label: "Play on Tidal" }]; + if (settings.displayPlayButton) + activity.buttons = [ + { + url: + currentlyPlaying.url ?? + `https://tidal.com/browse/track/${currentlyPlaying.id}?u`, + label: "Play on Tidal", + }, + ]; // Pause indicator - if (playbackState === "NOT_PLAYING" && currentTime) { - if (keepRpcOnPause === false) return _rpcClient.clearActivity(); - activityState.smallImageKey = "paused-icon"; - activityState.smallImageText = "Paused"; + if (playbackState === "NOT_PLAYING") { + if (settings.keepRpcOnPause === false) + return client.user.clearActivity(); + activity.smallImageKey = "paused-icon"; + activity.smallImageText = "Paused"; } else { // Playback/Time - if (currentlyPlaying.duration !== undefined && currentTime !== undefined) { - activityState.startTimestamp = Math.floor(Date.now() / 1000); - activityState.endTimestamp = Math.floor((Date.now() + (currentlyPlaying.duration - currentTime) * 1000) / 1000); + if ( + currentlyPlaying.duration !== undefined && + currentTime !== undefined + ) { + activity.startTimestamp = Math.floor(Date.now() / 1000); + activity.endTimestamp = Math.floor( + (Date.now() + + (currentlyPlaying.duration - currentTime) * 1000) / + 1000 + ); } // Artist image if (currentlyPlaying.artist && settings.displayArtistImage) { - activityState.smallImageKey = getMediaURLFromID(currentlyPlaying.artist.picture, "/320x320.jpg"); - activityState.smallImageText = formatLongString(currentlyPlaying.artist.name); + activity.smallImageKey = getMediaURLFromID( + currentlyPlaying.artist.picture, + "/320x320.jpg" + ); + activity.smallImageText = formatLongString( + currentlyPlaying.artist.name + ); } - } + } // Album if (currentlyPlaying.album !== undefined) { - activityState.largeImageKey = getMediaURLFromID(currentlyPlaying.album.cover); - activityState.largeImageText = formatLongString(currentlyPlaying.album.title); + activity.largeImageKey = getMediaURLFromID( + currentlyPlaying.album.cover + ); + activity.largeImageText = formatLongString( + currentlyPlaying.album.title + ); } // Title/Artist - const artist = currentlyPlaying.artists?.map((a) => a.name).join(", ") ?? "Unknown Artist"; + const artist = + currentlyPlaying.artists?.map((a) => a.name).join(", ") ?? + "Unknown Artist"; - activityState.details = formatLongString(currentlyPlaying.title); - activityState.state = formatLongString(artist); + activity.details = formatLongString(currentlyPlaying.title); + activity.state = formatLongString(artist); - return _rpcClient.setActivity(activityState); + return client.user.setActivity(activity); };