Room Background Media

Music or radio that the server plays under the voices in a room, so everyone hears the same thing at the same time.

Overview

  • A room can have background media: songs, albums or playlists from the server's music library, or a radio stream.
  • The server plays it and mixes it into the room's live audio, so guests and people on the web, iPhone, iPad, Mac and Windows all hear the same song at the same moment.
  • It sits well below the voices, and it gets quieter by itself while anyone is talking (about 15 dB), then comes back up when they stop.
  • It only plays while someone is listening in the room. When the room is empty it stops, and it carries on when someone comes back.
  • You can turn it down or mute it just for yourself. That does not change what anyone else hears.

Listening: your own volume and mute

Web app

  1. Join the room. When it has background media, a "Background media" section appears in the room, below the voice controls. It says what is playing, for example "Now playing: Song title by Artist".
  2. Use the "Background media volume" slider to set how loud it is for you (0 to 100 percent). Voices are not affected.
  3. Press "Mute background media" to silence it for you. It is a toggle button; press it again to hear it again. Your choice is remembered in this browser.

When the song changes, screen readers hear a short polite announcement such as "Now playing: Song title by Artist". It is not repeated for pauses or small changes.

iPhone and iPad (build 114 and later)

  1. Join the room. When it has background media, a "Background Media" section appears in the room, below Intercom and Broadcast. Its first row, "Background media", says what is playing, for example "Now playing: Song title by Artist", and whether it is playing, paused or waiting.
  2. Swipe up or down on the "Background media volume" slider to set how loud it is for you (0 to 100 percent, in steps of 5; it starts at 60 percent). Voices are not affected.
  3. Double tap "Mute background media" to silence it for you. Double tap again to hear it again.
  4. The same slider and switch are in Settings, Audio, under "Background Media". Your choice is remembered on this device.

When the song changes, VoiceOver says "Now playing" and the song, after anything it is already reading. To turn this off, go to Settings, Accessibility, Speech, and switch off "Background Media Song Changes". The music never shows up as a person in People, and it gets quieter while you whisper to someone.

Mac (build 114 and later)

  1. Join the room. When it has background media, a "Background media" panel appears in the room, below the whisper status and above the Members List. It says what is playing and whether it is playing, paused or waiting.
  2. Use the "Background media volume" slider (VoiceOver: interact, then Left or Right Arrow) and the "Mute background media" checkbox. They are also in Settings, Audio, under "Background Media".
  3. Open the Background Media window with Command-Option-B, from the Room menu ("Background Media..."), from the room's Room menu button, or with the "Background Media..." button in the panel. Press Escape to close it.

VoiceOver on the Mac announces "Now playing" and the song when it changes (it follows your Room events announcement setting). The music is not listed in the Members List.

Windows

The Windows app already plays background media, because it arrives on the same live audio as the voices. Separate background volume and mute controls are coming in its next update. Until then, the room volume controls apply to it.

Controlling background media

Server admins and the room's owner can control a room's background media. Room owners can also give other people this right in the room's Jellyfin access settings. In the web app these controls are in the "Background media" section:

  • Previous, Play or Pause, Next, Stop buttons. Previous goes back to the start of the song if it has played for more than 5 seconds, otherwise to the song before.
  • Background media settings (a section you expand): Shuffle, Repeat (Off, Whole queue, Current song), Level for everyone (Very quiet to Very loud; normal is 18 dB below voices), and "Let signed-in listeners request songs".
  • Music library: choose a source, then move through the list. Libraries, albums, artists and playlists have an "Open" button; songs, albums and playlists have "Play now" and "Add to queue". Use the "Search music" box to find songs, albums, artists or playlists. "Back" goes up one level. Choose "Radio streams" as the source to play one of the server's radio streams.
  • Queue: a numbered list of what will play. The current song is marked "now playing". Each entry has a "Remove" button.

On iPhone and iPad

Open "Background Media" from Room Actions, or double tap "Background Media Controls" in the room's Background Media section. Room owners can also open it without joining: Settings, My Rooms, choose the room, then "Background Media". The screen has:

  • Now Playing and Your Volume.
  • Controls: Previous, Play or Pause, Next and Stop.
  • Background Media Settings: Shuffle, Repeat (Off, Whole queue, Current song) and "Let signed-in listeners request songs".
  • Queue: numbered entries, the current one says "now playing". Each has a Remove button, also available as a VoiceOver action (swipe up or down).
  • Music Library: choose a source, then open libraries, artists, albums and playlists one level at a time; the Back button goes up a level. Type in "Search music" and press Search. Double tap a song to choose "Play now" or "Add to queue", or swipe up or down on any row for those actions. "Radio Streams" play a server stream now.

Listeners who may request songs see "Request a Song" instead of Music Library, and a "Request" action on songs.

On the Mac

The Background Media window (Command-Option-B) has the same parts as on iPhone: Now Playing, Your Volume, Controls (Command-Option-Space plays or pauses while the window is open), Background Media Settings, Queue with Remove buttons, and the Music Library. In the library, choose a source from the "Source" menu (or "Radio streams"), then use each row's "Open", "Play now", "Add to queue" or "Request" button. "Back" (Command-Left Bracket) goes up one level or clears a search.

When listener requests are on, signed-in listeners see the music library with a "Request" button on songs. Each person can have up to 5 requests waiting. Guests can listen but cannot control or request.

A room can also have a simple schedule (start and stop times, optionally only on some days). Outside those hours the media stays off.

Jukebox categories: music that matches the room

Every room can have a jukebox category, such as Lounge, Chill, Late night, Gaming, Quiet study or Talk (no music). When category music is on, the server picks the songs for you from the music library: songs that match the category's genres, tags and playlists come first, and if fewer than 20 songs match, other songs from the music libraries fill the rest. When the list runs out, the server picks a fresh one. Nothing plays until someone turns category music on for the room.

New rooms get a category from their name. For example, a room called "Music Lounge" gets Lounge, "Chill Zone" gets Chill, "Late Night" gets Late night, "Gaming Voice" gets Gaming, "Study Hall" gets Quiet study, and podcast, meeting, class and training rooms get Talk (no music). Rooms whose name doesn't suggest anything get no category. You can change it at any time.

The Jukebox screen (web app)

In the room's "Background media" section, choose the "Jukebox settings" button. It is there for the room's owner and managers and for server admins. The screen opens with focus on its "Jukebox" heading; Escape or the Close button closes it and returns you to the button.

  • This room: "Category for this room", the "Play music from this category" check box, "Music level for everyone", and a Schedule group ("Only play during set hours", Start time, Stop time and Days; no days checked means every day). Choose "Save room settings" to apply. "Rebuild queue from category" picks a fresh list of songs straight away.
  • Choosing songs yourself: use the Music library in the Background media section. In category mode, songs you add play next, then the category music carries on. "Play now" on an album or playlist replaces the category music until you turn it back on in the Jukebox screen.
  • Categories (server admins only): a list of every category with what it plays and which rooms use it, each with "Edit" and "Delete" buttons, and a "Create category" button. The editor has Name, Description, a "No music" check box, the music server, and check box groups for Genres, Tags, Libraries and Playlists read from the music server. No libraries checked means all music libraries (never audiobooks). Deleting a category asks you to confirm; rooms that used it are left with no category, and category music in them stops.

When a category is edited, or a room's category changes while its music is playing, the new choice takes over at the next song.

Limits

  • To keep the server fast, only a few rooms can play background media at the same time (3 by default). If the limit is reached, the room shows "Waiting: server busy" and starts by itself when a slot frees up.
  • When the server is very busy, background media waits or pauses and resumes automatically.
  • If a stream or song can't be reached, the server retries with growing pauses and skips a song that keeps failing.
  • The music library needs the server's Jellyfin connection to be set up by the server owner. All of our servers share one central Jellyfin music server and account, so the same media is available everywhere - each server just picks which of its libraries to use (Admin > Jukebox > Categories, music server libraries/genres/tags/playlists checkboxes). The library key never leaves the server; links handed to the app for playback or cover art are short-lived and only work through VoiceLink.
  • A server owner can hide a whole Jellyfin library from VoiceLink, for example one that only feeds a game world. Add its id to excludedLibraryIds (or its name to excludedLibraryNames, any letter case) on that Jellyfin server's entry in data/deploy.json (jellyfin.connection.servers) and restart VoiceLink. A hidden library and everything in it, including its playlists, is never listed, opened, searched, picked by a jukebox category, queued or played, for anyone, server admins included. Tracks from it that were already queued are removed when their turn comes. Jellyfin itself is not changed.

For app developers: protocol

All paths are relative to the server base URL, including any path prefix. Send Authorization: Bearer <accessToken>. Permissions come from the signed-in session only.

Audio

Background media arrives as ordinary relayed-audio packets to every listener in the room that has the relay on:

{ userId: "media:<roomId>", userName: "Background media", roomId, media: true,
  audioData: base64 float32 little-endian, codec: "pcm-f32", sampleRate: 48000, channels: 1,
  frameSize: 960 (20 ms), timestamp: seconds (number), seq, engine: "room-media-source",
  audioMode: "music", relayTelemetry: { jitterBufferMs: 120, serverReceivedAt } }
  • About 50 packets per second. The server has already applied the room level and voice ducking.
  • Personal volume and mute: apply your own gain to packets with media: true (or drop them when muted). Do not show media: senders as people and do not announce them joining or leaving.
  • When a room uses the server mix, room payloads have backgroundStream: null; do not start your own stream player for that room.

State

Socket event room-media-state is sent to the room on every change, and to you right after joined-room. joined-room also has room.backgroundMedia (same object, or null).

{ roomId, mode: "server-mix", enabled, status: "playing"|"paused"|"stopped"|"waiting"|"busy"|"retrying"|"ended"|"scheduled"|"error"|"loading",
  statusReason, playing, sourceType: "category"|"jellyfin"|"stream"|"url"|"none", sourceTitle,
  category: { id, name, noMusic } | null (the room's jukebox category), categoryMode: true when sourceType is "category",
  nowPlaying: { index, kind, itemId, title, artist, album, durationSec, positionSec, live, requestedBy, streamTitle?, stationName? },
  nowPlayingText: "Now playing: Title by Artist", announce: true only when the song changed,
  currentIndex, queue: [...up to 50 entries...], queueStart, queueLength, shuffle, repeat: "off"|"all"|"one",
  volumeDb, duckDb, allowUserRequests, schedule, startedBy, startedAt,
  member: { userId: "media:<roomId>", userName }, audio: { codec, sampleRate, channels, frameMs } }

REST

  • GET /api/rooms/:id/background-media - {state (full queue), permissions: {canControl, canConfigure, canRequest, canBrowse, canUseCustomUrl, signedIn, isServerAdmin}}. Anyone.
  • PUT /api/rooms/:id/background-media - control permission. Body: {sourceType: "jellyfin", serverId, itemIds: [...] | playlistId}, {sourceType: "stream", streamId?} (configured streams; without streamId the room's own), {sourceType: "url", url} (server admins only), {sourceType: "none"}; plus any of title, volumeDb (-40..0), duckDb (-40..0), shuffle, repeat, allowUserRequests, enabled, autoplay, schedule: {enabled, startTime: "HH:MM", stopTime: "HH:MM", days: [0-6], timezone}.
  • POST /api/rooms/:id/background-media/play | pause | next | previous | stop - control permission.
  • POST /api/rooms/:id/background-media/queue {serverId, itemId | itemIds, playNext?} - controllers add anything; listeners (when requests are on) one song at a time, 5 waiting max.
  • DELETE /api/rooms/:id/background-media/queue/:index - control permission.
  • GET /api/rooms/:id/background-media/library - without serverId: {servers: [{id, name, available}], streams}; with serverId: libraries; with parentId and/or search: {items: [{id, type, title, artist, album, durationSec, childCount, isFolder, playable}], total}.
  • PUT /api/rooms/:id/background-media also takes {sourceType: "category", categoryId?} (without categoryId the room's own category).
  • Every response has success; errors have error text to show. 401 = sign in, 403 = no permission, 503 with code: "no_api_key" = library not set up.

Jukebox categories API

Category object: {id, name, description, noMusic, sources: {serverId, genres: [], tags: [], libraryIds: [], playlistIds: []}, createdAt, updatedAt}. The room's category is room.jukeboxCategory (id or null).

  • GET /api/jukebox/categories - signed-in users. {categories: [category + rooms: [{id, name, categoryMode, playing}]], permissions: {canEditCategories}}. Private rooms are listed only for server admins.
  • POST /api/jukebox/categories (201), PUT /api/jukebox/categories/:id, DELETE /api/jukebox/categories/:id - server admins only. Body: any category fields; omitted fields keep their values on PUT. Responses include category and the new categories list; DELETE returns {deleted, roomsCleared}: rooms that used it get no category, and category-mode rooms stop (source becomes "none").
  • GET /api/jukebox/genres?serverId=&roomId= - server admins, or managers of roomId. {servers, serverId, libraries: [{id, name, collectionType, music}], genres: [], tags: [], playlists: [{id, name, childCount}]}. Genres and tags come from the music libraries.
  • GET /api/rooms/:id/jukebox - anyone. {jukebox: {roomId, categoryId, category, categoryMode, enabled, backgroundMediaEnabled, sourceType, volumeDb, schedule, queueLength}, permissions: {canConfigure, canEditCategories, signedIn}}.
  • PUT /api/rooms/:id/jukebox - room managers and server admins. Body: any of {categoryId (id or null), enabled (true = category music on, false = off), volumeDb, duckDb, schedule, allowUserRequests, rebuild}. Returns jukebox plus state and permissions as in GET background-media.
  • POST /api/rooms/:id/jukebox/rebuild - room managers and server admins. Turns category music on and picks a fresh queue now.
  • 400 when the category plays no music, 404 when the category doesn't exist, 503 when no music server is set up.