A Discord Rich Presence client that shows what you're watching on Jellyfin or listening to on Audiobookshelf - with cover art, timestamps, and chapter tracking.
media_rpc.py
in addition, you can run it in docker, either building it yourself, or using https://hub.docker.com/repository/docker/simoneklundh/media-rpc
- Jellyfin support - movies and TV shows with cover art (falls back to TMDB)
- Audiobookshelf support - audiobooks and podcasts with chapter tracking
- Privacy-focused - filters by your specific User ID so your friends' streams don't show on your profile
- Secure Configuration - uses a
.envfile to keep your API tokens and IDs safe - Cover art fetched directly from your media servers
- Elapsed and remaining time timestamps
- Library blacklist to hide specific Jellyfin libraries (cached for performance)
- Auto-reconnect if Discord connection drops
- Local cover cache to avoid redundant requests
- Can optionally be ran without discord running
- Python 3.10+ (for mac this might require homebrew)
- docker
- A running Jellyfin and/or Audiobookshelf instance
- A Discord application with a Client ID
- A reverse proxy (Caddy, Nginx, Apache, Traefik) with a public domain for ABS cover art - see Cover Art Setup
services:
media-rpc:
image: simoneklundh/media-rpc:2
container_name: media-rpc
restart: unless-stopped
environment:
- DISCORD_CLIENT_ID=idhere
- DISCORD_TOKEN=tokenhere
- JELLYFIN_SERVER=https://jellyfin.domain.tld/Sessions
- JELLYFIN_API_KEY=apikeyhere
- JELLYFIN_USER_ID=useridhere
- TMDB_API_KEY=api_key_here
- ABS_SERVER=https://books.domain.tld
- ABS_API_TOKEN=apitokenhere
- USE_CHAPTER_TITLE=true
- USE_CHAPTER_TIMESTAMPS=false
- DEFAULT_JELLYFIN_SERVER_NAME= Custom Server Name
- DEFAULT_AUDIOBOOKSHELF_SERVER_NAME= Custom Server Name
- JELLYFIN_IGNORE_LIBRARIES=Library1,Library2,Library3
- USE_GATEWAY=trueHow To Get Audiobookshelf Images In Discord
1. Clone the repository:
git clone https://github.com/MakD/media-rpc.git
cd media-rpc2. Create and source virtual environment
# python or python3 depending on your setup
python -m venv venv
# linux/mac
source venv/bin/activate (or activate.fish for fish users)
# windows powershell:
./venv/bin/Activate.ps1
# in case of permission errors, run this and then retry:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser3. Install dependencies:
pip install -r requirements.txt
4. Configure the script:
# discord (DISCORD_TOKEN is only required for the gateway version)
DISCORD_TOKEN=YOUR_DISCORD_TOKEN
DISCORD_CLIENT_ID=YOUR_DISCORD_CLIENT_ID
# jellyfin
JELLYFIN_SERVER=https://jellyfin.example.com/Sessions
JELLYFIN_API_KEY=YOUR_JELLYFIN_API_KEY
JELLYFIN_USER_ID=YOUR_JELLYFIN_USER_ID
TMDB_API_KEY=YOUR_TMDB_API_KEY
# audiobookshelf
ABS_SERVER=https://abs.example.com
ABS_API_TOKEN=YOUR_ABS_API_TOKEN
# optional, defaults to true
USE_CHAPTER_TITLE=true
#optional, defaults to empty string
DEFAULT_JELLYFIN_SERVER_NAME=jellfin_server_name
#optional, defaults to empty string
DEFAULT_AUDIOBOOKSHELF_SERVER_NAME=audiobookshelf_server_name
#optional, defaults to empty list
JELLYFIN_IGNORE_LIBRARIES=library1,library2
# true => use gateway, false => use rpc (requires discord running on your computer)
# defaults to false
USE_GATEWAY=true5. Run media-rpc:
python media_rpc.py1. Clone the repository:
git clone https://github.com/MakD/media-rpc.git
cd media-rpc2. Install dependencies:
pip install -r requirements.txt3. Configure the script: Create a new file named .env in the same directory as the script and fill in your details:
# discord (DISCORD_TOKEN is only required for media_rpc_gateway.py)
DISCORD_TOKEN=YOUR_DISCORD_TOKEN
DISCORD_CLIENT_ID=YOUR_DISCORD_CLIENT_ID
# jellyfin
JELLYFIN_SERVER=https://jellyfin.example.com/Sessions
JELLYFIN_API_KEY=YOUR_JELLYFIN_API_KEY
JELLYFIN_USER_ID=YOUR_JELLYFIN_USER_ID
TMDB_API_KEY=YOUR_TMDB_API_KEY
# audiobookshelf
ABS_SERVER=https://abs.example.com
ABS_API_TOKEN=YOUR_ABS_API_TOKEN
# optional, defaults to true
USE_CHAPTER_TITLE=true
# optional, defaults to false
USE_CHAPTER_TIMESTAMPS=false
#optional, defaults to empty string
DEFAULT_JELLYFIN_SERVER_NAME=jellfin_server_name
#optional, defaults to empty string
DEFAULT_AUDIOBOOKSHELF_SERVER_NAME=audiobookshelf_server_name
#optional, defaults to empty list
JELLYFIN_IGNORE_LIBRARIES=library1,library2
# defaults to false. true => use the gateway, no discord client needed. requires DISCORD_TOKEN
USE_GATEWAY=true4. Run manually to test:
python3 media_rpc.py- Go to the Discord Developer Portal
- Create a new application
- Copy the Application ID (not a bot token) into
DISCORD_CLIENT_ID
- log into discord in the browser
- open browser dev tools (usually ctrl+shift+i or cmd+shift+i)
- open the network tab
- refresh page
- in the search field: search for credentials
- click the credentials item
- under "Request Headers", find the "Authorization" part.
- copy the value and put it into
DISCORD_TOKEN
it should look something like this MTIzNDU2Nzg5MDEyMzQ1Njc4.GxKp2A.aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789ab
- In Jellyfin, go to Dashboard → API Keys
- Create a new key and paste it into
JELLYFIN_API_KEY
To ensure Discord only shows what you are watching and ignores other users on your server:
- Open your Jellyfin web dashboard.
- Go to Dashboard → Users and click on your user profile.
- Look at the URL in your browser. Copy the long string of letters and numbers after
userId=. - Paste it into
JELLYFIN_USER_IDin your.envfile.
- In Audiobookshelf, go to Settings → Users
- Click your user and copy the API Token
- Create an account at themoviedb.org
- Go to Settings → API and request a key
To hide specific Jellyfin libraries from showing in RPC, edit the list in the .env or the variable JELLYFIN_IGNORE_LIBRARIES in the compose:
JELLYFIN_IGNORE_LIBRARIES = ["Bollywood", "Kids"]JELLYFIN_IGNORE_LIBRARIES=Bollywood,Kids
Discord fetches cover images directly from the URL you provide, which means your media server needs to be publicly accessible. Since Audiobookshelf requires authentication, you need to set up an authenticated proxy route in your reverse proxy. This lets Discord access cover art via a short, clean URL, while your token stays on the server and is never exposed.
This only makes cover artwork publicly accessible - not your audio files or any other data.
your-abs-domain.com {
handle /cover/* {
rewrite * /api/items/{http.request.uri.path.1}/cover
reverse_proxy localhost:13378 {
header_up Authorization "Bearer YOUR_ABS_TOKEN"
}
}
reverse_proxy localhost:13378
}location /cover/ {
rewrite ^/cover/(.*)$ /api/items/$1/cover break;
proxy_pass http://localhost:13378;
proxy_set_header Authorization "Bearer YOUR_ABS_TOKEN";
}RewriteRule ^/cover/(.*)$ /api/items/$1/cover [PT]
RequestHeader set Authorization "Bearer YOUR_ABS_TOKEN"
ProxyPass /cover/ http://localhost:13378/api/items/After setup, test it by opening this in your browser (no login required):
[https://your-abs-domain.com/cover/ANY_LIBRARY_ITEM_ID](https://your-abs-domain.com/cover/ANY_LIBRARY_ITEM_ID)
For Jellyfin, no extra proxy setup is needed - cover art is fetched using your API key directly.
Because Discord runs under your personal user account, this must be set up as a User Service, not a system-wide root service. Do not use sudo for these steps.
1. Create the user systemd directory:
mkdir -p ~/.config/systemd/user2. Create the service file:
nano ~/.config/systemd/user/media-rpc.service3. Paste the following configuration:
(Make sure to replace /home/YOUR_USERNAME/path/to/ with the actual absolute path to your cloned folder!)
[Unit]
Description=Media Discord RPC
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/YOUR_USERNAME/path/to/media-rpc/media_rpc_gateway.py # or use local if you want to run discord still
WorkingDirectory=/home/YOUR_USERNAME/path/to/media-rpc/
Restart=always
RestartSec=10
[Install]
WantedBy=default.target4. Enable and start the service:
systemctl --user daemon-reload
systemctl --user enable --now media-rpc.service5. Check the live logs:
journalctl --user -u media-rpc.service -fEvery 15 seconds, the script polls your media servers for active sessions. If something is playing, it constructs a Rich Presence payload with the title, state, timestamps, and cover art, then sends it to Discord over a local Unix socket. Cover URLs are cached in cover_cache.json so they are only fetched once per item. If nothing is playing, the presence is cleared.
RPC not showing
- Make sure Discord is running, and your status is not set to invisible
- Confirm
DISCORD_CLIENT_IDis the Application ID, not a bot token
Cover art showing a dice/placeholder icon
- Your image URL is likely not publicly accessible, or exceeds Discord's 256-character URL limit
- Follow the Cover Art Setup instructions for your reverse proxy
Audiobookshelf not showing
- The script detects playback by comparing the position between polls - it needs two cycles to confirm something is playing, so there may be a brief delay on startup
- Make sure
ABS_SERVERhas no trailing slash
Jellyfin not showing
- Paused sessions are intentionally ignored
- Check that your
JELLYFIN_API_KEYis valid and your server is reachable

