Skip to content

Instantly share code, notes, and snippets.

@JonnyWong16
Last active April 16, 2025 16:23
Show Gist options
  • Save JonnyWong16/9640557cf459896a8b7e1da8863b0485 to your computer and use it in GitHub Desktop.
Save JonnyWong16/9640557cf459896a8b7e1da8863b0485 to your computer and use it in GitHub Desktop.
Saves poster and art images from Plex to same folder as the media files.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Description: Saves poster and art images 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_posters.py --library "TV Shows" --poster
* Save art for an entire library:
python save_posters.py --library "Music" --art
* Save posters and art for an entire library:
python save_posters.py --library "Movies" --poster --art
* Save posters and art for a specific media type in a library:
python save_posters.py --library "TV Shows" --libtype season --poster --art
* Save posters for a specific item:
python save_posters.py --rating_key 1234 --poster
* Save art for a specific item:
python save_posters.py --rating_key 1234 --art
* Save posters and art for a specific item:
python save_posters.py --rating_key 1234 --poster --art
'''
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):
for item in library.all(libtype=libtype, includeGuids=False):
save_item(item, poster=poster, art=art)
def save_item(item, poster=False, art=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
if poster:
save_item_poster(item, save_path)
if art:
save_item_art(item, save_path)
def save_item_poster(item, save_path):
print(f"Downloading poster for {item.title} to {save_path}")
try:
download(
url=item.posterUrl,
token=plex._token,
filename='poster.jpg',
savepath=save_path,
showstatus=True # Requires `tqdm` package
)
except Exception as e:
print(f"Failed to download poster for {item.title}: {e}")
def save_item_art(item, save_path):
print(f"Downloading art for {item.title} to {save_path}")
try:
download(
url=item.artUrl,
token=plex._token,
filename='background.jpg',
savepath=save_path,
showstatus=True # Requires `tqdm` package
)
except Exception as e:
print(f"Failed to download art for {item.title}: {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')
opts = parser.parse_args()
plex = PlexServer(PLEX_URL, PLEX_TOKEN)
if opts.rating_key:
item = plex.fetchItem(opts.rating_key)
save_item(item, opts.poster, opts.art)
elif opts.library:
library = plex.library.section(opts.library)
save_library(library, opts.libtype, opts.poster, opts.art)
else:
print("No --rating_key or --library specified. Exiting.")
@JonnyWong16
Copy link
Author

Updated with separate --poster and --art flags. I have not tested it myself.

@parkatoast
Copy link

parkatoast commented Apr 12, 2025

Updated with separate --poster and --art flags. I have not tested it myself.

I've tested it and it runs as it should, no issues and it's great do be able to save both poster and art with the same command. Thank you very much!

Edit:
Only change I've made is that I've changed "filename='background.jpg'," to "filename='art.jpg'," since there is nowhere else in the script it's referred to as "background", only "art".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment