feat(discord): publish the playing title, episode and position to Discord - #655
Closed
Zaphkiel-Ivanovna wants to merge 1 commit into
Closed
feat(discord): publish the playing title, episode and position to Discord#655Zaphkiel-Ivanovna wants to merge 1 commit into
Zaphkiel-Ivanovna wants to merge 1 commit into
Conversation
…cord Show the current series and episode, or film and year, on the user's Discord profile along with the playback position. The new jfn-discord crate splits the way jfn-mpris does: a pure projection module holding the display rules, unit tested without a socket or a clock, and a dedicated thread owning the connection. It registers with jfn_playback::register_event_sink rather than the per-platform MediaSink trait, which returns one sink per platform and is already taken by MPRIS, NowPlaying and SMTC. The RPC protocol is a length-prefixed JSON frame over a Unix socket or a named pipe, small enough to implement here instead of taking a dependency. Reads run on their own thread: Discord echoes every command and sends PING expecting PONG, so an undrained socket eventually blocks writes, and keeping reads off the sink thread means stop() never waits on I/O. Reconnect backoff is loop state rather than a sleep for the same reason. Timestamps are absolute, so Discord advances the progress bar itself and nothing needs pushing on a timer; an update goes out only when the relation between position and wall clock changes. The bar is omitted unless playback is steadily rolling, which is the only way to stop the client extrapolating through pauses, seeks and buffer underruns. MediaMetadata gains the season number, production year, item kind and primary image URL, all four of which the CEF layer already received and dropped, plus the provider ids behind the IMDb, AniList and TMDb links. Those ids come from the server and end up inside a URL, so they are validated first. Input::Metadata is boxed to keep the enum balanced now the struct is larger. The Flatpak manifest gains access to the host Discord socket, invisible from inside the sandbox, without which the feature would be inert on that target alone. Off by default behind discordRichPresence, and inert until an application id is set, either in the client settings page or as discordApplicationId in settings.json, falling back to a compiled-in constant. A non-numeric id is refused with a log line rather than left to fail silently at connect time, since the field is now free text in the UI.
Owner
|
This isn't something I want to add into the codebase proper due to reliance upon a third party API. Please open an issue for Discord support and I'll mark it as blocked by #371 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Discord Rich Presence: series and episode, or film and year, plus the playback position. New
jfn-discordcrate, no new external dependency, off by default.Action required from the maintainer
Inert until a Discord application exists.
DEFAULT_APPLICATION_IDis left empty so nothing ships pointing at a personal account.Jellium Desktop.resources/discord/logo.pngandpause.pngunder Rich Presence → Art Assets. Asset names must belogoandpause; Discord takes them from the filenames, so upload as-is.DEFAULT_APPLICATION_ID(src/discord/src/sink.rs:14).The ID is public in every presence payload, not a secret. Merging before step 3 is harmless.
Implementation
jfn_playback::register_event_sink, notMediaSink: Discord is cross-platform, that trait gives one sink per platform and is already taken.jfn-mpris: pureprojectionmodule for the rules, dedicated thread for the socket.PONG, so an undrained socket blocks writes, andstop()must not wait on I/O. Backoff is loop state, not a sleep.MediaMetadatagains seven fieldsparse_metadata_jsonalready received and dropped.Input::Metadataboxed forclippy::large_enum_variant.--filesystem=xdg-run/discord-ipc-0; the socket is invisible from the sandbox otherwise.Settings
New Discord section in Client Settings, mirrored in
settings.json. Read at startup, so toggling needs a restart.discordRichPresencefalsediscordApplicationIdYour call
The large image is the Jellyfin poster URL. Discord's proxy embeds the origin host, so the server hostname becomes visible in the payload, and it only resolves for a publicly reachable server. One branch in
projection.rsmakes it logo-only.Testing
jfn-discord, 272 workspace-wide,just lintclean.