diff --git a/README.md b/README.md index 83009e2..9ae63fd 100644 --- a/README.md +++ b/README.md @@ -118,13 +118,13 @@ Each option can be applied in three ways: |:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:| | Plex URl | Plex URL of the Server you want to connect to.
**Shell Command:** `-u` or `--url "http://192.168.1.12:32400"`
**Environment Variable:** `PLEX_URL=http://192.168.1.12:32400` | ✅ | | Plex Token | Plex Token of the Server you want to connect to.
**Shell Command:** `-t` or `--token 123456789`
**Environment Variable:** `PLEX_TOKEN=123456789` | ✅ | -| Plex Library | Plex Library Name you want to reset. Can use a bar-separated (|) list.
**Shell Command:** `-l` or --library Movies|TV Shows
**Environment Variable:** PLEX_LIBRARY=Movies|TV Shows | ✅ | +| Plex Library | Plex Library Name you want to reset.
**Shell Command:** `-l` or `--library Movies`
**Environment Variable:** `PLEX_LIBRARY=Movies` | ✅ | | Kometa Asset Folder | Kometa Asset Folder to Scan for restoring posters.
**Shell Command:** `-a` or `--asset "C:\Kometa\config\assets"`
**Environment Variable:** `KOMETA_ASSET=C:\Kometa\config\assets` | ❌ | | Kometa Original Folder | Kometa Original Folder to Scan for restoring posters.
**Shell Command:** `-o` or `--original "C:\Kometa\config\overlays\Movies Original Posters"`
**Environment Variable:** `KOMETA_ORIGINAL=C:\Kometa\config\overlays\Movies Original Posters` | ❌ | | TMDb V3 API Key | TMDb V3 API Key for restoring posters from TMDb.
**Shell Command:** `-ta` or `--tmdbapi 123456789123456789`
**Environment Variable:** `TMDBAPI=123456789123456789` | ❌ | | Start From | Plex Item Title to Start restoring posters from.
**Shell Command:** `-st` or `--start "Mad Max"`
**Environment Variable:** `START=Mad Max` | ❌ | -| Items | Restore specific Plex Items by Title. Can use a bar-separated (|) list.
**Shell Command:** `-it` or --items "Mad Max|Mad Max 2"
**Environment Variable:** ITEMS=Mad Max|Mad Max 2 | ❌ | -| Labels | Additional labels to remove. Can use a bar-separated (|) list.
**Shell Command:** `-lb` or --labels "TCM|Other Label"
**Environment Variable:** LABELS=TCM|Other Label | ❌ | +| Items | Restore specific Plex Items by Title. Can use a pipe-separated (|) list.
**Shell Command:** `-it` or --items "Mad Max|Mad Max 2"
**Environment Variable:** ITEMS=Mad Max|Mad Max 2 | ❌ | +| Labels | Additional labels to remove. Can use a pipe-separated (|) list.
**Shell Command:** `-lb` or --labels "TCM|Other Label"
**Environment Variable:** LABELS=TCM|Other Label | ❌ | | Timeout | Timeout can be any number greater then 0. **Default:** `600`
**Shell Command:** `-ti` or `--timeout 1000`
**Environment Variable:** `TIMEOUT=1000` | ❌ | | Dry Run | Run as a Dry Run without making changes in Plex.
**Shell Command:** `-d` or `--dry`
**Environment Variable:** `DRY_RUN=True` | ❌ | | Flat Assets | Kometa Asset Folder uses [Flat Assets Image Paths](https://kometa.wiki/en/latest/home/guides/assets.html#asset-naming).
**Shell Command:** `-f` or `--flat`
**Environment Variable:** `KOMETA_FLAT=True` | ❌ | @@ -140,7 +140,7 @@ Each option can be applied in three ways: ``` PLEX_URL=http://192.168.1.12:32400 PLEX_TOKEN=123456789 -PLEX_LIBRARY=Movies|TV Shows +PLEX_LIBRARY=Movies KOMETA_ASSET=C:\Kometa\config\assets KOMETA_ORIGINAL=C:\Kometa\config\overlays\Movies Original Posters TMDBAPI=123456789123456789 diff --git a/overlay_reset.py b/overlay_reset.py index 7353d44..addd753 100644 --- a/overlay_reset.py +++ b/overlay_reset.py @@ -22,13 +22,13 @@ options = [ {"arg": "u", "key": "url", "env": "PLEX_URL", "type": "str", "default": None, "help": "Plex URL of the Server you want to connect to."}, {"arg": "t", "key": "token", "env": "PLEX_TOKEN", "type": "str", "default": None, "help": "Plex Token of the Server you want to connect to."}, - {"arg": "l", "key": "library", "env": "PLEX_LIBRARY", "type": "str", "default": None, "help": "Plex Library Names you want to reset. Can use a bar-separated (|) list."}, - {"arg": "a", "key": "asset", "env": "KOMETA_ASSET", "type": "str", "default": None, "help": "Kometa Asset Folder to Scan for restoring posters."}, + {"arg": "l", "key": "library", "env": "PLEX_LIBRARY", "type": "str", "default": None, "help": "Plex Library Name you want to reset."}, + {"arg": "a", "key": "asset", "env": "KOMETA_ASSET", "type": "str", "default": None, "help": "Kometa Asset Folder to Scan for restoring posters. Can use a pipe-separated (|) list."}, {"arg": "o", "key": "original", "env": "KOMETA_ORIGINAL", "type": "str", "default": None, "help": "Kometa Original Folder to Scan for restoring posters."}, {"arg": "ta", "key": "tmdbapi", "env": "TMDBAPI", "type": "str", "default": None, "help": "TMDb V3 API Key for restoring posters from TMDb."}, {"arg": "st", "key": "start", "env": "START", "type": "str", "default": None, "help": "Plex Item Title to Start restoring posters from."}, - {"arg": "it", "key": "items", "env": "ITEMS", "type": "str", "default": None, "help": "Restore specific Plex Items by Title. Can use a bar-separated (|) list."}, - {"arg": "lb", "key": "labels", "env": "LABELS", "type": "str", "default": None, "help": "Additional labels to remove. Can use a bar-separated (|) list."}, + {"arg": "it", "key": "items", "env": "ITEMS", "type": "str", "default": None, "help": "Restore specific Plex Items by Title. Can use a pipe-separated (|) list."}, + {"arg": "lb", "key": "labels", "env": "LABELS", "type": "str", "default": None, "help": "Additional labels to remove. Can use a pipe-separated (|) list."}, {"arg": "di", "key": "discord", "env": "DISCORD", "type": "str", "default": None, "help": "Webhook URL to channel for Notifications."}, {"arg": "ti", "key": "timeout", "env": "TIMEOUT", "type": "int", "default": 600, "help": "Timeout can be any number greater then 0. (Default: 600)"}, {"arg": "d", "key": "dry", "env": "DRY_RUN", "type": "bool", "default": False, "help": "Run as a Dry Run without making changes in Plex."}, @@ -78,19 +78,11 @@ raise Failed("Plex Error: Plex token is invalid") except (requests.exceptions.ConnectionError, ParseError): raise Failed("Plex Error: Plex url is invalid") - given_names = args["library"].split("|") - sections = {s.title: s for s in server.library.sections() if s.title in given_names} - if not sections: - raise Failed(f"Plex Error: No Libraries found with the given titles: {', '.join(given_names)}. Options: {', '.join([s.title for s in server.library.sections()])}") - libs = [] - for n in given_names: - if n not in sections: - logger.error(f"Plex Error: Library: {n} not found and will be skipped. Options: {', '.join([s.title for s in server.library.sections()])}") - elif sections[n].type not in ["movie", "show"]: - logger.error(f"Plex Error: Plex Library: {n} ({sections[n].type}) must be Movie or Show and will be skipped.") - else: - logger.info(f"Plex Library: {n} will be Reset") - libs.append(sections[n]) + lib = next((s for s in server.library.sections() if s.title == args["library"]), None) + if not lib: + raise Failed(f"Plex Error: Library: {args['library']} not found. Options: {', '.join([s.title for s in server.library.sections()])}") + if lib.type not in ["movie", "show"]: + raise Failed("Plex Error: Plex Library must be Movie or Show") # Connect to TMDb tmdbapi = None @@ -122,15 +114,19 @@ # Check for Assets Folder assets_directory = os.path.join(base_dir, "assets") + assets = [] if os.path.exists(assets_directory) and os.listdir(assets_directory) and not args["asset"]: - args["asset"] = assets_directory + assets.append(assets_directory) if args["asset"]: - args["asset"] = os.path.abspath(args["asset"]) - if not os.path.exists(args["asset"]): - raise Failed(f"Folder Error: Asset Folder Path Not Found: {args['asset']}") - logger.info(f"Asset Folder Loaded: {args['asset']}") + for asset in args["asset"].split("|"): + test_dir = os.path.abspath(asset) + if not os.path.exists(test_dir): + logger.error(f"Folder Error: Asset Folder Path Not Found: {test_dir}") + else: + logger.info(f"Asset Folder Loaded: {test_dir}") + assets.append(test_dir) else: - logger.warning("No Asset Folder Found") + logger.warning("No Asset Folders Found") # Check for Originals Folder originals_directory = os.path.join(base_dir, "originals") @@ -366,174 +362,174 @@ def reload(plex_item): if not run_items and not start_from and not resume_rk: logger.separator("Resetting All Posters") - total_libs = len(libs) - for li, lib in enumerate(libs): - logger.separator(f"Resetting Library ({li + 1}/{total_libs}): {lib.title}") - items = lib.all() - total_items = len(items) - for i, item in enumerate(items): - if run_items or start_from or resume_rk: - if (run_items and item.title not in run_items) or \ - (start_from and item.title != start_from) or \ - (resume_rk and str(item.ratingKey) != resume_rk): - logger.info(f"Skipping {i + 1}/{total_items} {item.title}") - continue - elif start_from: - start_from = None - elif resume_rk: - resume_rk = None - title = item.title - current_rk = item.ratingKey - logger.separator(f"Resetting {i + 1}/{total_items} {title}", start="reset") - try: - reload(item) - except Failed as e: - logger.error(e, group=title) + items = lib.all() + total_items = len(items) + for i, item in enumerate(items): + if run_items or start_from or resume_rk: + if (run_items and item.title not in run_items) or \ + (start_from and item.title != start_from) or \ + (resume_rk and str(item.ratingKey) != resume_rk): + logger.info(f"Skipping {i + 1}/{total_items} {item.title}") continue - - # Find Item's Kometa Asset Directory - item_asset_directory = None - asset_name = None - if args["asset"]: - if not item.locations: - logger.error(f"Asset Error: No video filepath found fo {title}", group=title) - else: - file_name = "poster" - path_test = str(item.locations[0]) - if not os.path.dirname(path_test): - path_test = path_test.replace("\\", "/") - asset_name = util.validate_filename(os.path.basename(os.path.dirname(path_test) if isinstance(item, Movie) else path_test)) + elif start_from: + start_from = None + elif resume_rk: + resume_rk = None + title = item.title + current_rk = item.ratingKey + logger.separator(f"Resetting {i + 1}/{total_items} {title}", start="reset") + try: + reload(item) + except Failed as e: + logger.error(e, group=title) + continue + + # Find Item's Kometa Asset Directory + item_asset_directory = None + asset_name = None + if assets: + if not item.locations: + logger.error(f"Asset Error: No video filepath found fo {title}", group=title) + else: + file_name = "poster" + path_test = str(item.locations[0]) + if not os.path.dirname(path_test): + path_test = path_test.replace("\\", "/") + asset_name = util.validate_filename(os.path.basename(os.path.dirname(path_test) if isinstance(item, Movie) else path_test)) + for asset in assets: if args["flat"]: - item_asset_directory = args["asset"] + item_asset_directory = asset file_name = asset_name - elif os.path.isdir(os.path.join(args["asset"], asset_name)): - item_asset_directory = os.path.join(args["asset"], asset_name) + elif os.path.isdir(os.path.join(asset, asset_name)): + item_asset_directory = os.path.join(asset, asset_name) else: for n in range(1, 5): - new_path = args["asset"] + new_path = asset for m in range(1, n + 1): new_path = os.path.join(new_path, "*") matches = util.glob_filter(os.path.join(new_path, asset_name)) if len(matches) > 0: item_asset_directory = os.path.abspath(matches[0]) break - if not item_asset_directory: - logger.warning(f"Asset Warning: No Asset Directory Found") - - tmdb_item = None - if tmdbapi: - guid = requests.utils.urlparse(item.guid) # noqa - item_type = guid.scheme.split(".")[-1] - check_id = guid.netloc - tmdb_id = None - tvdb_id = None - imdb_id = None - if item_type == "plex": - for guid_tag in item.guids: - url_parsed = requests.utils.urlparse(guid_tag.id) # noqa - if url_parsed.scheme == "tvdb": - tvdb_id = int(url_parsed.netloc) - elif url_parsed.scheme == "imdb": - imdb_id = url_parsed.netloc - elif url_parsed.scheme == "tmdb": - tmdb_id = int(url_parsed.netloc) - if not tvdb_id and not imdb_id and not tmdb_id: - item.refresh() - elif item_type == "imdb": - imdb_id = check_id - elif item_type == "thetvdb": - tvdb_id = int(check_id) - elif item_type == "themoviedb": - tmdb_id = int(check_id) - elif item_type in ["xbmcnfo", "xbmcnfotv"]: - if len(check_id) > 10: - logger.warning(f"XMBC NFO Local ID: {check_id}") - try: - if item_type == "xbmcnfo": - tmdb_id = int(check_id) - else: - tvdb_id = int(check_id) - except ValueError: - imdb_id = check_id + if item_asset_directory: + break + if not item_asset_directory: + logger.warning(f"Asset Warning: No Asset Directory Found") + + tmdb_item = None + if tmdbapi: + guid = requests.utils.urlparse(item.guid) # noqa + item_type = guid.scheme.split(".")[-1] + check_id = guid.netloc + tmdb_id = None + tvdb_id = None + imdb_id = None + if item_type == "plex": + for guid_tag in item.guids: + url_parsed = requests.utils.urlparse(guid_tag.id) # noqa + if url_parsed.scheme == "tvdb": + tvdb_id = int(url_parsed.netloc) + elif url_parsed.scheme == "imdb": + imdb_id = url_parsed.netloc + elif url_parsed.scheme == "tmdb": + tmdb_id = int(url_parsed.netloc) if not tvdb_id and not imdb_id and not tmdb_id: - logger.error("Plex Error: No External GUIDs found", group=title) - if not tmdb_id and imdb_id: - try: - results = tmdbapi.find_by_id(imdb_id=imdb_id) - if results.movie_results and isinstance(item, Movie): - tmdb_id = results.movie_results[0].id - elif results.tv_results and isinstance(item, Show): - tmdb_id = results.tv_results[0].id - except TMDbException as e: - logger.warning(e, group=title) - if not tmdb_id and tvdb_id and isinstance(item, Show): - try: - results = tmdbapi.find_by_id(tvdb_id=tvdb_id) - if results.tv_results: - tmdb_id = results.tv_results[0].id - except TMDbException as e: - logger.warning(e, group=title) - if tmdb_id: - try: - tmdb_item = tmdbapi.movie(tmdb_id) if isinstance(item, Movie) else tmdbapi.tv_show(tmdb_id) - except TMDbException as e: - logger.error(f"TMDb Error: {e}", group=title) - else: - logger.error("Plex Error: TMDb ID Not Found", group=title) + item.refresh() + elif item_type == "imdb": + imdb_id = check_id + elif item_type == "thetvdb": + tvdb_id = int(check_id) + elif item_type == "themoviedb": + tmdb_id = int(check_id) + elif item_type in ["xbmcnfo", "xbmcnfotv"]: + if len(check_id) > 10: + logger.warning(f"XMBC NFO Local ID: {check_id}") + try: + if item_type == "xbmcnfo": + tmdb_id = int(check_id) + else: + tvdb_id = int(check_id) + except ValueError: + imdb_id = check_id + if not tvdb_id and not imdb_id and not tmdb_id: + logger.error("Plex Error: No External GUIDs found", group=title) + if not tmdb_id and imdb_id: + try: + results = tmdbapi.find_by_id(imdb_id=imdb_id) + if results.movie_results and isinstance(item, Movie): + tmdb_id = results.movie_results[0].id + elif results.tv_results and isinstance(item, Show): + tmdb_id = results.tv_results[0].id + except TMDbException as e: + logger.warning(e, group=title) + if not tmdb_id and tvdb_id and isinstance(item, Show): + try: + results = tmdbapi.find_by_id(tvdb_id=tvdb_id) + if results.tv_results: + tmdb_id = results.tv_results[0].id + except TMDbException as e: + logger.warning(e, group=title) + if tmdb_id: + try: + tmdb_item = tmdbapi.movie(tmdb_id) if isinstance(item, Movie) else tmdbapi.tv_show(tmdb_id) + except TMDbException as e: + logger.error(f"TMDb Error: {e}", group=title) + else: + logger.error("Plex Error: TMDb ID Not Found", group=title) - if not args["no-main"]: - reset_poster(title, item, tmdb_item.poster_url if tmdb_item else None, item_asset_directory, asset_name if args["flat"] else "poster") + if not args["no-main"]: + reset_poster(title, item, tmdb_item.poster_url if tmdb_item else None, item_asset_directory, asset_name if args["flat"] else "poster") - logger.info(f"Runtime: {logger.runtime('reset')}") + logger.info(f"Runtime: {logger.runtime('reset')}") - if isinstance(item, Show) and (args["season"] or args["episode"]): - tmdb_seasons = {s.season_number: s for s in tmdb_item.seasons} if tmdb_item else {} - for season in item.seasons(): - title = f"Season {season.seasonNumber}" - title = title if title == season.title else f"{title}: {season.title}" - title = f"{item.title}\n {title}" - if args["season"]: - logger.separator(f"Resetting {title}", start="reset") + if isinstance(item, Show) and (args["season"] or args["episode"]): + tmdb_seasons = {s.season_number: s for s in tmdb_item.seasons} if tmdb_item else {} + for season in item.seasons(): + title = f"Season {season.seasonNumber}" + title = title if title == season.title else f"{title}: {season.title}" + title = f"{item.title}\n {title}" + if args["season"]: + logger.separator(f"Resetting {title}", start="reset") + try: + reload(season) + except Failed as e: + logger.error(e, group=title) + continue + tmdb_poster = tmdb_seasons[season.seasonNumber].poster_url if season.seasonNumber in tmdb_seasons else None + file_name = f"Season{'0' if not season.seasonNumber or season.seasonNumber < 10 else ''}{season.seasonNumber}" + reset_poster(title, season, tmdb_poster, item_asset_directory, f"{asset_name}_{file_name}" if args["flat"] else file_name, parent=item) + + logger.info(f"Runtime: {logger.runtime('reset')}") + + if args["episode"]: + if not args["season"]: try: reload(season) except Failed as e: logger.error(e, group=title) continue - tmdb_poster = tmdb_seasons[season.seasonNumber].poster_url if season.seasonNumber in tmdb_seasons else None - file_name = f"Season{'0' if not season.seasonNumber or season.seasonNumber < 10 else ''}{season.seasonNumber}" - reset_poster(title, season, tmdb_poster, item_asset_directory, f"{asset_name}_{file_name}" if args["flat"] else file_name, parent=item) + tmdb_episodes = {} + if season.seasonNumber in tmdb_seasons: + for episode in tmdb_seasons[season.seasonNumber].episodes: + episode._partial = False + try: + tmdb_episodes[episode.episode_number] = episode + except TMDbException: + logger.error(f"TMDb Error: An Episode of Season {season.seasonNumber} was Not Found", group=title) + for episode in season.episodes(): + title = f"{item.title}\nEpisode {episode.seasonEpisode.upper()}: {episode.title}" + logger.separator(f"Resetting {title}", start="reset") + try: + reload(episode) + except Failed as e: + logger.error(e, group=title) + continue + tmdb_poster = tmdb_episodes[episode.episodeNumber].still_url if episode.episodeNumber in tmdb_episodes else None + file_name = episode.seasonEpisode.upper() + reset_poster(title, episode, tmdb_poster, item_asset_directory, f"{asset_name}_{file_name}" if args["flat"] else file_name, shape="landscape") logger.info(f"Runtime: {logger.runtime('reset')}") - if args["episode"]: - if not args["season"]: - try: - reload(season) - except Failed as e: - logger.error(e, group=title) - continue - tmdb_episodes = {} - if season.seasonNumber in tmdb_seasons: - for episode in tmdb_seasons[season.seasonNumber].episodes: - episode._partial = False - try: - tmdb_episodes[episode.episode_number] = episode - except TMDbException: - logger.error(f"TMDb Error: An Episode of Season {season.seasonNumber} was Not Found", group=title) - - for episode in season.episodes(): - title = f"{item.title}\nEpisode {episode.seasonEpisode.upper()}: {episode.title}" - logger.separator(f"Resetting {title}", start="reset") - try: - reload(episode) - except Failed as e: - logger.error(e, group=title) - continue - tmdb_poster = tmdb_episodes[episode.episodeNumber].still_url if episode.episodeNumber in tmdb_episodes else None - file_name = episode.seasonEpisode.upper() - reset_poster(title, episode, tmdb_poster, item_asset_directory, f"{asset_name}_{file_name}" if args["flat"] else file_name, shape="landscape") - logger.info(f"Runtime: {logger.runtime('reset')}") - current_rk = None except Failed as e: logger.separator()