Skip to content

Configuration examples

Complete data/config.yaml files, not fragments. Copy one whole, then adjust. Anything a block does not mention keeps its default.

All of them assume the matching credentials are already in .env. None of these files contain a secret.

Library names must match the server exactly

Every key under sections is looked up on the media server verbatim, including capitalisation. TV shows does not match a library called TV Shows, and a name that matches nothing is skipped without an error.

Minimal: Plex, show every library

The shortest useful configuration. No sections map, so every library the server reports appears in the dashboard under its own name. The bot's status lists no libraries until you add presence.libraries.

# Plex, everything the server reports, nothing customised.
media_server:
  type: "plex"

dashboard:
  name: "Media Server Dashboard"
  icon_url: ""
  footer_icon_url: ""

plex:
  # No sections listed, so this shows whatever Plex reports, in server order.
  # At most 15 embed fields fit; beyond that the dashboard says how many it dropped.
  show_all: true
  sections: {}

Minimal: Jellyfin, show every library

Identical apart from the two keys that select the platform. PLEX_* may stay empty.

# Jellyfin, everything the server reports, nothing customised.
media_server:
  type: "jellyfin"

dashboard:
  name: "Media Server Dashboard"
  icon_url: ""
  footer_icon_url: ""

jellyfin:
  # Emoji and episode counts are derived from each library's type when not listed:
  # TV libraries get 📺 and an episode count, movie libraries 🎥 and none.
  show_all: true
  sections: {}

Selected libraries with their own names and emojis

Only what is listed appears, in exactly this order. Everything else on the server is hidden from the dashboard.

media_server:
  type: "plex"

dashboard:
  name: "Home Media"
  icon_url: "https://mediawatch.nichtlegacy.com/logo.png"
  footer_icon_url: "https://mediawatch.nichtlegacy.com/logo.png"

plex:
  # false = the listed libraries and nothing else. A name that does not exist
  # on the server is skipped silently, so check spelling if one is missing.
  show_all: false
  sections:
    Movies:
      display_name: "Movies"
      emoji: "🎥"
      show_episodes: false
    "TV Shows":
      display_name: "Series"
      emoji: "📺"
      # Adds a second embed field with the episode count, so this library
      # costs two of the 15 available field slots.
      show_episodes: true
    Documentaries:
      display_name: "Docs"
      emoji: "📚"
      show_episodes: false

presence:
  # Shown in the bot's status while nothing is playing. Names only: display
  # name and emoji come from the sections above. ":episodes" counts episodes.
  libraries: ["Movies", "TV Shows:episodes"]
  offline_text: "🔴 Server Offline!"
  stream_text: "{count} active Stream{s} 🟢"
media_server:
  type: "jellyfin"

dashboard:
  name: "Home Media"
  icon_url: "https://mediawatch.nichtlegacy.com/logo.png"
  footer_icon_url: "https://mediawatch.nichtlegacy.com/logo.png"

jellyfin:
  show_all: false
  sections:
    Movies:
      display_name: "Movies"
      emoji: "🎥"
      show_episodes: false
    "TV Shows":
      display_name: "Series"
      emoji: "📺"
      show_episodes: true
    Music:
      display_name: "Music"
      emoji: "🎵"
      show_episodes: false

presence:
  # Shown in the bot's status while nothing is playing. Names only: display
  # name and emoji come from the sections above. ":episodes" counts episodes.
  libraries: ["Movies", "TV Shows:episodes"]
  offline_text: "🔴 Server Offline!"
  stream_text: "{count} active Stream{s} 🟢"

Privacy-conscious

For a dashboard in a channel other people can read. All three restrictions default to false, so this is an opt-in.

media_server:
  type: "plex"

dashboard:
  name: "Media Server Dashboard"
  icon_url: ""
  footer_icon_url: ""

plex:
  show_all: true
  sections: {}

stream_details:
  # Nobody sees client IPs. Even with this on, only DISCORD_AUTHORIZED_USERS would.
  show_ip_for_authorized_users: false
  # Only DISCORD_AUTHORIZED_USERS may open the "Stream N - User" buttons.
  # Everyone else gets an ephemeral rejection instead of the details.
  restrict_to_authorized: true

global_stats:
  # Separate switch on purpose: this one guards server-wide watch history,
  # including a top-users-by-watch-time leaderboard, not just current playback.
  restrict_to_authorized: true
  button_location: "stream_details"
  page_2:
    time_range: 30

user_stats:
  # One user's whole watch history. Closed separately, so the live stream details
  # above could stay open while this stays locked.
  restrict_to_authorized: true

This does not hide the dashboard itself

The embed is a normal message: the server state, library totals and who is currently watching what stay visible to everyone who can read the channel. Only channel permissions change that. See Privacy and visibility.

With Tautulli statistics

Plex plus Tautulli, with TAUTULLI_URL and TAUTULLI_API_KEY set in .env. Without those two the statistics buttons do not exist, and everything here except cache.library_update_interval has no effect.

media_server:
  type: "plex"

dashboard:
  name: "Media Server Dashboard"
  icon_url: ""
  footer_icon_url: ""

plex:
  show_all: true
  sections: {}

cache:
  library_update_interval: 900
  # TTL for Tautulli user history and player lookups. Raise it if Tautulli
  # is slow; 0 turns the cache off and every button press hits the API.
  user_stats_ttl: 60

global_stats:
  # "both" puts the Global Stats button on the dashboard as well, where every
  # channel member sees it - pair it with restrict_to_authorized if that matters.
  button_location: "both"
  restrict_to_authorized: false
  page_2:
    # Days covered by peak hours, active users, most active day and the
    # top-users leaderboard. A positive integer, or "all" for all time.
    time_range: 90

user_stats:
  enabled: true
  pages:
    behavior: true
    devices: true
    top_content: true
  # 0 = all time.
  time_range: 0
  # Shows on page 3, 0-10. 0 hides that section entirely.
  top_tv_count: 10

With SABnzbd

Needs SABNZBD_URL and SABNZBD_API_KEY in .env; without both, the download block never appears no matter what is configured here.

media_server:
  type: "plex"

dashboard:
  name: "Media Server Dashboard"
  icon_url: ""
  footer_icon_url: ""

plex:
  show_all: true
  sections: {}

sabnzbd:
  # false hides the block while the queue is empty, instead of showing a
  # permanent "no active downloads" row on a mostly idle server.
  show_when_empty: false
  show_status_icons: true
  # Which SABnzbd value is reported: diskspace1 is the download directory,
  # diskspace2 the completed directory. A key your SABnzbd does not return
  # is logged as a warning at startup, and the dashboard shows "Unknown" for it.
  diskspace_free_key: "diskspace1"
  diskspace_total_key: "diskspacetotal1"
  # Download names are cut where the earliest of these appears, to keep release
  # tags out of the embed. A name that starts with a keyword is left whole.
  # Writing this list replaces the built-in one completely.
  keywords:
    - "German"
    - "GERMAN"
    - "DL"
    - "1080p"
    - "2160p"
    - "4K"
    - "UHD"
    - "HDR"
    - "x264"
    - "x265"
    - "HEVC"
    - "WEB"
    - "BluRay"
    - "REPACK"
    - "Remux"

Tolerating a maintenance window

Not a full file: drop this block into any of the above. Worth knowing about if your server reboots nightly.

server:
  # Seconds an unreachable server is tolerated before the dashboard turns red
  # and the bot's status flips to offline_text. 300 covers a normal restart;
  # 0 reports every hiccup immediately. Rejected credentials are reported at
  # once regardless, as an auth failure rather than an outage.
  offline_threshold: 1800