--- name: plex-metadata-title-cache-read description: "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: ```python 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. ```sql 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.