Last active
May 3, 2026 16:21
-
-
Save JonnyWong16/9640557cf459896a8b7e1da8863b0485 to your computer and use it in GitHub Desktop.
Saves posters, art, logos, square art, and themes from Plex to same folder as the media files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # -*- coding: utf-8 -*- | |
| ''' | |
| Description: Saves posters, art, logos, square art, and themes from Plex to same folder as the media files. | |
| Author: /u/SwiftPanda16 | |
| Requires: plexapi, tqdm (optional) | |
| Usage: | |
| * Save posters for an entire library: | |
| python save_resources.py --library "TV Shows" --poster | |
| * Save art for an entire library: | |
| python save_resources.py --library "Music" --art | |
| * Save posters, art, logos, and square art for an entire library: | |
| python save_resources.py --library "Movies" --poster --art --logo --squareArt | |
| * Save posters and art for a specific media type in a library: | |
| python save_resources.py --library "TV Shows" --libtype season --poster --art | |
| * Save themes for an entire library: | |
| python save_resources.py --library "TV Shows" --theme | |
| * Save posters for a specific item: | |
| python save_resources.py --rating_key 1234 --poster | |
| * Save art for a specific item: | |
| python save_resources.py --rating_key 1234 --art | |
| * Save posters, art, logos, and square art for a specific item: | |
| python save_resources.py --rating_key 1234 --poster --art --logo --squareArt | |
| * Overwrite existing images (add --overwrite flag): | |
| python save_resources.py --library "Movies" --poster --overwrite | |
| ''' | |
| import argparse | |
| from pathlib import Path | |
| from plexapi.server import PlexServer | |
| from plexapi.utils import download | |
| PLEX_URL = 'http://localhost:32400' | |
| PLEX_TOKEN = 'XXXXXXXXXXXXXXXXXXXX' | |
| # Specify the mapped docker folder paths {host: container}. Leave blank {} if non-docker. | |
| MAPPED_FOLDERS = { | |
| '/mnt/movies': '/movies', | |
| '/mnt/tvshows': '/tv', | |
| } | |
| _MAPPED_FOLDERS = {Path(host): Path(container) for host, container in MAPPED_FOLDERS.items()} | |
| def map_path(file_path): | |
| for host, container in _MAPPED_FOLDERS.items(): | |
| if container in file_path.parents: | |
| return host / file_path.relative_to(container) | |
| return file_path | |
| def save_library( | |
| library, | |
| libtype=None, | |
| poster=False, | |
| art=False, | |
| logo=False, | |
| squareArt=False, | |
| theme=False, | |
| overwrite=False | |
| ): | |
| for item in library.all(libtype=libtype, includeGuids=False): | |
| save_item( | |
| item, | |
| poster=poster, | |
| art=art, | |
| logo=logo, | |
| squareArt=squareArt, | |
| theme=theme, | |
| overwrite=overwrite | |
| ) | |
| def save_item( | |
| item, | |
| poster=False, | |
| art=False, | |
| logo=False, | |
| squareArt=False, | |
| theme=False, | |
| overwrite=False | |
| ): | |
| if hasattr(item, 'locations'): | |
| file_path = Path(item.locations[0]) | |
| else: | |
| file_path = Path(next(iter(item)).locations[0]) | |
| save_path = map_path(file_path) | |
| if save_path.is_file(): | |
| save_path = save_path.parent | |
| print(f"{item.title}{f' ({item.year})' if hasattr(item, 'year') else ''}") | |
| if poster: | |
| if item.type == 'season': | |
| filename = f'season{item.seasonNumber:02d}.jpg' | |
| else: | |
| filename = 'poster.jpg' | |
| download_resource('poster', item.posterUrl, save_path, filename, overwrite=overwrite) | |
| if art: | |
| download_resource('art', item.artUrl, save_path, 'art.jpg', overwrite=overwrite) | |
| if logo: | |
| download_resource('logo', item.logoUrl, save_path, 'logo.png', overwrite=overwrite) | |
| if squareArt: | |
| download_resource('squareArt', item.squareArtUrl, save_path, 'squareArt.jpg', overwrite=overwrite) | |
| if theme: | |
| download_resource('theme', item.themeUrl, save_path, 'theme.mp3', overwrite=overwrite) | |
| def download_resource(resource, resource_url, save_path, filename, overwrite=False): | |
| full_path = save_path / filename | |
| if not overwrite and full_path.exists(): | |
| print(f" └─ {resource.capitalize()} already exists at {full_path}. Skipping.") | |
| return | |
| if not resource_url: | |
| print(f" └─ No {resource} set. Skipping.") | |
| return | |
| print(f" └─ Downloading {full_path}") | |
| try: | |
| download( | |
| url=resource_url, | |
| token=plex._token, | |
| filename=filename, | |
| savepath=save_path, | |
| showstatus=True # Requires `tqdm` package | |
| ) | |
| except Exception as e: | |
| print(f" └─ Failed to download {resource}: {e}") | |
| if __name__ == '__main__': | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--rating_key', type=int) | |
| parser.add_argument('--library') | |
| parser.add_argument('--libtype', choices=['movie', 'show', 'season', 'artist', 'album']) | |
| parser.add_argument('--poster', action='store_true') | |
| parser.add_argument('--art', action='store_true') | |
| parser.add_argument('--logo', action='store_true') | |
| parser.add_argument('--squareArt', action='store_true') | |
| parser.add_argument('--theme', action='store_true') | |
| parser.add_argument('--overwrite', action='store_true') | |
| opts = parser.parse_args() | |
| plex = PlexServer(PLEX_URL, PLEX_TOKEN) | |
| if opts.rating_key: | |
| item = plex.fetchItem(opts.rating_key) | |
| save_item( | |
| item, | |
| poster=opts.poster, | |
| art=opts.art, | |
| logo=opts.logo, | |
| squareArt=opts.squareArt, | |
| theme=opts.theme, | |
| overwrite=opts.overwrite | |
| ) | |
| elif opts.library: | |
| library = plex.library.section(opts.library) | |
| save_library( | |
| library, | |
| libtype=opts.libtype, | |
| poster=opts.poster, | |
| art=opts.art, | |
| logo=opts.logo, | |
| squareArt=opts.squareArt, | |
| theme=opts.theme, | |
| overwrite=opts.overwrite | |
| ) | |
| else: | |
| print("No --rating_key or --library specified. Exiting.") |
Author
Updated script with --logo and --squareArt options, as well as a flag to overwrite existing files (the script skips existing files by default now). Background art has been changed to art.jpg instead of background.jpg.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I don't understand why "Saves poster and art images from Plex to same folder as the media files." The script did run, but I really have no idea just what it did other than add to log files Now what and how would one need to change to save posters in a simple windows folder [ie]
C:\Posters??????? I want easy access to the Posters. I have some colorized versions which are hard to find poster for, and yea, there are some rather crummy colorization , and some good.