#!/usr/bin/env python # -*- coding: utf-8 -*- # plex_poster_fix.py # # Swaps out posters provided by Gracenote with ones from TMDB. # Adapted from JonnyWong16's [`select_tmdb_poster.py`](https://gist.github.com/JonnyWong16/b0e6b2761f8649d811f51866e682464b). # With large libraries, it may take a few seconds up to a minute for the script to start producing output. # Usage Instructions # # Note: usage of a Python venv is optional, but recommended to avoid installing PlexAPI system-wide. See e.g. https://realpython.com/python-virtual-environments-a-primer/ # # 1. Install Python (method depends on OS) # * Tested with Python 3.10+ # 2. Download script (example, can use any method to download the file) # * `wget https://gist.github.com/xenago/f24a5a2d82a0f0060f1794c6c1b76266/raw` # 3. Install PlexAPI library: # * `pip install PlexAPI==4.15.10` # * Depending on OS, `pip3` may need to be used as the command instead of `pip` # 4. Run script: # * `python plex_poster_fix.py --library "Library Name Here" --plex_url "https://plex-server-ip-here:32400" --plex_token "contents of myPlexAccessToken browser cookie go here"` # * To increase security, can optionally use the `PLEX_TOKEN` environment variable instead of the `--plex_token` argument # * Depending on OS, `python3` may need to be used as the command instead of `python` ################################ """ Description: Selects the default TMDB poster if no poster is selected or the current poster is from Gracenote. Author: /u/SwiftPanda16 Requires: plexapi Usage: * Provide the Plex server URL, if not using the PLEX_URL environment variable: python plex_poster_fix.py --plex_url "https://my-plex-url" * Provide a Plex API key, if not using the PLEX_TOKEN environment variable: python plex_poster_fix.py --plex_token "my-token-here" * Change the posters for an entire library: python plex_poster_fix.py --library "Movies" * Change the poster for a specific item: python plex_poster_fix.py --rating_key 1234 * By default locked posters are skipped. To update locked posters: python plex_poster_fix.py --library "Movies" --include_locked Tautulli script trigger: * Notify on recently added Tautulli script conditions: * Filter which media to select the poster. Examples: [ Media Type | is | movie ] Tautulli script arguments: * Recently Added: --rating_key {rating_key} """ import argparse import os import plexapi.base from plexapi.server import PlexServer plexapi.base.USER_DONT_RELOAD_FOR_KEYS.add("fields") def select_tmdb_poster_library(library, include_locked=False): for item in library.all(includeGuids=False): # Only reload for fields item.reload(**{k: 0 for k, v in item._INCLUDES.items()}) select_tmdb_poster_item(item, include_locked=include_locked) def select_tmdb_poster_item(item, include_locked=False): if next((f.locked for f in item.fields if f.name == "thumb"), False) and not include_locked: print(f"Poster is locked for `{item.title}`. Skipping.") return posters = item.posters() selected_poster = next((p for p in posters if p.selected), None) item_slug = f"{item.title} ({item.year})" if selected_poster is None: print(f"WARNING: No poster selected for `{item_slug}`.") elif selected_poster.provider == "gracenote": print(f"WARNING: Poster provider is `gracenote` for `{item_slug}`.") else: print(f"Poster provider is `{selected_poster.provider}` for `{item_slug}`. Skipping.") if selected_poster is None or selected_poster.provider == "gracenote": # Fallback to first poster if no TMDB posters are available tmdb_poster = next((p for p in posters if p.provider == "tmdb"), posters[0]) # Selecting the poster automatically locks it tmdb_poster.select() print(f"Selected `{tmdb_poster.provider}` poster for `{item_slug}`.") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--plex_url", type=str) parser.add_argument("--plex_token", type=str) parser.add_argument("--rating_key", type=int) parser.add_argument("--library") parser.add_argument("--include_locked", action="store_true") opts = parser.parse_args() # If a token is not provided as a CLI argument, try the environment variable if opts.plex_url: PLEX_URL = opts.plex_url else: PLEX_URL = os.getenv("PLEX_URL") if PLEX_URL is None: print("No Plex URL provided via `--plex_url` argument or `PLEX_URL` environment variable. Exiting.") exit(1) if opts.plex_token: PLEX_TOKEN = opts.plex_token else: PLEX_TOKEN = os.getenv("PLEX_TOKEN") if PLEX_TOKEN is None: print("No Plex API key provided via `--plex_token` argument or `PLEX_TOKEN` environment variable. Exiting.") exit(1) # Attempt to authenticate with the Plex server plex = PlexServer(PLEX_URL, PLEX_TOKEN) # Actually perform the poster replacement if opts.rating_key: selected_item = plex.fetchItem(opts.rating_key) select_tmdb_poster_item(selected_item, opts.include_locked) elif opts.library: selected_library = plex.library.section(opts.library) select_tmdb_poster_library(selected_library, opts.include_locked) else: print("No `--rating_key` or `--library` specified. Exiting.") exit(1)