From b745827f79fcf8cb128da5287887652910efe819 Mon Sep 17 00:00:00 2001 From: josselinonduty Date: Sat, 1 Feb 2025 17:39:26 +0100 Subject: [PATCH] feat: provide duration and url information to mpris and discord rpc; update rich-presence-builder; make discord rpc opt-in --- patches/10-additional-metadata.patch | 288 +++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 patches/10-additional-metadata.patch diff --git a/patches/10-additional-metadata.patch b/patches/10-additional-metadata.patch new file mode 100644 index 0000000..11d4e86 --- /dev/null +++ b/patches/10-additional-metadata.patch @@ -0,0 +1,288 @@ +From 493f6f6a6433882cc2d50b4ae352cd808bf0c23e Mon Sep 17 00:00:00 2001 +From: josselinonduty +Date: Sat, 1 Feb 2025 17:34:11 +0100 +Subject: [PATCH] feat: provide duration and url information to mpris and + discord rpc; update rich-presence-builder; make discord rpc opt-in + +--- + build/main.js | 144 ++++++++++++++++++++++++++++++++++------------ + build/renderer.js | 10 ++-- + package.json | 2 +- + 3 files changed, 113 insertions(+), 43 deletions(-) + +diff --git a/build/main.js b/build/main.js +index c62943b..66c1a45 100644 +--- a/build/main.js ++++ b/build/main.js +@@ -120,11 +120,11 @@ + var external_electron_mpris_default = __webpack_require__.n( + external_electron_mpris_namespaceObject + ); +- const external_rich_presence_builder_namespaceObject = require("rich-presence-builder"); ++ const external_rich_presence_builder_namespaceObject = require("@josselinonduty/rich-presence-builder"); + var external_rich_presence_builder_default = __webpack_require__.n( + external_rich_presence_builder_namespaceObject + ); +- var rpcConnection; ++ var rpcConnection, rpcData, rpcStartedAt, rpcPausedAt; + function isPlatform(platform) { + switch (platform) { + case PLATFORM.WINDOWS: +@@ -1248,7 +1248,6 @@ + this.stop(); + })); + this.initMprisPlayerControls(); +- this.initDiscordRichPresence(); + } + initMprisPlayerControls() { + // Events => ['raise', 'quit', 'next', 'previous', 'pause', 'playpause', 'stop', 'play', 'seek', 'position', 'open', 'volume', 'loopStatus', 'shuffle']; +@@ -1262,27 +1261,92 @@ + this.mprisPlayer.on('loopStatus', this.setRepeatMode.bind(this)); + this.mprisPlayer.on('raise', () => this.app.getWindow().show()) + } +- initDiscordRichPresence() { +- if (process.argv.some(arg => arg === '--disable-discord-rpc')) return; +- rpcConnection = new external_rich_presence_builder_namespaceObject({ +- clientID: "1244016234203185183", +- }); +- }; +- updateDiscordRichPresence(track) { +- if (!rpcConnection) return; +- rpcConnection.setSmallImage( +- this.player.state === "playing" ? "play" : "pause", +- this.player.state === "playing" ? "Playing" : "Paused" +- ); +- if (track) { +- rpcConnection.setLargeImage(track.coverUrl); +- rpcConnection.setDescription(track.title); +- if (track.title === track.album) +- rpcConnection.setState(`${track.artist}`); +- else +- rpcConnection.setState(`${track.artist} - ${track.album}`); ++ updateDiscordRichPresence(track, data) { ++ if (!process.argv.some(arg => arg === '--enable-discord-rpc')) return; ++ ++ if (track && data) { ++ // Update track information ++ const duration = data?.trackInfo?.song?.DURATION; ++ if (!duration) return; ++ ++ rpcStartedAt = Date.now(); ++ rpcPausedAt = undefined; ++ rpcData = { ++ type: 2, ++ smallImage: { ++ image: "play", ++ text: "Playing" ++ }, ++ largeImage: track.coverUrl, ++ description: track.title, ++ state: track.title === track.album ? ++ `${track.artist}` ++ : `${track.artist} - ${track.album}`, ++ startTimestamp: rpcStartedAt, ++ button: { ++ label: "Listen on Deezer", ++ url: `https://deezer.com/track/${data.trackInfo.song.SNG_ID}` ++ } ++ } ++ ++ rpcConnection = new external_rich_presence_builder_namespaceObject({ ++ clientID: "1244016234203185183", ++ }) ++ .setType(rpcData.type) ++ .setSmallImage( ++ rpcData.smallImage.image, ++ rpcData.smallImage.text ++ ) ++ .setLargeImage(rpcData.largeImage) ++ .setDescription(rpcData.description) ++ .setState(rpcData.state) ++ .setStartTimestamp(rpcData.startTimestamp) ++ .addButton(rpcData.button.label, rpcData.button.url); ++ rpcConnection ++ .go().catch() ++ } else { ++ // Update playing state only ++ if (!rpcConnection) return; ++ ++ if (this.player.state === "playing") { ++ if (rpcPausedAt) { ++ const elapsed = rpcPausedAt - rpcStartedAt; ++ rpcStartedAt = Date.now() - elapsed; ++ rpcData.startTimestamp = rpcStartedAt; ++ } ++ rpcPausedAt = undefined; ++ rpcData.smallImage = { ++ image: "play", ++ text: "Playing" ++ } ++ } else { ++ rpcData.startTimestamp = undefined; ++ rpcPausedAt = Date.now(); ++ rpcData.smallImage = { ++ image: "pause", ++ text: "Paused" ++ } ++ } ++ ++ rpcConnection = new external_rich_presence_builder_namespaceObject({ ++ clientID: "1244016234203185183", ++ }) ++ .setType(rpcData.type) ++ .setSmallImage( ++ rpcData.smallImage.image, ++ rpcData.smallImage.text ++ ) ++ .setLargeImage(rpcData.largeImage) ++ .setDescription(rpcData.description) ++ .setState(rpcData.state) ++ .addButton(rpcData.button.label, rpcData.button.url); ++ ++ if (rpcData.startTimestamp) ++ rpcConnection.setStartTimestamp(rpcData.startTimestamp); ++ ++ rpcConnection ++ .go().catch() + } +- rpcConnection.go().catch(); + } + play() { + this.ipc.send("channel-player-media-control", MediaPlayerControl.Play); +@@ -1308,19 +1372,25 @@ + setRepeatMode(repeatMode) { + this.ipc.send("channel-player-repeat-mode-update", repeatMode); + } +- setTrackInfo(track) { ++ setTrackInfo(track, data) { + (this.track = Object.assign(this.track, track)), + this.emit(MediaEvents.TrackUpdated, this.track), + (this.mprisPlayer.metadata = { ++ 'mpris:length': data?.trackInfo?.song?.DURATION ++ ? data.trackInfo.song.DURATION * 1000 ++ : undefined, + 'mpris:trackid': this.mprisPlayer.objectPath('track/0'), + 'mpris:artUrl': track.coverUrl, + 'xesam:title': track.title, + 'xesam:album': track.album, +- 'xesam:artist': [track.artist] +- }), +- this.updateDiscordRichPresence(track); ++ 'xesam:artist': [track.artist], ++ 'xesam:url': data?.trackInfo?.song ++ ? `https://deezer.com/track/${data.trackInfo.song.SNG_ID}` ++ : undefined ++ }); ++ this.updateDiscordRichPresence(track, data); + } +- setPlayerInfo(player) { ++ setPlayerInfo(player, data) { + (this.player = Object.assign(this.player, player)), + this.emit(MediaEvents.PlayerUpdated, this.player), + (this.mprisPlayer.playbackStatus = +@@ -2833,8 +2903,8 @@ + var powerSaveTimeoutId; + PlayerIpc_ipc.on( + "channel-player-state-update", +- (event, state) => { +- media.setPlayerInfo({ state }), ++ (event, state, data) => { ++ media.setPlayerInfo({ state }, data), + clearTimeout(powerSaveTimeoutId), + powerSaveTimeoutId = setTimeout( + () => { +@@ -2847,15 +2917,15 @@ + ), + PlayerIpc_ipc.on( + "channel-player-track-update", +- (event, track, player) => { +- media.setPlayerInfo(player), media.setTrackInfo(track); ++ (event, track, player, data) => { ++ media.setPlayerInfo(player, data), media.setTrackInfo(track, data); + } + ), +- PlayerIpc_ipc.on("channel-player-shuffle-update", (event, player) => { +- media.setPlayerInfo(player); ++ PlayerIpc_ipc.on("channel-player-shuffle-update", (event, player, data) => { ++ media.setPlayerInfo(player, data); + }), +- PlayerIpc_ipc.on("channel-player-repeat-mode-update", (event, player) => { +- media.setPlayerInfo(player); ++ PlayerIpc_ipc.on("channel-player-repeat-mode-update", (event, player, data) => { ++ media.setPlayerInfo(player, data); + }); + const UpdaterIpc_ipc = main_di.get(SERVICE_IPC), + autoUpdater = main_di.get(SERVICE_UPDATER); +@@ -2864,7 +2934,7 @@ + }); + const UserIpc_ipc = main_di.get(SERVICE_IPC), + user = main_di.get(SERVICE_USER); +- UserIpc_ipc.on("channel-user-store-updated", (event, userData) => { ++ UserIpc_ipc.on("channel-user-store-updated", (event, userData, data) => { + user.setUserInfo(userData); + }); + var application_awaiter = function (thisArg, _arguments, P, generator) { +diff --git a/build/renderer.js b/build/renderer.js +index fb02453..4328ccd 100644 +--- a/build/renderer.js ++++ b/build/renderer.js +@@ -354,7 +354,7 @@ + offerId: user.OFFER_ID, + country: user.COUNTRY, + gatekeeps: user.__DZR_GATEKEEPS__, +- }); ++ }, event.data); + break; + } + case "player-repeat-changed": +@@ -363,7 +363,7 @@ + canPrev: event.data.player.hasPrev, + canNext: event.data.player.hasNext, + canRepeat: event.data.player.hasRepeat, +- }); ++ }, event.data); + break; + case "player-shuffle-changed": + renderer_ipc.send("channel-player-shuffle-update", { +@@ -371,14 +371,14 @@ + canPrev: event.data.player.hasPrev, + canNext: event.data.player.hasNext, + canShuffle: event.data.player.hasShuffle, +- }); ++ }, event.data); + break; + case "player-playing-changed": { + const state = + !0 === event.data.isPlaying + ? MediaPlayerState.Playing + : MediaPlayerState.Paused; +- renderer_ipc.send("channel-player-state-update", state); ++ renderer_ipc.send("channel-player-state-update", state, event.data); + break; + } + case "player-track-updated": { +@@ -414,7 +414,7 @@ + canRepeat: event.data.player.hasRepeat, + canShuffle: event.data.player.hasShuffle, + }; +- renderer_ipc.send("channel-player-track-update", track, player); ++ renderer_ipc.send("channel-player-track-update", track, player, event.data); + break; + } + } +diff --git a/package.json b/package.json +index 6c78c7d..cee7ce4 100644 +--- a/package.json ++++ b/package.json +@@ -14,7 +14,7 @@ + "dependencies": { + "@electron/remote": "2.1.2", + "@jellybrick/mpris-service": "2.1.5", +- "rich-presence-builder": "0.1.1", ++ "@josselinonduty/rich-presence-builder": "0.1.2", + "electron-log": "^5.1.2", + "electron-settings": "4.0.4", + "electron-updater": "^6.1.8", +-- +2.43.0 +