add run_again mode #65

This commit is contained in:
meisnate12 2021-03-01 15:59:10 -05:00
parent 601117402c
commit b30f8182be
5 changed files with 120 additions and 13 deletions

View file

@ -16,6 +16,7 @@ settings: # Can be individually specified
show_filtered: false show_filtered: false
show_missing: true show_missing: true
save_missing: true save_missing: true
run_again_delay: 2
plex: # Can be individually specified per library as well plex: # Can be individually specified per library as well
url: http://192.168.1.12:32400 url: http://192.168.1.12:32400
token: #################### token: ####################

View file

@ -2,6 +2,8 @@ import glob, logging, os, re
from datetime import datetime, timedelta from datetime import datetime, timedelta
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
from plexapi.collection import Collections
from plexapi.exceptions import BadRequest, NotFound
logger = logging.getLogger("Plex Meta Manager") logger = logging.getLogger("Plex Meta Manager")
@ -17,12 +19,15 @@ class CollectionBuilder:
"show_missing": library.show_missing, "show_missing": library.show_missing,
"save_missing": library.save_missing "save_missing": library.save_missing
} }
self.missing_movies = []
self.missing_shows = []
self.methods = [] self.methods = []
self.filters = [] self.filters = []
self.posters = {} self.posters = {}
self.backgrounds = {} self.backgrounds = {}
self.summaries = {} self.summaries = {}
self.schedule = "" self.schedule = ""
self.rating_key_map = {}
current_time = datetime.now() current_time = datetime.now()
current_year = current_time.year current_year = current_time.year
@ -168,12 +173,7 @@ class CollectionBuilder:
logger.info(f"Scanning {self.name} Collection") logger.info(f"Scanning {self.name} Collection")
self.collectionless = "plex_collectionless" in data self.collectionless = "plex_collectionless" in data
self.run_again = "run_again" in data
self.sync = self.library.sync_mode == "sync"
if "sync_mode" in data:
if not data["sync_mode"]: logger.warning(f"Collection Warning: sync_mode attribute is blank using general: {self.library.sync_mode}")
elif data["sync_mode"] not in ["append", "sync"]: logger.warning(f"Collection Warning: {self.library.sync_mode} sync_mode invalid using general: {data['sync_mode']}")
else: self.sync = data["sync_mode"] == "sync"
if "tmdb_person" in data: if "tmdb_person" in data:
if data["tmdb_person"]: if data["tmdb_person"]:
@ -252,6 +252,9 @@ class CollectionBuilder:
elif method_name == "label_sync_mode": elif method_name == "label_sync_mode":
if data[m] in ["append", "sync"]: self.details[method_name] = data[m] if data[m] in ["append", "sync"]: self.details[method_name] = data[m]
else: raise Failed("Collection Error: label_sync_mode attribute must be either 'append' or 'sync'") else: raise Failed("Collection Error: label_sync_mode attribute must be either 'append' or 'sync'")
elif method_name == "sync_mode":
if data[m] in ["append", "sync"]: self.details[method_name] = data[m]
else: raise Failed("Collection Error: sync_mode attribute must be either 'append' or 'sync'")
elif method_name in ["arr_tag", "label"]: elif method_name in ["arr_tag", "label"]:
self.details[method_name] = util.get_list(data[m]) self.details[method_name] = util.get_list(data[m])
elif method_name in util.boolean_details: elif method_name in util.boolean_details:
@ -524,6 +527,12 @@ class CollectionBuilder:
else: else:
raise Failed(f"Collection Error: {m} attribute is blank") raise Failed(f"Collection Error: {m} attribute is blank")
self.sync = self.library.sync_mode == "sync"
if "sync_mode" in data:
if not data["sync_mode"]: logger.warning(f"Collection Warning: sync_mode attribute is blank using general: {self.library.sync_mode}")
elif data["sync_mode"] not in ["append", "sync"]: logger.warning(f"Collection Warning: {self.library.sync_mode} sync_mode invalid using general: {data['sync_mode']}")
else: self.sync = data["sync_mode"] == "sync"
self.do_arr = False self.do_arr = False
if self.library.Radarr: if self.library.Radarr:
self.do_arr = self.details["add_to_arr"] if "add_to_arr" in self.details else self.library.Radarr.add self.do_arr = self.details["add_to_arr"] if "add_to_arr" in self.details else self.library.Radarr.add
@ -654,6 +663,8 @@ class CollectionBuilder:
self.library.add_missing(collection_name, missing_movies_with_names, True) self.library.add_missing(collection_name, missing_movies_with_names, True)
if self.do_arr and self.library.Radarr: if self.do_arr and self.library.Radarr:
self.library.Radarr.add_tmdb([missing_id for title, missing_id in missing_movies_with_names], tag=self.details["arr_tag"]) self.library.Radarr.add_tmdb([missing_id for title, missing_id in missing_movies_with_names], tag=self.details["arr_tag"])
if self.run_again:
self.missing_movies.extend([missing_id for title, missing_id in missing_movies_with_names])
if len(missing_shows) > 0 and self.library.is_show: if len(missing_shows) > 0 and self.library.is_show:
missing_shows_with_names = [] missing_shows_with_names = []
for missing_id in missing_shows: for missing_id in missing_shows:
@ -675,12 +686,14 @@ class CollectionBuilder:
if self.details["show_missing"] is True: if self.details["show_missing"] is True:
logger.info(f"{collection_name} Collection | ? | {title} (TVDB: {missing_id})") logger.info(f"{collection_name} Collection | ? | {title} (TVDB: {missing_id})")
elif self.details["show_filtered"] is True: elif self.details["show_filtered"] is True:
logger.info(f"{collection_name} Collection | X | {title} (TMDb: {missing_id})") logger.info(f"{collection_name} Collection | X | {title} (TVDb: {missing_id})")
logger.info(f"{len(missing_shows_with_names)} Show{'s' if len(missing_shows_with_names) > 1 else ''} Missing") logger.info(f"{len(missing_shows_with_names)} Show{'s' if len(missing_shows_with_names) > 1 else ''} Missing")
if self.details["save_missing"] is True: if self.details["save_missing"] is True:
self.library.add_missing(collection_name, missing_shows_with_names, False) self.library.add_missing(collection_name, missing_shows_with_names, False)
if self.do_arr and self.library.Sonarr: if self.do_arr and self.library.Sonarr:
self.library.Sonarr.add_tvdb([missing_id for title, missing_id in missing_shows_with_names], tag=self.details["arr_tag"]) self.library.Sonarr.add_tvdb([missing_id for title, missing_id in missing_shows_with_names], tag=self.details["arr_tag"])
if self.run_again:
self.missing_shows.extend([missing_id for title, missing_id in missing_shows_with_names])
if self.sync and items_found > 0: if self.sync and items_found > 0:
logger.info("") logger.info("")
@ -786,12 +799,12 @@ class CollectionBuilder:
def set_image(image_method, images, is_background=False): def set_image(image_method, images, is_background=False):
if image_method in ['file_poster', 'asset_directory']: if image_method in ['file_poster', 'asset_directory']:
if is_background: collection.uploadArt(url=images[image_method])
else: collection.uploadPoster(url=images[image_method])
image_location = "File"
else:
if is_background: collection.uploadArt(filepath=images[image_method]) if is_background: collection.uploadArt(filepath=images[image_method])
else: collection.uploadPoster(filepath=images[image_method]) else: collection.uploadPoster(filepath=images[image_method])
image_location = "File"
else:
if is_background: collection.uploadArt(url=images[image_method])
else: collection.uploadPoster(url=images[image_method])
image_location = "URL" image_location = "URL"
logger.info(f"Detail: {image_method} updated collection {'background' if is_background else 'poster'} to [{image_location}] {images[image_method]}") logger.info(f"Detail: {image_method} updated collection {'background' if is_background else 'poster'} to [{image_location}] {images[image_method]}")
@ -825,4 +838,52 @@ class CollectionBuilder:
elif "tmdb_collection_details" in self.backgrounds: set_image("tmdb_collection", self.backgrounds, is_background=True) elif "tmdb_collection_details" in self.backgrounds: set_image("tmdb_collection", self.backgrounds, is_background=True)
elif "tmdb_movie_details" in self.backgrounds: set_image("tmdb_movie", self.backgrounds, is_background=True) elif "tmdb_movie_details" in self.backgrounds: set_image("tmdb_movie", self.backgrounds, is_background=True)
elif "tmdb_show_details" in self.backgrounds: set_image("tmdb_show", self.backgrounds, is_background=True) elif "tmdb_show_details" in self.backgrounds: set_image("tmdb_show", self.backgrounds, is_background=True)
else: logger.info("No background to update") else: logger.info("No background to update")
def run_collections_again(self, library, collection_obj, movie_map, show_map):
collection_items = collection_obj.items() if isinstance(collection_obj, Collections) else []
name = collection_obj.title if isinstance(collection_obj, Collections) else collection_obj
rating_keys = [movie_map[mm] for mm in self.missing_movies if mm in movie_map]
if library.is_show:
rating_keys.extend([show_map[sm] for sm in self.missing_shows if sm in show_map])
if len(rating_keys) > 0:
for rating_key in rating_keys:
try:
current = library.fetchItem(int(rating_key))
except (BadRequest, NotFound):
logger.error(f"Plex Error: Item {rating_key} not found")
continue
if current in collection_items:
logger.info(f"{name} Collection | = | {current.title}")
else:
current.addCollection(name)
logger.info(f"{name} Collection | + | {current.title}")
logger.info(f"{len(rating_keys)} {'Movie' if library.is_movie else 'Show'}{'s' if len(rating_keys) > 1 else ''} Processed")
if len(self.missing_movies) > 0:
logger.info("")
for missing_id in self.missing_movies:
if missing_id not in movie_map:
try:
movie = self.config.TMDb.get_movie(missing_id)
except Failed as e:
logger.error(e)
continue
if self.details["show_missing"] is True:
logger.info(f"{name} Collection | ? | {movie.title} (TMDb: {missing_id})")
logger.info("")
logger.info(f"{len(self.missing_movies)} Movie{'s' if len(self.missing_movies) > 1 else ''} Missing")
if len(self.missing_shows) > 0 and library.is_show:
logger.info("")
for missing_id in self.missing_shows:
if missing_id not in show_map:
try:
title = str(self.config.TVDb.get_series(self.library.Plex.language, tvdb_id=missing_id).title.encode("ascii", "replace").decode())
except Failed as e:
logger.error(e)
continue
if self.details["show_missing"] is True:
logger.info(f"{name} Collection | ? | {title} (TVDb: {missing_id})")
logger.info(f"{len(self.missing_shows)} Show{'s' if len(self.missing_shows) > 1 else ''} Missing")

View file

@ -1,4 +1,4 @@
import logging, os, re, requests import logging, os, re, requests, time
from modules import util from modules import util
from modules.anidb import AniDBAPI from modules.anidb import AniDBAPI
from modules.builder import CollectionBuilder from modules.builder import CollectionBuilder
@ -152,6 +152,7 @@ class Config:
self.general["show_filtered"] = check_for_attribute(self.data, "show_filtered", parent="settings", var_type="bool", default=False) self.general["show_filtered"] = check_for_attribute(self.data, "show_filtered", parent="settings", var_type="bool", default=False)
self.general["show_missing"] = check_for_attribute(self.data, "show_missing", parent="settings", var_type="bool", default=True) self.general["show_missing"] = check_for_attribute(self.data, "show_missing", parent="settings", var_type="bool", default=True)
self.general["save_missing"] = check_for_attribute(self.data, "save_missing", parent="settings", var_type="bool", default=True) self.general["save_missing"] = check_for_attribute(self.data, "save_missing", parent="settings", var_type="bool", default=True)
self.general["run_again_delay"] = check_for_attribute(self.data, "run_again_delay", parent="settings", var_type="int", default=0)
util.separator() util.separator()
@ -414,9 +415,13 @@ class Config:
builder.update_details(plex_collection) builder.update_details(plex_collection)
if builder.run_again and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
library.run_again.append(builder)
except Exception as e: except Exception as e:
util.print_stacktrace() util.print_stacktrace()
logger.error(f"Unknown Error: {e}") logger.error(f"Unknown Error: {e}")
if library.show_unmanaged is True and not test and not requested_collections: if library.show_unmanaged is True and not test and not requested_collections:
logger.info("") logger.info("")
util.separator(f"Unmanaged Collections in {library.name} Library") util.separator(f"Unmanaged Collections in {library.name} Library")
@ -432,6 +437,44 @@ class Config:
logger.info("") logger.info("")
logger.error("No collection to update") logger.error("No collection to update")
has_run_again = False
for library in self.libraries:
if library.run_again:
has_run_again = True
break
if has_run_again:
logger.info("")
util.separator("Run Again")
logger.info("")
length = 0
for x in range(1, self.general["run_again_delay"] + 1):
length = util.print_return(length, f"Waiting to run again in {self.general['run_again_delay'] - x + 1} minutes")
for y in range(60):
time.sleep(1)
util.print_end(length)
for library in self.libraries:
if library.run_again:
os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
logger.info("")
util.separator(f"{library.name} Library Run Again")
logger.info("")
collections = {c: library.collections[c] for c in util.get_list(requested_collections) if c in library.collections} if requested_collections else library.collections
if collections:
util.separator(f"Mapping {library.name} Library")
logger.info("")
movie_map, show_map = self.map_guids(library)
for builder in library.run_again:
logger.info("")
util.separator(f"{builder.name} Collection")
logger.info("")
try:
collection_obj = library.get_collection(builder.name)
except Failed as e:
util.print_multiline(e, error=True)
continue
builder.run_collections_again(library, collection_obj, movie_map, show_map)
def map_guids(self, library): def map_guids(self, library):
movie_map = {} movie_map = {}
show_map = {} show_map = {}

View file

@ -63,6 +63,7 @@ class PlexAPI:
self.plex = params["plex"] self.plex = params["plex"]
self.timeout = params["plex"]["timeout"] self.timeout = params["plex"]["timeout"]
self.missing = {} self.missing = {}
self.run_again = []
def add_Radarr(self, Radarr): def add_Radarr(self, Radarr):
self.Radarr = Radarr self.Radarr = Radarr

View file

@ -264,6 +264,7 @@ collectionless_lists = [
"name_mapping", "label", "label_sync_mode" "name_mapping", "label", "label_sync_mode"
] ]
other_attributes = [ other_attributes = [
"run_again",
"schedule", "schedule",
"sync_mode", "sync_mode",
"template", "template",