Skip to content

Releases: TIGamingTV/JellyWatchParty

v4.0.0.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 10 Oct 15:54
9f2644b

✨ Highlights

Discord bot. People on third-party Jellyfin clients (Android TV, Fladder, Swiftfin, …) can now run and join watch parties from Discord, without an admin placing their devices. Admins link a Discord user to a Jellyfin user with a one-time code, and the linked user controls their own devices from a live room panel in the channel.

🆕 What's new

Discord bot

  • New optional sidecar (src/integrations/discord-bot) that relays Discord interactions to the session server. It holds no state and decides nothing itself.
  • /jwp slash commands with private replies, plus one live panel message per room: join, add or remove my device, leave, pick host, close.
  • Passwords and link codes are only typed into modals. User-supplied names are escaped, and the bot never pings anyone.
  • Runs from the new discord compose profile, with its own image on GHCR (ghcr.io/<owner>/<bot-image>).
  • Built on twilight instead of serenity, because serenity 0.12 pulls in a rustls-webpki with open advisories (RUSTSEC-2026-0049/0098/0099/0104).

Linking chat accounts to Jellyfin users

  • In the admin panel, an admin assigns a 4-digit code to a Jellyfin user. The user enters their Jellyfin name and the code in chat to link.
  • Codes are stored as an HMAC under a per-install key (secret.key in DATA_DIR). They are never logged and shown only once.
  • Attempt limits (fixed windows that start at the first wrong code):
Limit Effect
10 wrong tries on one code, from anyone That user's code locks
5 wrong tries on one account Account waits 15 min
50 wrong tries across all users in 10 min Linking pauses for everyone

Rooms for linked chat users

  • Rooms created through the bot belong to the Jellyfin user who made them.
  • Participants join with the room password (same throttle as the Watch Party panel, per user per room) and can only add or remove their own Jellyfin devices.
  • The owner or an admin can pick the host, remove people, change the name and password, hand the room over, and close it. The owner cannot leave.
  • Device ownership is checked against the same fresh /Sessions result the device is bridged from, so a session ID can't be used to drive someone else's TV.
  • A chat room stays open (hostless) when its last device leaves, and closes after the platform's idle timeout.

Admin panel additions

  • New bot settings section, a users and link codes table, and an activity log.
  • Sidecars talk to the server over a token-protected integration API (config, heartbeat, link, unlink, me) on its own port: <port>.

New and changed settings

Variable / setting What it does
<DISCORD_TOKEN> Bot token for the discord profile <confirm name>
<INTEGRATION_PORT> Port of the integration API <confirm name and default>
JWP_TAG Image tag the prod compose file pulls (dev, beta, X.Y.Z, X.Y, latest)
DATA_DIR Now backed by the jwp-data volume, holds integration data and secret.key
ADMIN_HOST Now passed through by both compose files
ADMIN_PASSWORD_FILE Now passed through by both compose files
JELLYFIN_API_KEY_FILE Now passed through by both compose files

The bot settings in the admin panel have defaults and ranges, listed in the Discord bot docs.

🐳 Docker and CI

  • The Discord bot image is now published to GHCR next to the session server image. Both get the same tags: dev (from develop), beta (from main), and X.Y.Z, X.Y and latest on release. Tags have no v prefix.
  • The prod compose file now pulls the published images instead of building them. Choose the tag with JWP_TAG.
  • Security checks now run on develop, and the bot image is scanned.
  • A failed bot build does not hold back the release assets.

🔒 Security fixes

  • Integration data file permissions. A leftover integrations.json.tmp kept its own permissions when reopened, so the data file could end up 0644. It is now always written as a fresh 0600 file.
  • Integration tokens are kept out of debug output.
  • Redirects. The bot no longer follows redirects from the integration API.

🐛 Bug fixes

  • A web host who became host of a Discord room could close it for everyone by starting a new room. Only the owner or an admin can close a chat room now, and the host just leaves it.
  • Escaped member names could push a panel field past 1024 characters, after which every panel edit was refused. Long room lists and whoami replies over 2000 characters left the user on "thinking...".
  • The bot missed settings saved just before a server restart, because the settings version restarts at 1 with the server. It now reloads them.
  • Panel edits lost to network or Discord errors are now retried.

📚 Documentation

  • README and installation quick start now cover the admin panel and the optional Discord bot.
  • New Discord Bot troubleshooting section, setting defaults and ranges, and backup guidance for secret.key.
  • Corrected the lockout and throttle descriptions, the host promotion notes and the integration token notes.
  • Documented how to test develop builds with JWP_TAG=dev.

🔄 Upgrading

  1. Update the session server. The plugin is unchanged in this release <confirm>.
  2. Production compose users: the compose file now pulls images from GHCR. Set JWP_TAG if you don't want latest.
  3. To use the bot, start it with the discord compose profile and set <DISCORD_TOKEN> <confirm>.
  4. In the admin panel, open the bot settings, then assign link codes to your users.
  5. Make sure the jwp-data volume is persisted and back up secret.key. Without it, issued link codes can no longer be verified <confirm>.

What's Changed

  • feat(server): link chat accounts to Jellyfin users with admin-issued codes in #92
  • feat(server): let linked chat users run rooms with their own devices in #93
  • feat: add a Discord bot for third-party-client watch parties in #94
  • ci: publish the Discord bot image to GHCR in #95
  • fix: pre-release hardening in #96
  • docs: fix Discord bot docs and add troubleshooting in #97

Full Changelog: v3.0.0.0...v4.0.0.0

v3.0.0.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 04 Oct 14:47
54c7b77

✨ Highlights

Admin panel. The session server now has a web UI and JSON API for managing every room, including rooms users created. It is also the new way to put non-browser Jellyfin clients (Android TV, Fladder, Swiftfin, …) into a watch party.

⚠️ Breaking change: panel bridging is now off by default

Plugin 3.0.0.0 adds a new master switch, EnablePanelBridging, which is off by default, including on upgraded installs. If you used the Host or Receiver bridges, they stop working after the update until an admin ticks the new switch on the plugin config page. The per-role flags do nothing until then.

For most servers, use the admin panel's device support instead (see below).

🆕 What's new

Admin panel (#85)

  • Second listener on ADMIN_PORT (default 3001) with a web UI and JSON API.
  • See every room with its members, host, sync status and connection, plus connected clients that are not in a room.
  • Create groups: named rooms with an optional password (with a built-in generator) that users join from the Watch Party panel.
  • Add or move signed-in clients into any room without its password, make another member host, remove members, rename rooms, set or clear passwords, and close rooms.
  • Empty admin groups are removed after ADMIN_EMPTY_GROUP_TTL_SECS (default 10 minutes).
  • Clients moved or removed by an admin get a toast in the web UI.
  • Opt-out config: the panel starts only when ADMIN_PASSWORD or ADMIN_PASSWORD_FILE is set. Without it, the server logs a warning and runs normally.
Variable Default
ADMIN_ENABLED true
ADMIN_HOST 0.0.0.0
ADMIN_PORT 3001
ADMIN_USERNAME admin
ADMIN_PASSWORD / _FILE required
ADMIN_SESSION_TTL_SECS 43200
ADMIN_COOKIE_SECURE false
ADMIN_TRUST_X_FORWARDED_FOR false
ADMIN_EMPTY_GROUP_TTL_SECS 600

Both compose files, .env.example, the Dockerfile EXPOSE and the Windows README are updated.

Panel security: constant-time password check, HttpOnly; SameSite=Strict session cookie, login throttling (5 per IP and 30 overall per minute), required x-jwp-admin header and matching Origin on every change, strict CSP, X-Frame-Options: DENY, no-store, and an admin: log line for every action.

Jellyfin device control (#86)

Set JELLYFIN_URL and JELLYFIN_API_KEY and the admin panel lists active Jellyfin clients that can't show the Watch Party panel. Add any of them to any room as host or receiver. The session server drives them over the Jellyfin REST API, so per-user plugin bridges are no longer needed.

  • Receivers start on the room's item, follow pause, play, seek and media changes, and stay within 2 s of the room.
  • Hosts report play, pause, seeks and item switches to the room.
  • Positions are extrapolated between progress reports to avoid repeated seeks.
  • A device missing for 90 s leaves its room. When a host leaves, a person is promoted before any device.
Variable Default
JELLYFIN_URL none (as reachable from the session server)
JELLYFIN_API_KEY / _FILE none (Dashboard → API Keys)
BRIDGE_POLL_INTERVAL_MS 1000 (minimum 250)

Plugin 3.0.0.0 (#87)

  • New EnablePanelBridging master switch (see above). Turning it or a role off now stops the matching running bridges immediately.
  • The plugin config section is now "Watch Party Panel Bridging (trusted servers only)". It explains the change and points to the admin panel.
  • Plugin bridges now send bridge_device_id. The admin panel labels these connections Plugin bridge, and a device can't be bridged twice.

🔒 Security fixes

  • Session takeover via client_id (#85). Reconnecting with a known client id took over that session, including its room and host role. Each client now gets a resume secret that is sent only to its owner and required to reattach. Clients from before this change still connect, but lose their room on reconnect while the old entry is held.
  • Missing ownership checks in the plugin (#87). Any user could bridge or stop anyone's session and attach any session to any room, bypassing Jellyfin's EnableRemoteControlOfOtherUsers. Bridge/Sessions and Bridge/Status now list only your own sessions, and Start, Follow and Stop return 403 for someone else's. Jellyfin administrators can still bridge any session.

🐛 Bug fixes

  • Fixed a possible deadlock: leave and disconnect took locks in the opposite order from every other handler (#85).
  • Fixed stale membership: join_room and create_room left a client listed in the room it was already in (#85).

📚 Documentation

Updated configuration, host-bridge, features, user-guide, troubleshooting, plugin, ARCHITECTURE and protocol docs. Also corrected plugin.md: the JWT secret is sent back to the config page through the admin-only plugin configuration API.

🔄 Upgrading

  1. Update the session server and plugin together.
  2. Set ADMIN_PASSWORD (or _FILE) to enable the admin panel, and open port 3001.
  3. To control TV apps from the panel, set JELLYFIN_URL and JELLYFIN_API_KEY.
  4. If you still want the in-player Host and Receiver bridges, tick Enable panel bridging on the plugin config page.

What's Changed

  • feat(server): put Jellyfin devices into rooms from the admin panel by @TIGamingTV in #86
  • feat(plugin): make panel bridging opt-in and limit it to own sessions by @TIGamingTV in #87
  • fix(plugin): never drive one device from two places, and leave rooms cleanly by @TIGamingTV in #89
  • Feat/admin jellyfin bridge by @TIGamingTV in #90
  • Feat/admin panel core by @TIGamingTV in #91

Full Changelog: v2.0.8.0...v3.0.0.0

v2.0.8.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 25 Sep 15:33
914f385

What's Changed

  • feat(protocol,web,server): room media follows the host (set_media) by @TIGamingTV in #73
  • fix(web): stale item id from the hidden player OSD, and guest ready after media_changed (#71 follow-up) by @francotosqui in #77
  • feat(web): close the panel from an X button, Escape, or a click outside by @francotosqui in #78
  • feat: show each participant's name and playback status by @francotosqui in #79
  • fix(server): stop logging room passwords and throttle failed joins by @TIGamingTV in #83
  • feat: start the room's first play together after a countdown by @francotosqui in #80
  • Develop by @TIGamingTV in #84

New Contributors

Full Changelog: v2.0.7.0...v2.0.8.0

v2.0.7.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 24 Sep 09:09
b259155

What's Changed

  • fix(web): resolve item id and start playback without global playbackManager by @TIGamingTV in #68
  • fix(web): send Authorization header from apiFetch, not just X-Emby-Token by @TIGamingTV in #72
  • fix(web): send Authorization header from apiFetch, not just X-Emby-Token by @TIGamingTV in #74
  • fix(server): correct set_media tests for the room_list broadcast to host by @TIGamingTV in #75

Full Changelog: v2.0.6.0...v2.0.7.0

v2.0.6.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 24 Sep 08:56

Full Changelog: v2.0.5.0...v2.0.6.0

v2.0.5.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 23 Sep 21:59
71de9a8

What's Changed

  • Update develop plugin manifest (build 127) by @TIGamingTV in #69
  • Merge remote-tracking branch 'origin/develop' into fix/jf-12.1-playback-manager by @TIGamingTV in #70

Full Changelog: v2.0.4.0...v2.0.5.0

v2.0.4.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 09 Sep 23:31
f22d46e

What's Changed

  • refactor: migrate session-server from warp to axum, resolving RUSTSEC-2026-0258 at the source by @TIGamingTV in #66

Full Changelog: v2.0.3.0...v2.0.4.0

v2.0.3.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 09 Sep 18:22
0b6de76

What's Changed

  • fix: ignore RUSTSEC-2026-0258 in cargo-audit config by @TIGamingTV in #64
  • fix: inject the Jellyfin 12 header button into the MUI toolbar instead of floating it by @TIGamingTV in #65

Full Changelog: v2.0.2.0...v2.0.3.0

v2.0.2.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 09 Sep 12:24
6cc7cbe

What's Changed

  • fix: v12 floating button overlaps other plugins; replace SyncPlay when configured by @TIGamingTV in #63

Full Changelog: v2.0.1.0...v2.0.2.0

V2.0.1.0

Choose a tag to compare

@TIGamingTV TIGamingTV released this 09 Sep 07:20
9e0bed5

What's Changed

  • Drop Jellyfin 10.11.x support, target Jellyfin 12.x only by @TIGamingTV in #61
  • fix: Jellyfin 12's default layout hides the header button by @TIGamingTV in #62

Full Changelog: v2.0.0.0...v2.0.1.0