Files
command-center/agent/managed-skills/plex-metadata-title-cache-read/SKILL.md
T

3.0 KiB

name, description
name description
plex-metadata-title-cache-read Read the live Plex library DB read-only for clean titles/file paths (no sqlite3 binary, server running), and use it as the title authority over filename parsing.

Plex's metadata cache already holds a clean title for every file it matched (Alien, not Alien.Directors.Cut.1979.1080p.BRrip.x265-RARBG). Read it instead of parsing filenames, and name folders / group duplicates from it.

Where it lives

Host package: data dir /var/lib/plexmediaserver, DB at

/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db

(Resources/com.plexapp.plugins.library.db under /usr/lib/plexmediaserver is a blank template — not the library.) The dir is dank:dank, so no sudo needed. Containers: check podman ps first; on Dankserver Plex runs as a host process.

Read it while Plex is RUNNING

No sqlite3 binary on the fleet hosts — use python3 -c with sqlite3. Open immutable read-only so no WAL/SHM is touched and no lock is taken:

con = sqlite3.connect(f"file:{db}?mode=ro&immutable=1", uri=True)

A long local find / can hit the tool's own deadline; scope find with -maxdepth or use the path above directly.

Schema traps

  • library_sections → column is section_type, not type. Movies = 1, TV = 2, Music = 8.
  • metadata_titles does not exist (empty name in the table list). Titles are on metadata_items: title, original_title, edition_title, year, metadata_type, parent_id, deleted_at.
  • File path is media_parts.file; join media_parts → media_items (id = media_item_id) → metadata_items (id = metadata_item_id), and LEFT JOIN metadata_items par ON par.id = mi.parent_id for the show/movie parent.
  • Movies use the LEAF title (mi.title); the parent row is empty. TV uses the parent. Filter p.deleted_at IS NULL AND mi.deleted_at IS NULL or deleted scans pollute the census.
select p.file, mi.title, mi.original_title, mi.edition_title, mi.year, m.container,
       m.video_codec, m.audio_codec, m.width, m.height
from media_parts p
join media_items m on m.id = p.media_item_id
join metadata_items mi on mi.id = m.metadata_item_id
where p.file like '/mnt/entropy/Movies/%'
  and p.deleted_at is null and mi.deleted_at is null

media_items also carries codec/resolution already — often no ffprobe pass is needed for matched files.

Use it as the title authority

Reject a Plex title only if it looks like a filename (2160p|1080p|x26[45]|hevc|bluray|web-?dl) — a healthy cache scores 0 junk titles. Coverage expectation measured on /mnt/entropy/Movies: 3,754 of 4,698 media files matched; the unmatched remainder is extras, BDMV disc trees, TV episodes misfiled into Movies, and collection box sets — exactly the classes a filename parser must still handle.

Propagate one match to every copy of a film (a group of versions) so sidecars, .nfo and media files share one stem; fall back to the filename parser only when no row exists.