agentsclimarketplace

Plex media server

Skill ddnetters/homelab-agent-skills/plex-media-server

Agent skills for homelab services: Caddy, ntfy, Radarr/Sonarr, Langfuse, Plex

Install
npx -y skills add ddnetters/homelab-agent-skills --skill plex-media-server

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Plex Media Server API — library management, media search, playback sessions, server status, and automation

SKILL.md

7.2 KB, as published. Nobody here has run it

Plex Media Server

Manage your Plex Media Server: scan libraries, search media, monitor playback sessions, and automate maintenance.

Authentication

All Plex API calls require an X-Plex-Token. Find yours:

  1. Open Plex Web → any media item → Get Info → View XML → check URL for X-Plex-Token=
  2. Or from the Plex claim: curl -s "https://plex.tv/api/v2/user" -H "X-Plex-Token: YOUR_TOKEN" | jq .
export PLEX_URL="http://localhost:32400"
export PLEX_TOKEN="YOUR_PLEX_TOKEN"

Server status

# Server identity
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/identity" | jq .

# Server capabilities
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/" | jq '.MediaContainer | {friendlyName, version, platform, updatedAt}'

# Server preferences
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/:/prefs" | jq '.MediaContainer.Setting[] | {id, value}'

Libraries

List libraries

curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/sections" | \
  jq '.MediaContainer.Directory[] | {key, title, type, agent, scanner}'

Scan library

# Scan specific library (use key from list)
curl -s -X POST -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/sections/SECTION_KEY/refresh"

# Scan all libraries
curl -s -X POST -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/sections/all/refresh"

# Force full metadata refresh
curl -s -X POST -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/sections/SECTION_KEY/refresh?force=1"

Browse library contents

# List all items in a library section
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/sections/SECTION_KEY/all" | \
  jq '.MediaContainer.Metadata[] | {title, year, rating, addedAt}'

# Recently added
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/recentlyAdded" | \
  jq '.MediaContainer.Metadata[] | {title, type, year, addedAt}'

# On deck (continue watching)
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/onDeck" | \
  jq '.MediaContainer.Metadata[] | {title, grandparentTitle, viewOffset, duration}'

Search

# Search across all libraries
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/hubs/search?query=inception" | \
  jq '.MediaContainer.Hub[] | {type, size, Metadata: [.Metadata[]? | {title, year, type}]}'

# Search specific library
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/sections/SECTION_KEY/search?query=inception" | \
  jq '.MediaContainer.Metadata[] | {title, year, summary}'

Playback sessions

Currently playing

curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/status/sessions" | \
  jq '.MediaContainer.Metadata[]? | {
    title,
    user: .User.title,
    player: .Player.title,
    state: .Player.state,
    progress: ((.viewOffset / .duration * 100) | round),
    transcoding: (.TranscodeSession != null)
  }'

Playback history

curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/status/sessions/history/all" | \
  jq '.MediaContainer.Metadata[] | {title, viewedAt, accountID}'

Media info

Get item details

curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/metadata/RATING_KEY" | \
  jq '.MediaContainer.Metadata[0] | {title, year, summary, rating, Media: [.Media[] | {videoResolution, bitrate, container}]}'

Get TV show seasons and episodes

# Seasons of a show
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/metadata/SHOW_RATING_KEY/children" | \
  jq '.MediaContainer.Metadata[] | {title, index, leafCount}'

# Episodes of a season
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/library/metadata/SEASON_RATING_KEY/children" | \
  jq '.MediaContainer.Metadata[] | {title, index, duration}'

Maintenance

Optimize database

curl -s -X PUT -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/optimize"

Clean bundles (remove orphaned data)

curl -s -X PUT -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/clean/bundles"

Empty trash for a library

curl -s -X PUT -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/sections/SECTION_KEY/emptyTrash"

Analyze media (detect intros, credits)

# Analyze all items in a library
curl -s -X POST -H "X-Plex-Token: $PLEX_TOKEN" \
  "$PLEX_URL/library/sections/SECTION_KEY/analyze"

Playback control

# Get list of available players
curl -s -H "X-Plex-Token: $PLEX_TOKEN" \
  -H "Accept: application/json" \
  "$PLEX_URL/clients" | \
  jq '.MediaContainer.Server[] | {name, address, port, machineIdentifier}'

Docker deployment

services:
  plex:
    image: lscr.io/linuxserver/plex:latest
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - VERSION=docker
      - PLEX_CLAIM=claim-XXXXX  # From https://plex.tv/claim
    ports:
      - "32400:32400"
    volumes:
      - ./plex-config:/config
      - /path/to/movies:/movies
      - /path/to/tv:/tv
    restart: unless-stopped

Webhooks

Plex can send webhooks on playback events (requires Plex Pass):

Events: media.play, media.pause, media.resume, media.stop, media.scrobble, library.new, library.on.deck

Configure in Settings → Webhooks → Add Webhook URL.

Example: ntfy notification on new media

# Receive webhook and forward to ntfy (simple Flask/Node server)
# Plex sends POST with JSON payload containing event type and metadata
# Parse and forward:
curl -H "Authorization: Bearer $NTFY_TOKEN" \
     -H "Tags: movie_camera" \
     -d "New on Plex: $TITLE ($YEAR)" \
     "https://ntfy.example.com/plex"

Integration with Radarr/Sonarr

Trigger Plex library scan after Radarr/Sonarr imports:

  • Radarr: Settings → Connect → Add → Plex Media Server → enter host, port, token
  • Sonarr: Settings → Connect → Add → Plex Media Server → same config

This automatically scans the relevant library section after new media is imported.

Troubleshooting

ProblemFix
Library not showing new filesTrigger manual scan: POST /library/sections/KEY/refresh
Remote access not workingCheck port 32400 forwarded, or use reverse proxy. Check Settings → Remote Access
Transcoding issuesCheck hardware transcoding enabled. Verify /dev/dri mapped for Intel QSV
Slow library scanRun optimize and clean/bundles. Check disk I/O
Metadata wrongForce refresh: PUT /library/metadata/KEY/refresh?force=1
"Not authorized"Token expired or wrong. Re-fetch from Plex Web UI
Database lockedStop Plex, check for com.plexapp.plugins.library.db-wal, restart

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.