mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
push notifiarr update
This commit is contained in:
parent
cef150bec0
commit
8516ac10db
9 changed files with 638 additions and 401 deletions
|
@ -76,7 +76,10 @@ summary_details = [
|
|||
]
|
||||
poster_details = ["url_poster", "tmdb_poster", "tmdb_profile", "tvdb_poster", "file_poster"]
|
||||
background_details = ["url_background", "tmdb_background", "tvdb_background", "file_background"]
|
||||
boolean_details = ["visible_library", "visible_home", "visible_shared", "show_filtered", "show_missing", "save_missing", "missing_only_released", "delete_below_minimum"]
|
||||
boolean_details = [
|
||||
"visible_library", "visible_home", "visible_shared", "show_filtered", "show_missing", "save_missing", "missing_only_released",
|
||||
"delete_below_minimum", "notifiarr_collection_creation", "notifiarr_collection_addition", "notifiarr_collection_removing"
|
||||
]
|
||||
string_details = ["sort_title", "content_rating", "name_mapping"]
|
||||
ignored_details = [
|
||||
"smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test",
|
||||
|
@ -168,7 +171,10 @@ class CollectionBuilder:
|
|||
"save_missing": self.library.save_missing,
|
||||
"missing_only_released": self.library.missing_only_released,
|
||||
"create_asset_folders": self.library.create_asset_folders,
|
||||
"delete_below_minimum": self.library.delete_below_minimum
|
||||
"delete_below_minimum": self.library.delete_below_minimum,
|
||||
"notifiarr_collection_creation": self.library.notifiarr_collection_creation,
|
||||
"notifiarr_collection_addition": self.library.notifiarr_collection_addition,
|
||||
"notifiarr_collection_removing": self.library.notifiarr_collection_removing,
|
||||
}
|
||||
self.item_details = {}
|
||||
self.radarr_details = {}
|
||||
|
@ -183,6 +189,8 @@ class CollectionBuilder:
|
|||
self.filtered_keys = {}
|
||||
self.run_again_movies = []
|
||||
self.run_again_shows = []
|
||||
self.notifiarr_additions = []
|
||||
self.notifiarr_removals = []
|
||||
self.items = []
|
||||
self.posters = {}
|
||||
self.backgrounds = {}
|
||||
|
@ -191,6 +199,8 @@ class CollectionBuilder:
|
|||
self.minimum = self.library.collection_minimum
|
||||
self.current_time = datetime.now()
|
||||
self.current_year = self.current_time.year
|
||||
self.exists = False
|
||||
self.created = False
|
||||
|
||||
methods = {m.lower(): m for m in self.data}
|
||||
|
||||
|
@ -537,6 +547,7 @@ class CollectionBuilder:
|
|||
elif not self.library.Sonarr and "sonarr" in method_name: raise Failed(f"Collection Error: {method_final} requires Sonarr to be configured")
|
||||
elif not self.library.Tautulli and "tautulli" in method_name: raise Failed(f"Collection Error: {method_final} requires Tautulli to be configured")
|
||||
elif not self.config.MyAnimeList and "mal" in method_name: raise Failed(f"Collection Error: {method_final} requires MyAnimeList to be configured")
|
||||
elif not self.library.Notifiarr and "notifiarr" in method_name: raise Failed(f"Collection Error: {method_final} requires Notifiarr to be configured")
|
||||
elif self.library.is_movie and method_name in show_only_builders: raise Failed(f"Collection Error: {method_final} attribute only works for show libraries")
|
||||
elif self.library.is_show and method_name in movie_only_builders: raise Failed(f"Collection Error: {method_final} attribute only works for movie libraries")
|
||||
elif self.library.is_show and method_name in plex.movie_only_searches: raise Failed(f"Collection Error: {method_final} plex search only works for movie libraries")
|
||||
|
@ -617,6 +628,8 @@ class CollectionBuilder:
|
|||
if self.sync and self.obj:
|
||||
for item in self.library.get_collection_items(self.obj, self.smart_label_collection):
|
||||
self.plex_map[item.ratingKey] = item
|
||||
if self.obj:
|
||||
self.exists = True
|
||||
else:
|
||||
self.obj = None
|
||||
self.sync = False
|
||||
|
@ -1122,7 +1135,7 @@ class CollectionBuilder:
|
|||
rating_keys.append(input_id)
|
||||
elif id_type == "tmdb" and not self.parts_collection:
|
||||
if input_id in self.library.movie_map:
|
||||
rating_keys.append(self.library.movie_map[input_id][0])
|
||||
rating_keys.extend(self.library.movie_map[input_id])
|
||||
elif input_id not in self.missing_movies:
|
||||
self.missing_movies.append(input_id)
|
||||
elif id_type in ["tvdb", "tmdb_show"] and not self.parts_collection:
|
||||
|
@ -1133,12 +1146,12 @@ class CollectionBuilder:
|
|||
logger.error(e)
|
||||
continue
|
||||
if input_id in self.library.show_map:
|
||||
rating_keys.append(self.library.show_map[input_id][0])
|
||||
rating_keys.extend(self.library.show_map[input_id])
|
||||
elif input_id not in self.missing_shows:
|
||||
self.missing_shows.append(input_id)
|
||||
elif id_type == "imdb" and not self.parts_collection:
|
||||
if input_id in self.library.imdb_map:
|
||||
rating_keys.append(self.library.imdb_map[input_id][0])
|
||||
rating_keys.extend(self.library.imdb_map[input_id])
|
||||
else:
|
||||
if self.do_missing:
|
||||
try:
|
||||
|
@ -1486,6 +1499,14 @@ class CollectionBuilder:
|
|||
self.plex_map[current.ratingKey] = None
|
||||
else:
|
||||
self.library.alter_collection(current, name, smart_label_collection=self.smart_label_collection)
|
||||
if self.details["notifiarr_collection_addition"]:
|
||||
if self.library.is_movie and current.ratingKey in self.library.movie_rating_key_map:
|
||||
add_id = self.library.movie_rating_key_map[current.ratingKey]
|
||||
elif self.library.is_show and current.ratingKey in self.library.show_rating_key_map:
|
||||
add_id = self.library.show_rating_key_map[current.ratingKey]
|
||||
else:
|
||||
add_id = None
|
||||
self.notifiarr_additions.append({"title": current.title, "id": add_id})
|
||||
util.print_end()
|
||||
logger.info("")
|
||||
logger.info(f"{total} {self.collection_level.capitalize()}{'s' if total > 1 else ''} Processed")
|
||||
|
@ -1714,6 +1735,14 @@ class CollectionBuilder:
|
|||
self.library.reload(item)
|
||||
logger.info(f"{self.name} Collection | - | {self.item_title(item)}")
|
||||
self.library.alter_collection(item, self.name, smart_label_collection=self.smart_label_collection, add=False)
|
||||
if self.details["notifiarr_collection_removing"]:
|
||||
if self.library.is_movie and item.ratingKey in self.library.movie_rating_key_map:
|
||||
remove_id = self.library.movie_rating_key_map[item.ratingKey]
|
||||
elif self.library.is_show and item.ratingKey in self.library.show_rating_key_map:
|
||||
remove_id = self.library.show_rating_key_map[item.ratingKey]
|
||||
else:
|
||||
remove_id = None
|
||||
self.notifiarr_removals.append({"title": item.title, "id": remove_id})
|
||||
count_removed += 1
|
||||
if count_removed > 0:
|
||||
logger.info("")
|
||||
|
@ -1835,6 +1864,8 @@ class CollectionBuilder:
|
|||
except Failed:
|
||||
raise Failed(f"Collection Error: Label: {self.name} was not added to any items in the Library")
|
||||
self.obj = self.library.get_collection(self.name)
|
||||
if not self.exists:
|
||||
self.created = True
|
||||
|
||||
def update_details(self):
|
||||
logger.info("")
|
||||
|
@ -2002,10 +2033,26 @@ class CollectionBuilder:
|
|||
self.library.move_item(self.obj, key, after=previous)
|
||||
previous = key
|
||||
|
||||
def send_notifications(self):
|
||||
if self.obj and (
|
||||
(self.details["notifiarr_collection_creation"] and self.created) or
|
||||
(self.details["notifiarr_collection_addition"] and len(self.notifiarr_additions) > 0) or
|
||||
(self.details["notifiarr_collection_removing"] and len(self.notifiarr_removals) > 0)
|
||||
):
|
||||
self.obj.reload()
|
||||
self.library.Notifiarr.plex_collection(
|
||||
self.obj,
|
||||
created=self.created,
|
||||
additions=self.notifiarr_additions,
|
||||
removals=self.notifiarr_removals
|
||||
)
|
||||
|
||||
def run_collections_again(self):
|
||||
self.obj = self.library.get_collection(self.name)
|
||||
name, collection_items = self.library.get_collection_name_and_items(self.obj, self.smart_label_collection)
|
||||
self.created = False
|
||||
rating_keys = []
|
||||
self.notifiarr_additions = []
|
||||
for mm in self.run_again_movies:
|
||||
if mm in self.library.movie_map:
|
||||
rating_keys.extend(self.library.movie_map[mm])
|
||||
|
@ -2025,6 +2072,14 @@ class CollectionBuilder:
|
|||
else:
|
||||
self.library.alter_collection(current, name, smart_label_collection=self.smart_label_collection)
|
||||
logger.info(f"{name} Collection | + | {self.item_title(current)}")
|
||||
if self.library.is_movie and current.ratingKey in self.library.movie_rating_key_map:
|
||||
add_id = self.library.movie_rating_key_map[current.ratingKey]
|
||||
elif self.library.is_show and current.ratingKey in self.library.show_rating_key_map:
|
||||
add_id = self.library.show_rating_key_map[current.ratingKey]
|
||||
else:
|
||||
add_id = None
|
||||
self.notifiarr_additions.append({"title": current.title, "id": add_id})
|
||||
self.send_notifications()
|
||||
logger.info(f"{len(rating_keys)} {self.collection_level.capitalize()}{'s' if len(rating_keys) > 1 else ''} Processed")
|
||||
|
||||
if len(self.run_again_movies) > 0:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import logging, os, requests
|
||||
import base64, logging, os, requests
|
||||
from datetime import datetime
|
||||
from lxml import html
|
||||
from modules import util, radarr, sonarr
|
||||
|
@ -10,6 +10,7 @@ from modules.icheckmovies import ICheckMovies
|
|||
from modules.imdb import IMDb
|
||||
from modules.letterboxd import Letterboxd
|
||||
from modules.mal import MyAnimeList
|
||||
from modules.notifiarr import NotifiarrFactory
|
||||
from modules.omdb import OMDb
|
||||
from modules.plex import Plex
|
||||
from modules.radarr import Radarr
|
||||
|
@ -29,21 +30,22 @@ sync_modes = {"append": "Only Add Items to the Collection", "sync": "Add & Remov
|
|||
mass_update_options = {"tmdb": "Use TMDb Metadata", "omdb": "Use IMDb Metadata through OMDb"}
|
||||
|
||||
class Config:
|
||||
def __init__(self, default_dir, config_path=None, is_test=False, time_scheduled=None, requested_collections=None, requested_libraries=None, resume_from=None):
|
||||
def __init__(self, default_dir, attrs):
|
||||
logger.info("Locating config...")
|
||||
if config_path and os.path.exists(config_path): self.config_path = os.path.abspath(config_path)
|
||||
elif config_path and not os.path.exists(config_path): raise Failed(f"Config Error: config not found at {os.path.abspath(config_path)}")
|
||||
config_file = attrs["config_file"]
|
||||
if config_file and os.path.exists(config_file): self.config_path = os.path.abspath(config_file)
|
||||
elif config_file and not os.path.exists(config_file): raise Failed(f"Config Error: config not found at {os.path.abspath(config_file)}")
|
||||
elif os.path.exists(os.path.join(default_dir, "config.yml")): self.config_path = os.path.abspath(os.path.join(default_dir, "config.yml"))
|
||||
else: raise Failed(f"Config Error: config not found at {os.path.abspath(default_dir)}")
|
||||
logger.info(f"Using {self.config_path} as config")
|
||||
|
||||
self.default_dir = default_dir
|
||||
self.test_mode = is_test
|
||||
self.run_start_time = time_scheduled
|
||||
self.run_hour = datetime.strptime(time_scheduled, "%H:%M").hour
|
||||
self.requested_collections = util.get_list(requested_collections)
|
||||
self.requested_libraries = util.get_list(requested_libraries)
|
||||
self.resume_from = resume_from
|
||||
self.test_mode = attrs["test"]
|
||||
self.run_start_time = attrs["time"]
|
||||
self.run_hour = datetime.strptime(attrs["time"], "%H:%M").hour
|
||||
self.requested_collections = util.get_list(attrs["collections"])
|
||||
self.requested_libraries = util.get_list(attrs["libraries"])
|
||||
self.resume_from = attrs["resume"]
|
||||
|
||||
yaml.YAML().allow_duplicate_keys = True
|
||||
try:
|
||||
|
@ -87,6 +89,7 @@ class Config:
|
|||
if "radarr" in new_config: new_config["radarr"] = new_config.pop("radarr")
|
||||
if "sonarr" in new_config: new_config["sonarr"] = new_config.pop("sonarr")
|
||||
if "omdb" in new_config: new_config["omdb"] = new_config.pop("omdb")
|
||||
if "notifiarr" in new_config: new_config["notifiarr"] = new_config.pop("notifiarr")
|
||||
if "trakt" in new_config: new_config["trakt"] = new_config.pop("trakt")
|
||||
if "mal" in new_config: new_config["mal"] = new_config.pop("mal")
|
||||
if "anidb" in new_config: new_config["anidb"] = new_config.pop("anidb")
|
||||
|
@ -186,7 +189,10 @@ class Config:
|
|||
"missing_only_released": check_for_attribute(self.data, "missing_only_released", parent="settings", var_type="bool", default=False),
|
||||
"create_asset_folders": check_for_attribute(self.data, "create_asset_folders", parent="settings", var_type="bool", default=False),
|
||||
"collection_minimum": check_for_attribute(self.data, "collection_minimum", parent="settings", var_type="int", default=1),
|
||||
"delete_below_minimum": check_for_attribute(self.data, "delete_below_minimum", parent="settings", var_type="bool", default=False)
|
||||
"delete_below_minimum": check_for_attribute(self.data, "delete_below_minimum", parent="settings", var_type="bool", default=False),
|
||||
"notifiarr_collection_creation": check_for_attribute(self.data, "notifiarr_collection_creation", parent="settings", var_type="bool", default=False),
|
||||
"notifiarr_collection_addition": check_for_attribute(self.data, "notifiarr_collection_addition", parent="settings", var_type="bool", default=False),
|
||||
"notifiarr_collection_removing": check_for_attribute(self.data, "notifiarr_collection_removing", parent="settings", var_type="bool", default=False)
|
||||
}
|
||||
if self.general["cache"]:
|
||||
util.separator()
|
||||
|
@ -196,323 +202,383 @@ class Config:
|
|||
|
||||
util.separator()
|
||||
|
||||
self.TMDb = None
|
||||
if "tmdb" in self.data:
|
||||
logger.info("Connecting to TMDb...")
|
||||
self.TMDb = TMDb(self, {
|
||||
"apikey": check_for_attribute(self.data, "apikey", parent="tmdb", throw=True),
|
||||
"language": check_for_attribute(self.data, "language", parent="tmdb", default="en")
|
||||
})
|
||||
logger.info(f"TMDb Connection {'Failed' if self.TMDb is None else 'Successful'}")
|
||||
else:
|
||||
raise Failed("Config Error: tmdb attribute not found")
|
||||
|
||||
util.separator()
|
||||
|
||||
self.OMDb = None
|
||||
if "omdb" in self.data:
|
||||
logger.info("Connecting to OMDb...")
|
||||
self.NotifiarrFactory = None
|
||||
if "notifiarr" in self.data:
|
||||
logger.info("Connecting to Notifiarr...")
|
||||
try:
|
||||
self.OMDb = OMDb(self, {"apikey": check_for_attribute(self.data, "apikey", parent="omdb", throw=True)})
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
logger.info(f"OMDb Connection {'Failed' if self.OMDb is None else 'Successful'}")
|
||||
else:
|
||||
logger.warning("omdb attribute not found")
|
||||
|
||||
util.separator()
|
||||
|
||||
self.Trakt = None
|
||||
if "trakt" in self.data:
|
||||
logger.info("Connecting to Trakt...")
|
||||
try:
|
||||
self.Trakt = Trakt(self, {
|
||||
"client_id": check_for_attribute(self.data, "client_id", parent="trakt", throw=True),
|
||||
"client_secret": check_for_attribute(self.data, "client_secret", parent="trakt", throw=True),
|
||||
"config_path": self.config_path,
|
||||
"authorization": self.data["trakt"]["authorization"] if "authorization" in self.data["trakt"] else None
|
||||
self.NotifiarrFactory = NotifiarrFactory(self, {
|
||||
"apikey": check_for_attribute(self.data, "apikey", parent="notifiarr", throw=True),
|
||||
"error_notification": check_for_attribute(self.data, "error_notification", parent="notifiarr", var_type="bool", default=True),
|
||||
"develop": check_for_attribute(self.data, "develop", parent="notifiarr", var_type="bool", default=False, do_print=False, save=False),
|
||||
"test": check_for_attribute(self.data, "test", parent="notifiarr", var_type="bool", default=False, do_print=False, save=False)
|
||||
})
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
logger.info(f"Trakt Connection {'Failed' if self.Trakt is None else 'Successful'}")
|
||||
logger.info(f"Notifiarr Connection {'Failed' if self.NotifiarrFactory is None else 'Successful'}")
|
||||
else:
|
||||
logger.warning("trakt attribute not found")
|
||||
logger.warning("notifiarr attribute not found")
|
||||
|
||||
self.errors = []
|
||||
|
||||
util.separator()
|
||||
|
||||
self.MyAnimeList = None
|
||||
if "mal" in self.data:
|
||||
logger.info("Connecting to My Anime List...")
|
||||
try:
|
||||
self.MyAnimeList = MyAnimeList(self, {
|
||||
"client_id": check_for_attribute(self.data, "client_id", parent="mal", throw=True),
|
||||
"client_secret": check_for_attribute(self.data, "client_secret", parent="mal", throw=True),
|
||||
"config_path": self.config_path,
|
||||
"authorization": self.data["mal"]["authorization"] if "authorization" in self.data["mal"] else None
|
||||
try:
|
||||
self.TMDb = None
|
||||
if "tmdb" in self.data:
|
||||
logger.info("Connecting to TMDb...")
|
||||
self.TMDb = TMDb(self, {
|
||||
"apikey": check_for_attribute(self.data, "apikey", parent="tmdb", throw=True),
|
||||
"language": check_for_attribute(self.data, "language", parent="tmdb", default="en")
|
||||
})
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
logger.info(f"My Anime List Connection {'Failed' if self.MyAnimeList is None else 'Successful'}")
|
||||
else:
|
||||
logger.warning("mal attribute not found")
|
||||
logger.info(f"TMDb Connection {'Failed' if self.TMDb is None else 'Successful'}")
|
||||
else:
|
||||
raise Failed("Config Error: tmdb attribute not found")
|
||||
|
||||
util.separator()
|
||||
|
||||
self.AniDB = None
|
||||
if "anidb" in self.data:
|
||||
util.separator()
|
||||
logger.info("Connecting to AniDB...")
|
||||
try:
|
||||
self.AniDB = AniDB(self, {
|
||||
"username": check_for_attribute(self.data, "username", parent="anidb", throw=True),
|
||||
"password": check_for_attribute(self.data, "password", parent="anidb", throw=True)
|
||||
|
||||
self.OMDb = None
|
||||
if "omdb" in self.data:
|
||||
logger.info("Connecting to OMDb...")
|
||||
try:
|
||||
self.OMDb = OMDb(self, {"apikey": check_for_attribute(self.data, "apikey", parent="omdb", throw=True)})
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
logger.info(f"OMDb Connection {'Failed' if self.OMDb is None else 'Successful'}")
|
||||
else:
|
||||
logger.warning("omdb attribute not found")
|
||||
|
||||
util.separator()
|
||||
|
||||
self.Trakt = None
|
||||
if "trakt" in self.data:
|
||||
logger.info("Connecting to Trakt...")
|
||||
try:
|
||||
self.Trakt = Trakt(self, {
|
||||
"client_id": check_for_attribute(self.data, "client_id", parent="trakt", throw=True),
|
||||
"client_secret": check_for_attribute(self.data, "client_secret", parent="trakt", throw=True),
|
||||
"config_path": self.config_path,
|
||||
"authorization": self.data["trakt"]["authorization"] if "authorization" in self.data["trakt"] else None
|
||||
})
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
logger.info(f"My Anime List Connection {'Failed Continuing as Guest ' if self.MyAnimeList is None else 'Successful'}")
|
||||
if self.AniDB is None:
|
||||
self.AniDB = AniDB(self, None)
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
logger.info(f"Trakt Connection {'Failed' if self.Trakt is None else 'Successful'}")
|
||||
else:
|
||||
logger.warning("trakt attribute not found")
|
||||
|
||||
self.TVDb = TVDb(self)
|
||||
self.IMDb = IMDb(self)
|
||||
self.Convert = Convert(self)
|
||||
self.AniList = AniList(self)
|
||||
self.Letterboxd = Letterboxd(self)
|
||||
self.ICheckMovies = ICheckMovies(self)
|
||||
self.StevenLu = StevenLu(self)
|
||||
|
||||
util.separator()
|
||||
|
||||
logger.info("Connecting to Plex Libraries...")
|
||||
|
||||
self.general["plex"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="plex", var_type="url", default_is_none=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="plex", default_is_none=True),
|
||||
"timeout": check_for_attribute(self.data, "timeout", parent="plex", var_type="int", default=60),
|
||||
"clean_bundles": check_for_attribute(self.data, "clean_bundles", parent="plex", var_type="bool", default=False),
|
||||
"empty_trash": check_for_attribute(self.data, "empty_trash", parent="plex", var_type="bool", default=False),
|
||||
"optimize": check_for_attribute(self.data, "optimize", parent="plex", var_type="bool", default=False)
|
||||
}
|
||||
self.general["radarr"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="radarr", var_type="url", default_is_none=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="radarr", default_is_none=True),
|
||||
"add": check_for_attribute(self.data, "add", parent="radarr", var_type="bool", default=False),
|
||||
"add_existing": check_for_attribute(self.data, "add_existing", parent="radarr", var_type="bool", default=False),
|
||||
"root_folder_path": check_for_attribute(self.data, "root_folder_path", parent="radarr", default_is_none=True),
|
||||
"monitor": check_for_attribute(self.data, "monitor", parent="radarr", var_type="bool", default=True),
|
||||
"availability": check_for_attribute(self.data, "availability", parent="radarr", test_list=radarr.availability_descriptions, default="announced"),
|
||||
"quality_profile": check_for_attribute(self.data, "quality_profile", parent="radarr", default_is_none=True),
|
||||
"tag": check_for_attribute(self.data, "tag", parent="radarr", var_type="lower_list", default_is_none=True),
|
||||
"search": check_for_attribute(self.data, "search", parent="radarr", var_type="bool", default=False)
|
||||
}
|
||||
self.general["sonarr"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="sonarr", var_type="url", default_is_none=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="sonarr", default_is_none=True),
|
||||
"add": check_for_attribute(self.data, "add", parent="sonarr", var_type="bool", default=False),
|
||||
"add_existing": check_for_attribute(self.data, "add_existing", parent="sonarr", var_type="bool", default=False),
|
||||
"root_folder_path": check_for_attribute(self.data, "root_folder_path", parent="sonarr", default_is_none=True),
|
||||
"monitor": check_for_attribute(self.data, "monitor", parent="sonarr", test_list=sonarr.monitor_descriptions, default="all"),
|
||||
"quality_profile": check_for_attribute(self.data, "quality_profile", parent="sonarr", default_is_none=True),
|
||||
"language_profile": check_for_attribute(self.data, "language_profile", parent="sonarr", default_is_none=True),
|
||||
"series_type": check_for_attribute(self.data, "series_type", parent="sonarr", test_list=sonarr.series_type_descriptions, default="standard"),
|
||||
"season_folder": check_for_attribute(self.data, "season_folder", parent="sonarr", var_type="bool", default=True),
|
||||
"tag": check_for_attribute(self.data, "tag", parent="sonarr", var_type="lower_list", default_is_none=True),
|
||||
"search": check_for_attribute(self.data, "search", parent="sonarr", var_type="bool", default=False),
|
||||
"cutoff_search": check_for_attribute(self.data, "cutoff_search", parent="sonarr", var_type="bool", default=False)
|
||||
}
|
||||
self.general["tautulli"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="tautulli", var_type="url", default_is_none=True),
|
||||
"apikey": check_for_attribute(self.data, "apikey", parent="tautulli", default_is_none=True)
|
||||
}
|
||||
|
||||
self.libraries = []
|
||||
libs = check_for_attribute(self.data, "libraries", throw=True)
|
||||
|
||||
for library_name, lib in libs.items():
|
||||
if self.requested_libraries and library_name not in self.requested_libraries:
|
||||
continue
|
||||
util.separator()
|
||||
params = {
|
||||
"mapping_name": str(library_name),
|
||||
"name": str(lib["library_name"]) if lib and "library_name" in lib and lib["library_name"] else str(library_name)
|
||||
|
||||
self.MyAnimeList = None
|
||||
if "mal" in self.data:
|
||||
logger.info("Connecting to My Anime List...")
|
||||
try:
|
||||
self.MyAnimeList = MyAnimeList(self, {
|
||||
"client_id": check_for_attribute(self.data, "client_id", parent="mal", throw=True),
|
||||
"client_secret": check_for_attribute(self.data, "client_secret", parent="mal", throw=True),
|
||||
"config_path": self.config_path,
|
||||
"authorization": self.data["mal"]["authorization"] if "authorization" in self.data["mal"] else None
|
||||
})
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
logger.info(f"My Anime List Connection {'Failed' if self.MyAnimeList is None else 'Successful'}")
|
||||
else:
|
||||
logger.warning("mal attribute not found")
|
||||
|
||||
util.separator()
|
||||
|
||||
self.AniDB = None
|
||||
if "anidb" in self.data:
|
||||
util.separator()
|
||||
logger.info("Connecting to AniDB...")
|
||||
try:
|
||||
self.AniDB = AniDB(self, {
|
||||
"username": check_for_attribute(self.data, "username", parent="anidb", throw=True),
|
||||
"password": check_for_attribute(self.data, "password", parent="anidb", throw=True)
|
||||
})
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
logger.info(f"My Anime List Connection {'Failed Continuing as Guest ' if self.MyAnimeList is None else 'Successful'}")
|
||||
if self.AniDB is None:
|
||||
self.AniDB = AniDB(self, None)
|
||||
|
||||
self.TVDb = TVDb(self)
|
||||
self.IMDb = IMDb(self)
|
||||
self.Convert = Convert(self)
|
||||
self.AniList = AniList(self)
|
||||
self.Letterboxd = Letterboxd(self)
|
||||
self.ICheckMovies = ICheckMovies(self)
|
||||
self.StevenLu = StevenLu(self)
|
||||
|
||||
util.separator()
|
||||
|
||||
logger.info("Connecting to Plex Libraries...")
|
||||
|
||||
self.general["plex"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="plex", var_type="url", default_is_none=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="plex", default_is_none=True),
|
||||
"timeout": check_for_attribute(self.data, "timeout", parent="plex", var_type="int", default=60),
|
||||
"clean_bundles": check_for_attribute(self.data, "clean_bundles", parent="plex", var_type="bool", default=False),
|
||||
"empty_trash": check_for_attribute(self.data, "empty_trash", parent="plex", var_type="bool", default=False),
|
||||
"optimize": check_for_attribute(self.data, "optimize", parent="plex", var_type="bool", default=False)
|
||||
}
|
||||
self.general["radarr"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="radarr", var_type="url", default_is_none=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="radarr", default_is_none=True),
|
||||
"add": check_for_attribute(self.data, "add", parent="radarr", var_type="bool", default=False),
|
||||
"add_existing": check_for_attribute(self.data, "add_existing", parent="radarr", var_type="bool", default=False),
|
||||
"root_folder_path": check_for_attribute(self.data, "root_folder_path", parent="radarr", default_is_none=True),
|
||||
"monitor": check_for_attribute(self.data, "monitor", parent="radarr", var_type="bool", default=True),
|
||||
"availability": check_for_attribute(self.data, "availability", parent="radarr", test_list=radarr.availability_descriptions, default="announced"),
|
||||
"quality_profile": check_for_attribute(self.data, "quality_profile", parent="radarr", default_is_none=True),
|
||||
"tag": check_for_attribute(self.data, "tag", parent="radarr", var_type="lower_list", default_is_none=True),
|
||||
"search": check_for_attribute(self.data, "search", parent="radarr", var_type="bool", default=False)
|
||||
}
|
||||
self.general["sonarr"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="sonarr", var_type="url", default_is_none=True),
|
||||
"token": check_for_attribute(self.data, "token", parent="sonarr", default_is_none=True),
|
||||
"add": check_for_attribute(self.data, "add", parent="sonarr", var_type="bool", default=False),
|
||||
"add_existing": check_for_attribute(self.data, "add_existing", parent="sonarr", var_type="bool", default=False),
|
||||
"root_folder_path": check_for_attribute(self.data, "root_folder_path", parent="sonarr", default_is_none=True),
|
||||
"monitor": check_for_attribute(self.data, "monitor", parent="sonarr", test_list=sonarr.monitor_descriptions, default="all"),
|
||||
"quality_profile": check_for_attribute(self.data, "quality_profile", parent="sonarr", default_is_none=True),
|
||||
"language_profile": check_for_attribute(self.data, "language_profile", parent="sonarr", default_is_none=True),
|
||||
"series_type": check_for_attribute(self.data, "series_type", parent="sonarr", test_list=sonarr.series_type_descriptions, default="standard"),
|
||||
"season_folder": check_for_attribute(self.data, "season_folder", parent="sonarr", var_type="bool", default=True),
|
||||
"tag": check_for_attribute(self.data, "tag", parent="sonarr", var_type="lower_list", default_is_none=True),
|
||||
"search": check_for_attribute(self.data, "search", parent="sonarr", var_type="bool", default=False),
|
||||
"cutoff_search": check_for_attribute(self.data, "cutoff_search", parent="sonarr", var_type="bool", default=False)
|
||||
}
|
||||
self.general["tautulli"] = {
|
||||
"url": check_for_attribute(self.data, "url", parent="tautulli", var_type="url", default_is_none=True),
|
||||
"apikey": check_for_attribute(self.data, "apikey", parent="tautulli", default_is_none=True)
|
||||
}
|
||||
display_name = f"{params['name']} ({params['mapping_name']})" if lib and "library_name" in lib and lib["library_name"] else params["mapping_name"]
|
||||
|
||||
util.separator(f"{display_name} Configuration")
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} Library...")
|
||||
self.libraries = []
|
||||
libs = check_for_attribute(self.data, "libraries", throw=True)
|
||||
|
||||
params["asset_directory"] = check_for_attribute(lib, "asset_directory", parent="settings", var_type="list_path", default=self.general["asset_directory"], default_is_none=True, save=False)
|
||||
if params["asset_directory"] is None:
|
||||
logger.warning("Config Warning: Assets will not be used asset_directory attribute must be set under config or under this specific Library")
|
||||
|
||||
params["asset_folders"] = check_for_attribute(lib, "asset_folders", parent="settings", var_type="bool", default=self.general["asset_folders"], do_print=False, save=False)
|
||||
params["assets_for_all"] = check_for_attribute(lib, "assets_for_all", parent="settings", var_type="bool", default=self.general["assets_for_all"], do_print=False, save=False)
|
||||
params["sync_mode"] = check_for_attribute(lib, "sync_mode", parent="settings", test_list=sync_modes, default=self.general["sync_mode"], do_print=False, save=False)
|
||||
params["show_unmanaged"] = check_for_attribute(lib, "show_unmanaged", parent="settings", var_type="bool", default=self.general["show_unmanaged"], do_print=False, save=False)
|
||||
params["show_filtered"] = check_for_attribute(lib, "show_filtered", parent="settings", var_type="bool", default=self.general["show_filtered"], do_print=False, save=False)
|
||||
params["show_missing"] = check_for_attribute(lib, "show_missing", parent="settings", var_type="bool", default=self.general["show_missing"], do_print=False, save=False)
|
||||
params["save_missing"] = check_for_attribute(lib, "save_missing", parent="settings", var_type="bool", default=self.general["save_missing"], do_print=False, save=False)
|
||||
params["missing_only_released"] = check_for_attribute(lib, "missing_only_released", parent="settings", var_type="bool", default=self.general["missing_only_released"], do_print=False, save=False)
|
||||
params["create_asset_folders"] = check_for_attribute(lib, "create_asset_folders", parent="settings", var_type="bool", default=self.general["create_asset_folders"], do_print=False, save=False)
|
||||
params["collection_minimum"] = check_for_attribute(lib, "collection_minimum", parent="settings", var_type="int", default=self.general["collection_minimum"], do_print=False, save=False)
|
||||
params["delete_below_minimum"] = check_for_attribute(lib, "delete_below_minimum", parent="settings", var_type="bool", default=self.general["delete_below_minimum"], do_print=False, save=False)
|
||||
|
||||
params["mass_genre_update"] = check_for_attribute(lib, "mass_genre_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=lib and "mass_genre_update" in lib)
|
||||
if self.OMDb is None and params["mass_genre_update"] == "omdb":
|
||||
params["mass_genre_update"] = None
|
||||
logger.error("Config Error: mass_genre_update cannot be omdb without a successful OMDb Connection")
|
||||
|
||||
params["mass_audience_rating_update"] = check_for_attribute(lib, "mass_audience_rating_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=lib and "mass_audience_rating_update" in lib)
|
||||
if self.OMDb is None and params["mass_audience_rating_update"] == "omdb":
|
||||
params["mass_audience_rating_update"] = None
|
||||
logger.error("Config Error: mass_audience_rating_update cannot be omdb without a successful OMDb Connection")
|
||||
|
||||
params["mass_critic_rating_update"] = check_for_attribute(lib, "mass_critic_rating_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=lib and "mass_audience_rating_update" in lib)
|
||||
if self.OMDb is None and params["mass_critic_rating_update"] == "omdb":
|
||||
params["mass_critic_rating_update"] = None
|
||||
logger.error("Config Error: mass_critic_rating_update cannot be omdb without a successful OMDb Connection")
|
||||
|
||||
params["mass_trakt_rating_update"] = check_for_attribute(lib, "mass_trakt_rating_update", var_type="bool", default=False, save=False, do_print=lib and "mass_trakt_rating_update" in lib)
|
||||
if self.Trakt is None and params["mass_trakt_rating_update"]:
|
||||
params["mass_trakt_rating_update"] = None
|
||||
logger.error("Config Error: mass_trakt_rating_update cannot run without a successful Trakt Connection")
|
||||
|
||||
params["split_duplicates"] = check_for_attribute(lib, "split_duplicates", var_type="bool", default=False, save=False, do_print=lib and "split_duplicates" in lib)
|
||||
params["radarr_add_all"] = check_for_attribute(lib, "radarr_add_all", var_type="bool", default=False, save=False, do_print=lib and "radarr_add_all" in lib)
|
||||
params["sonarr_add_all"] = check_for_attribute(lib, "sonarr_add_all", var_type="bool", default=False, save=False, do_print=lib and "sonarr_add_all" in lib)
|
||||
|
||||
try:
|
||||
if lib and "metadata_path" in lib:
|
||||
params["metadata_path"] = []
|
||||
if lib["metadata_path"] is None:
|
||||
raise Failed("Config Error: metadata_path attribute is blank")
|
||||
paths_to_check = lib["metadata_path"] if isinstance(lib["metadata_path"], list) else [lib["metadata_path"]]
|
||||
for path in paths_to_check:
|
||||
if isinstance(path, dict):
|
||||
def check_dict(attr, name):
|
||||
if attr in path:
|
||||
if path[attr] is None:
|
||||
logger.error(f"Config Error: metadata_path {attr} is blank")
|
||||
else:
|
||||
params["metadata_path"].append((name, path[attr]))
|
||||
check_dict("url", "URL")
|
||||
check_dict("git", "Git")
|
||||
check_dict("file", "File")
|
||||
check_dict("folder", "Folder")
|
||||
else:
|
||||
params["metadata_path"].append(("File", path))
|
||||
else:
|
||||
params["metadata_path"] = [("File", os.path.join(default_dir, f"{library_name}.yml"))]
|
||||
params["default_dir"] = default_dir
|
||||
params["plex"] = {
|
||||
"url": check_for_attribute(lib, "url", parent="plex", var_type="url", default=self.general["plex"]["url"], req_default=True, save=False),
|
||||
"token": check_for_attribute(lib, "token", parent="plex", default=self.general["plex"]["token"], req_default=True, save=False),
|
||||
"timeout": check_for_attribute(lib, "timeout", parent="plex", var_type="int", default=self.general["plex"]["timeout"], save=False),
|
||||
"clean_bundles": check_for_attribute(lib, "clean_bundles", parent="plex", var_type="bool", default=self.general["plex"]["clean_bundles"], save=False),
|
||||
"empty_trash": check_for_attribute(lib, "empty_trash", parent="plex", var_type="bool", default=self.general["plex"]["empty_trash"], save=False),
|
||||
"optimize": check_for_attribute(lib, "optimize", parent="plex", var_type="bool", default=self.general["plex"]["optimize"], save=False)
|
||||
for library_name, lib in libs.items():
|
||||
if self.requested_libraries and library_name not in self.requested_libraries:
|
||||
continue
|
||||
util.separator()
|
||||
params = {
|
||||
"mapping_name": str(library_name),
|
||||
"name": str(lib["library_name"]) if lib and "library_name" in lib and lib["library_name"] else str(library_name)
|
||||
}
|
||||
library = Plex(self, params)
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} Library Connection Successful")
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.info(f"{display_name} Library Connection Failed")
|
||||
continue
|
||||
display_name = f"{params['name']} ({params['mapping_name']})" if lib and "library_name" in lib and lib["library_name"] else params["mapping_name"]
|
||||
|
||||
if self.general["radarr"]["url"] or (lib and "radarr" in lib):
|
||||
logger.info("")
|
||||
util.separator("Radarr Configuration", space=False, border=False)
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} library's Radarr...")
|
||||
util.separator(f"{display_name} Configuration")
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} Library...")
|
||||
|
||||
params["asset_directory"] = check_for_attribute(lib, "asset_directory", parent="settings", var_type="list_path", default=self.general["asset_directory"], default_is_none=True, save=False)
|
||||
if params["asset_directory"] is None:
|
||||
logger.warning("Config Warning: Assets will not be used asset_directory attribute must be set under config or under this specific Library")
|
||||
|
||||
params["asset_folders"] = check_for_attribute(lib, "asset_folders", parent="settings", var_type="bool", default=self.general["asset_folders"], do_print=False, save=False)
|
||||
params["assets_for_all"] = check_for_attribute(lib, "assets_for_all", parent="settings", var_type="bool", default=self.general["assets_for_all"], do_print=False, save=False)
|
||||
params["sync_mode"] = check_for_attribute(lib, "sync_mode", parent="settings", test_list=sync_modes, default=self.general["sync_mode"], do_print=False, save=False)
|
||||
params["show_unmanaged"] = check_for_attribute(lib, "show_unmanaged", parent="settings", var_type="bool", default=self.general["show_unmanaged"], do_print=False, save=False)
|
||||
params["show_filtered"] = check_for_attribute(lib, "show_filtered", parent="settings", var_type="bool", default=self.general["show_filtered"], do_print=False, save=False)
|
||||
params["show_missing"] = check_for_attribute(lib, "show_missing", parent="settings", var_type="bool", default=self.general["show_missing"], do_print=False, save=False)
|
||||
params["save_missing"] = check_for_attribute(lib, "save_missing", parent="settings", var_type="bool", default=self.general["save_missing"], do_print=False, save=False)
|
||||
params["missing_only_released"] = check_for_attribute(lib, "missing_only_released", parent="settings", var_type="bool", default=self.general["missing_only_released"], do_print=False, save=False)
|
||||
params["create_asset_folders"] = check_for_attribute(lib, "create_asset_folders", parent="settings", var_type="bool", default=self.general["create_asset_folders"], do_print=False, save=False)
|
||||
params["collection_minimum"] = check_for_attribute(lib, "collection_minimum", parent="settings", var_type="int", default=self.general["collection_minimum"], do_print=False, save=False)
|
||||
params["delete_below_minimum"] = check_for_attribute(lib, "delete_below_minimum", parent="settings", var_type="bool", default=self.general["delete_below_minimum"], do_print=False, save=False)
|
||||
params["notifiarr_collection_creation"] = check_for_attribute(lib, "notifiarr_collection_creation", parent="settings", var_type="bool", default=self.general["notifiarr_collection_creation"], do_print=False, save=False)
|
||||
params["notifiarr_collection_addition"] = check_for_attribute(lib, "notifiarr_collection_addition", parent="settings", var_type="bool", default=self.general["notifiarr_collection_addition"], do_print=False, save=False)
|
||||
params["notifiarr_collection_removing"] = check_for_attribute(lib, "notifiarr_collection_removing", parent="settings", var_type="bool", default=self.general["notifiarr_collection_removing"], do_print=False, save=False)
|
||||
|
||||
params["mass_genre_update"] = check_for_attribute(lib, "mass_genre_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=lib and "mass_genre_update" in lib)
|
||||
if self.OMDb is None and params["mass_genre_update"] == "omdb":
|
||||
params["mass_genre_update"] = None
|
||||
e = "Config Error: mass_genre_update cannot be omdb without a successful OMDb Connection"
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
|
||||
params["mass_audience_rating_update"] = check_for_attribute(lib, "mass_audience_rating_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=lib and "mass_audience_rating_update" in lib)
|
||||
if self.OMDb is None and params["mass_audience_rating_update"] == "omdb":
|
||||
params["mass_audience_rating_update"] = None
|
||||
e = "Config Error: mass_audience_rating_update cannot be omdb without a successful OMDb Connection"
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
|
||||
params["mass_critic_rating_update"] = check_for_attribute(lib, "mass_critic_rating_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=lib and "mass_audience_rating_update" in lib)
|
||||
if self.OMDb is None and params["mass_critic_rating_update"] == "omdb":
|
||||
params["mass_critic_rating_update"] = None
|
||||
e = "Config Error: mass_critic_rating_update cannot be omdb without a successful OMDb Connection"
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
|
||||
params["mass_trakt_rating_update"] = check_for_attribute(lib, "mass_trakt_rating_update", var_type="bool", default=False, save=False, do_print=lib and "mass_trakt_rating_update" in lib)
|
||||
if self.Trakt is None and params["mass_trakt_rating_update"]:
|
||||
params["mass_trakt_rating_update"] = None
|
||||
e = "Config Error: mass_trakt_rating_update cannot run without a successful Trakt Connection"
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
|
||||
params["split_duplicates"] = check_for_attribute(lib, "split_duplicates", var_type="bool", default=False, save=False, do_print=lib and "split_duplicates" in lib)
|
||||
params["radarr_add_all"] = check_for_attribute(lib, "radarr_add_all", var_type="bool", default=False, save=False, do_print=lib and "radarr_add_all" in lib)
|
||||
params["sonarr_add_all"] = check_for_attribute(lib, "sonarr_add_all", var_type="bool", default=False, save=False, do_print=lib and "sonarr_add_all" in lib)
|
||||
|
||||
try:
|
||||
library.Radarr = Radarr(self, {
|
||||
"url": check_for_attribute(lib, "url", parent="radarr", var_type="url", default=self.general["radarr"]["url"], req_default=True, save=False),
|
||||
"token": check_for_attribute(lib, "token", parent="radarr", default=self.general["radarr"]["token"], req_default=True, save=False),
|
||||
"add": check_for_attribute(lib, "add", parent="radarr", var_type="bool", default=self.general["radarr"]["add"], save=False),
|
||||
"add_existing": check_for_attribute(lib, "add_existing", parent="radarr", var_type="bool", default=self.general["radarr"]["add_existing"], save=False),
|
||||
"root_folder_path": check_for_attribute(lib, "root_folder_path", parent="radarr", default=self.general["radarr"]["root_folder_path"], req_default=True, save=False),
|
||||
"monitor": check_for_attribute(lib, "monitor", parent="radarr", var_type="bool", default=self.general["radarr"]["monitor"], save=False),
|
||||
"availability": check_for_attribute(lib, "availability", parent="radarr", test_list=radarr.availability_descriptions, default=self.general["radarr"]["availability"], save=False),
|
||||
"quality_profile": check_for_attribute(lib, "quality_profile", parent="radarr",default=self.general["radarr"]["quality_profile"], req_default=True, save=False),
|
||||
"tag": check_for_attribute(lib, "tag", parent="radarr", var_type="lower_list", default=self.general["radarr"]["tag"], default_is_none=True, save=False),
|
||||
"search": check_for_attribute(lib, "search", parent="radarr", var_type="bool", default=self.general["radarr"]["search"], save=False)
|
||||
})
|
||||
if lib and "metadata_path" in lib:
|
||||
params["metadata_path"] = []
|
||||
if lib["metadata_path"] is None:
|
||||
raise Failed("Config Error: metadata_path attribute is blank")
|
||||
paths_to_check = lib["metadata_path"] if isinstance(lib["metadata_path"], list) else [lib["metadata_path"]]
|
||||
for path in paths_to_check:
|
||||
if isinstance(path, dict):
|
||||
def check_dict(attr, name):
|
||||
if attr in path:
|
||||
if path[attr] is None:
|
||||
e = f"Config Error: metadata_path {attr} is blank"
|
||||
self.errors.append(e)
|
||||
logger.error(e)
|
||||
else:
|
||||
params["metadata_path"].append((name, path[attr]))
|
||||
check_dict("url", "URL")
|
||||
check_dict("git", "Git")
|
||||
check_dict("file", "File")
|
||||
check_dict("folder", "Folder")
|
||||
else:
|
||||
params["metadata_path"].append(("File", path))
|
||||
else:
|
||||
params["metadata_path"] = [("File", os.path.join(default_dir, f"{library_name}.yml"))]
|
||||
params["default_dir"] = default_dir
|
||||
params["plex"] = {
|
||||
"url": check_for_attribute(lib, "url", parent="plex", var_type="url", default=self.general["plex"]["url"], req_default=True, save=False),
|
||||
"token": check_for_attribute(lib, "token", parent="plex", default=self.general["plex"]["token"], req_default=True, save=False),
|
||||
"timeout": check_for_attribute(lib, "timeout", parent="plex", var_type="int", default=self.general["plex"]["timeout"], save=False),
|
||||
"clean_bundles": check_for_attribute(lib, "clean_bundles", parent="plex", var_type="bool", default=self.general["plex"]["clean_bundles"], save=False),
|
||||
"empty_trash": check_for_attribute(lib, "empty_trash", parent="plex", var_type="bool", default=self.general["plex"]["empty_trash"], save=False),
|
||||
"optimize": check_for_attribute(lib, "optimize", parent="plex", var_type="bool", default=self.general["plex"]["optimize"], save=False)
|
||||
}
|
||||
library = Plex(self, params)
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} Library Connection Successful")
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.info(f"{display_name} Library Connection Failed")
|
||||
continue
|
||||
|
||||
if self.general["radarr"]["url"] or (lib and "radarr" in lib):
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} library's Radarr Connection {'Failed' if library.Radarr is None else 'Successful'}")
|
||||
|
||||
if self.general["sonarr"]["url"] or (lib and "sonarr" in lib):
|
||||
logger.info("")
|
||||
util.separator("Sonarr Configuration", space=False, border=False)
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} library's Sonarr...")
|
||||
logger.info("")
|
||||
try:
|
||||
library.Sonarr = Sonarr(self, {
|
||||
"url": check_for_attribute(lib, "url", parent="sonarr", var_type="url", default=self.general["sonarr"]["url"], req_default=True, save=False),
|
||||
"token": check_for_attribute(lib, "token", parent="sonarr", default=self.general["sonarr"]["token"], req_default=True, save=False),
|
||||
"add": check_for_attribute(lib, "add", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add"], save=False),
|
||||
"add_existing": check_for_attribute(lib, "add_existing", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add_existing"], save=False),
|
||||
"root_folder_path": check_for_attribute(lib, "root_folder_path", parent="sonarr", default=self.general["sonarr"]["root_folder_path"], req_default=True, save=False),
|
||||
"monitor": check_for_attribute(lib, "monitor", parent="sonarr", test_list=sonarr.monitor_descriptions, default=self.general["sonarr"]["monitor"], save=False),
|
||||
"quality_profile": check_for_attribute(lib, "quality_profile", parent="sonarr", default=self.general["sonarr"]["quality_profile"], req_default=True, save=False),
|
||||
"language_profile": check_for_attribute(lib, "language_profile", parent="sonarr", default=self.general["sonarr"]["language_profile"], save=False) if self.general["sonarr"]["language_profile"] else check_for_attribute(lib, "language_profile", parent="sonarr", default_is_none=True, save=False),
|
||||
"series_type": check_for_attribute(lib, "series_type", parent="sonarr", test_list=sonarr.series_type_descriptions, default=self.general["sonarr"]["series_type"], save=False),
|
||||
"season_folder": check_for_attribute(lib, "season_folder", parent="sonarr", var_type="bool", default=self.general["sonarr"]["season_folder"], save=False),
|
||||
"tag": check_for_attribute(lib, "tag", parent="sonarr", var_type="lower_list", default=self.general["sonarr"]["tag"], default_is_none=True, save=False),
|
||||
"search": check_for_attribute(lib, "search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["search"], save=False),
|
||||
"cutoff_search": check_for_attribute(lib, "cutoff_search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["cutoff_search"], save=False)
|
||||
})
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
util.separator("Radarr Configuration", space=False, border=False)
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} library's Sonarr Connection {'Failed' if library.Sonarr is None else 'Successful'}")
|
||||
|
||||
if self.general["tautulli"]["url"] or (lib and "tautulli" in lib):
|
||||
logger.info("")
|
||||
util.separator("Tautulli Configuration", space=False, border=False)
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} library's Tautulli...")
|
||||
logger.info("")
|
||||
try:
|
||||
library.Tautulli = Tautulli(self, {
|
||||
"url": check_for_attribute(lib, "url", parent="tautulli", var_type="url", default=self.general["tautulli"]["url"], req_default=True, save=False),
|
||||
"apikey": check_for_attribute(lib, "apikey", parent="tautulli", default=self.general["tautulli"]["apikey"], req_default=True, save=False)
|
||||
})
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.info(f"Connecting to {display_name} library's Radarr...")
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} library's Tautulli Connection {'Failed' if library.Tautulli is None else 'Successful'}")
|
||||
try:
|
||||
library.Radarr = Radarr(self, {
|
||||
"url": check_for_attribute(lib, "url", parent="radarr", var_type="url", default=self.general["radarr"]["url"], req_default=True, save=False),
|
||||
"token": check_for_attribute(lib, "token", parent="radarr", default=self.general["radarr"]["token"], req_default=True, save=False),
|
||||
"add": check_for_attribute(lib, "add", parent="radarr", var_type="bool", default=self.general["radarr"]["add"], save=False),
|
||||
"add_existing": check_for_attribute(lib, "add_existing", parent="radarr", var_type="bool", default=self.general["radarr"]["add_existing"], save=False),
|
||||
"root_folder_path": check_for_attribute(lib, "root_folder_path", parent="radarr", default=self.general["radarr"]["root_folder_path"], req_default=True, save=False),
|
||||
"monitor": check_for_attribute(lib, "monitor", parent="radarr", var_type="bool", default=self.general["radarr"]["monitor"], save=False),
|
||||
"availability": check_for_attribute(lib, "availability", parent="radarr", test_list=radarr.availability_descriptions, default=self.general["radarr"]["availability"], save=False),
|
||||
"quality_profile": check_for_attribute(lib, "quality_profile", parent="radarr",default=self.general["radarr"]["quality_profile"], req_default=True, save=False),
|
||||
"tag": check_for_attribute(lib, "tag", parent="radarr", var_type="lower_list", default=self.general["radarr"]["tag"], default_is_none=True, save=False),
|
||||
"search": check_for_attribute(lib, "search", parent="radarr", var_type="bool", default=self.general["radarr"]["search"], save=False)
|
||||
})
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} library's Radarr Connection {'Failed' if library.Radarr is None else 'Successful'}")
|
||||
|
||||
logger.info("")
|
||||
self.libraries.append(library)
|
||||
if self.general["sonarr"]["url"] or (lib and "sonarr" in lib):
|
||||
logger.info("")
|
||||
util.separator("Sonarr Configuration", space=False, border=False)
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} library's Sonarr...")
|
||||
logger.info("")
|
||||
try:
|
||||
library.Sonarr = Sonarr(self, {
|
||||
"url": check_for_attribute(lib, "url", parent="sonarr", var_type="url", default=self.general["sonarr"]["url"], req_default=True, save=False),
|
||||
"token": check_for_attribute(lib, "token", parent="sonarr", default=self.general["sonarr"]["token"], req_default=True, save=False),
|
||||
"add": check_for_attribute(lib, "add", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add"], save=False),
|
||||
"add_existing": check_for_attribute(lib, "add_existing", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add_existing"], save=False),
|
||||
"root_folder_path": check_for_attribute(lib, "root_folder_path", parent="sonarr", default=self.general["sonarr"]["root_folder_path"], req_default=True, save=False),
|
||||
"monitor": check_for_attribute(lib, "monitor", parent="sonarr", test_list=sonarr.monitor_descriptions, default=self.general["sonarr"]["monitor"], save=False),
|
||||
"quality_profile": check_for_attribute(lib, "quality_profile", parent="sonarr", default=self.general["sonarr"]["quality_profile"], req_default=True, save=False),
|
||||
"language_profile": check_for_attribute(lib, "language_profile", parent="sonarr", default=self.general["sonarr"]["language_profile"], save=False) if self.general["sonarr"]["language_profile"] else check_for_attribute(lib, "language_profile", parent="sonarr", default_is_none=True, save=False),
|
||||
"series_type": check_for_attribute(lib, "series_type", parent="sonarr", test_list=sonarr.series_type_descriptions, default=self.general["sonarr"]["series_type"], save=False),
|
||||
"season_folder": check_for_attribute(lib, "season_folder", parent="sonarr", var_type="bool", default=self.general["sonarr"]["season_folder"], save=False),
|
||||
"tag": check_for_attribute(lib, "tag", parent="sonarr", var_type="lower_list", default=self.general["sonarr"]["tag"], default_is_none=True, save=False),
|
||||
"search": check_for_attribute(lib, "search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["search"], save=False),
|
||||
"cutoff_search": check_for_attribute(lib, "cutoff_search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["cutoff_search"], save=False)
|
||||
})
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} library's Sonarr Connection {'Failed' if library.Sonarr is None else 'Successful'}")
|
||||
|
||||
util.separator()
|
||||
if self.general["tautulli"]["url"] or (lib and "tautulli" in lib):
|
||||
logger.info("")
|
||||
util.separator("Tautulli Configuration", space=False, border=False)
|
||||
logger.info("")
|
||||
logger.info(f"Connecting to {display_name} library's Tautulli...")
|
||||
logger.info("")
|
||||
try:
|
||||
library.Tautulli = Tautulli(self, {
|
||||
"url": check_for_attribute(lib, "url", parent="tautulli", var_type="url", default=self.general["tautulli"]["url"], req_default=True, save=False),
|
||||
"apikey": check_for_attribute(lib, "apikey", parent="tautulli", default=self.general["tautulli"]["apikey"], req_default=True, save=False)
|
||||
})
|
||||
except Failed as e:
|
||||
self.errors.append(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.info("")
|
||||
logger.info(f"{display_name} library's Tautulli Connection {'Failed' if library.Tautulli is None else 'Successful'}")
|
||||
|
||||
if len(self.libraries) > 0:
|
||||
logger.info(f"{len(self.libraries)} Plex Library Connection{'s' if len(self.libraries) > 1 else ''} Successful")
|
||||
else:
|
||||
raise Failed("Plex Error: No Plex libraries were connected to")
|
||||
library.Notifiarr = self.NotifiarrFactory.getNotifiarr(library) if self.NotifiarrFactory else None
|
||||
|
||||
util.separator()
|
||||
logger.info("")
|
||||
self.libraries.append(library)
|
||||
|
||||
util.separator()
|
||||
|
||||
if len(self.libraries) > 0:
|
||||
logger.info(f"{len(self.libraries)} Plex Library Connection{'s' if len(self.libraries) > 1 else ''} Successful")
|
||||
else:
|
||||
raise Failed("Plex Error: No Plex libraries were connected to")
|
||||
|
||||
util.separator()
|
||||
|
||||
if self.errors:
|
||||
self.notify(self.errors)
|
||||
except Exception as e:
|
||||
self.notify(e)
|
||||
raise
|
||||
|
||||
def notify(self, text, library=None, collection=None, critical=True):
|
||||
if self.NotifiarrFactory:
|
||||
if not isinstance(text, list):
|
||||
text = [text]
|
||||
for t in text:
|
||||
self.NotifiarrFactory.error(t, library=library, collection=collection, critical=critical)
|
||||
|
||||
def get_html(self, url, headers=None, params=None):
|
||||
return html.fromstring(self.get(url, headers=headers, params=params).content)
|
||||
|
||||
def get_json(self, url, headers=None):
|
||||
return self.get(url, headers=headers).json()
|
||||
def get_json(self, url, json=None, headers=None, params=None):
|
||||
return self.get(url, json=json, headers=headers, params=params).json()
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000)
|
||||
def get(self, url, headers=None, params=None):
|
||||
return self.session.get(url, headers=headers, params=params)
|
||||
def get(self, url, json=None, headers=None, params=None):
|
||||
return self.session.get(url, json=json, headers=headers, params=params)
|
||||
|
||||
def get_image_encoded(self, url):
|
||||
return base64.b64encode(self.get(url).content).decode('utf-8')
|
||||
|
||||
def post_html(self, url, data=None, json=None, headers=None):
|
||||
return html.fromstring(self.post(url, data=data, json=json, headers=headers).content)
|
||||
|
|
|
@ -57,7 +57,7 @@ class IMDb:
|
|||
pass
|
||||
if total > 0:
|
||||
return total, item_counts[page_type]
|
||||
raise ValueError(f"IMDb Error: Failed to parse URL: {imdb_url}")
|
||||
raise Failed(f"IMDb Error: Failed to parse URL: {imdb_url}")
|
||||
|
||||
def _ids_from_url(self, imdb_url, language, limit):
|
||||
total, item_count = self._total(imdb_url, language)
|
||||
|
@ -93,7 +93,7 @@ class IMDb:
|
|||
if len(imdb_ids) > 0:
|
||||
logger.debug(f"{len(imdb_ids)} IMDb IDs Found: {imdb_ids}")
|
||||
return imdb_ids
|
||||
raise ValueError(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
|
||||
raise Failed(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
|
||||
|
||||
def get_imdb_ids(self, method, data, language):
|
||||
if method == "imdb_id":
|
||||
|
|
|
@ -13,6 +13,7 @@ class Library(ABC):
|
|||
self.Radarr = None
|
||||
self.Sonarr = None
|
||||
self.Tautulli = None
|
||||
self.Notifiarr = None
|
||||
self.collections = []
|
||||
self.metadatas = []
|
||||
self.metadata_files = []
|
||||
|
@ -54,6 +55,9 @@ class Library(ABC):
|
|||
self.sonarr_add_all = params["sonarr_add_all"]
|
||||
self.collection_minimum = params["collection_minimum"]
|
||||
self.delete_below_minimum = params["delete_below_minimum"]
|
||||
self.notifiarr_collection_creation = params["notifiarr_collection_creation"]
|
||||
self.notifiarr_collection_addition = params["notifiarr_collection_addition"]
|
||||
self.notifiarr_collection_removing = params["notifiarr_collection_removing"]
|
||||
self.split_duplicates = params["split_duplicates"] # TODO: Here or just in Plex?
|
||||
self.clean_bundles = params["plex"]["clean_bundles"] # TODO: Here or just in Plex?
|
||||
self.empty_trash = params["plex"]["empty_trash"] # TODO: Here or just in Plex?
|
||||
|
@ -175,6 +179,9 @@ class Library(ABC):
|
|||
if background_uploaded:
|
||||
self.config.Cache.update_image_map(item.ratingKey, f"{self.image_table_name}_backgrounds", item.art, background.compare)
|
||||
|
||||
def notify(self, text, collection=None, critical=True):
|
||||
self.config.notify(text, library=self, collection=collection, critical=critical)
|
||||
|
||||
@abstractmethod
|
||||
def _upload_image(self, item, image):
|
||||
pass
|
||||
|
|
|
@ -249,6 +249,7 @@ class Metadata:
|
|||
add_edit("originally_available", item, meta, methods, key="originallyAvailableAt", value=originally_available, var_type="date")
|
||||
add_edit("critic_rating", item, meta, methods, value=rating, key="rating", var_type="float")
|
||||
add_edit("audience_rating", item, meta, methods, key="audienceRating", var_type="float")
|
||||
add_edit("user_rating", item, meta, methods, key="userRating", var_type="float")
|
||||
add_edit("content_rating", item, meta, methods, key="contentRating")
|
||||
add_edit("original_title", item, meta, methods, key="originalTitle", value=original_title)
|
||||
add_edit("studio", item, meta, methods, value=studio)
|
||||
|
|
76
modules/notifiarr.py
Normal file
76
modules/notifiarr.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
import logging
|
||||
|
||||
from modules.util import Failed
|
||||
|
||||
logger = logging.getLogger("Plex Meta Manager")
|
||||
|
||||
base_url = "https://notifiarr.com/api/v1/"
|
||||
dev_url = "https://dev.notifiarr.com/api/v1/"
|
||||
|
||||
class NotifiarrBase:
|
||||
def __init__(self, config, apikey, develop, test, error_notification):
|
||||
self.config = config
|
||||
self.apikey = apikey
|
||||
self.develop = develop
|
||||
self.test = test
|
||||
self.error_notification = error_notification
|
||||
|
||||
def _request(self, path, json=None, params=None):
|
||||
url = f"{dev_url if self.develop else base_url}" + \
|
||||
("notification/test" if self.test else f"{path}{self.apikey}")
|
||||
logger.debug(url)
|
||||
response = self.config.get(url, json=json, params={"event": "pmm"} if self.test else params)
|
||||
response_json = response.json()
|
||||
if self.develop or self.test:
|
||||
logger.debug(json)
|
||||
logger.debug("")
|
||||
logger.debug(response_json)
|
||||
if response.status_code >= 400 or ("response" in response_json and response_json["response"] == "error"):
|
||||
raise Failed(f"({response.status_code} [{response.reason}]) {response_json}")
|
||||
return response_json
|
||||
|
||||
def error(self, text, library=None, collection=None, critical=True):
|
||||
if self.error_notification:
|
||||
json = {"error": str(text), "critical": critical}
|
||||
if library:
|
||||
json["server_name"] = library.PlexServer.friendlyName
|
||||
json["library_name"] = library.name
|
||||
if collection:
|
||||
json["collection"] = str(collection)
|
||||
self._request("notification/plex/", json=json, params={"event": "collections"})
|
||||
|
||||
class NotifiarrFactory(NotifiarrBase):
|
||||
def __init__(self, config, params):
|
||||
super().__init__(config, params["apikey"], params["develop"], params["test"], params["error_notification"])
|
||||
if not params["test"] and not self._request("user/validate/")["message"]["response"]:
|
||||
raise Failed("Notifiarr Error: Invalid apikey")
|
||||
|
||||
def getNotifiarr(self, library):
|
||||
return Notifiarr(self.config, library, self.apikey, self.develop, self.test, self.error_notification)
|
||||
|
||||
class Notifiarr(NotifiarrBase):
|
||||
def __init__(self, config, library, apikey, develop, test, error_notification):
|
||||
super().__init__(config, apikey, develop, test, error_notification)
|
||||
self.library = library
|
||||
|
||||
def plex_collection(self, collection, created=False, additions=None, removals=None):
|
||||
thumb = None
|
||||
if collection.thumb and next((f for f in collection.fields if f.name == "thumb"), None):
|
||||
thumb = self.config.get_image_encoded(f"{self.library.url}{collection.thumb}?X-Plex-Token={self.library.token}")
|
||||
art = None
|
||||
if collection.art and next((f for f in collection.fields if f.name == "art"), None):
|
||||
art = self.config.get_image_encoded(f"{self.library.url}{collection.art}?X-Plex-Token={self.library.token}")
|
||||
json = {
|
||||
"server_name": self.library.PlexServer.friendlyName,
|
||||
"library_name": self.library.name,
|
||||
"type": "movie" if self.library.is_movie else "show",
|
||||
"collection": collection.title,
|
||||
"created": created,
|
||||
"poster": thumb,
|
||||
"background": art
|
||||
}
|
||||
if additions:
|
||||
json["additions"] = additions
|
||||
if removals:
|
||||
json["removals"] = removals
|
||||
self._request("notification/plex/", json=json, params={"event": "collections"})
|
|
@ -56,9 +56,12 @@ class TMDb:
|
|||
self.TMDb = tmdbv3api.TMDb(session=self.config.session)
|
||||
self.TMDb.api_key = params["apikey"]
|
||||
self.TMDb.language = params["language"]
|
||||
response = tmdbv3api.Configuration().info()
|
||||
if hasattr(response, "status_message"):
|
||||
raise Failed(f"TMDb Error: {response.status_message}")
|
||||
try:
|
||||
response = tmdbv3api.Configuration().info()
|
||||
if hasattr(response, "status_message"):
|
||||
raise Failed(f"TMDb Error: {response.status_message}")
|
||||
except TMDbException as e:
|
||||
raise Failed(f"TMDb Error: {e}")
|
||||
self.apikey = params["apikey"]
|
||||
self.language = params["language"]
|
||||
self.Movie = tmdbv3api.Movie()
|
||||
|
|
|
@ -29,6 +29,9 @@ class ImageData:
|
|||
self.compare = location if is_url else os.stat(location).st_size
|
||||
self.message = f"{prefix}{'poster' if is_poster else 'background'} to [{'URL' if is_url else 'File'}] {location}"
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__dict__)
|
||||
|
||||
def retry_if_not_failed(exception):
|
||||
return not isinstance(exception, Failed)
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ logger.addHandler(cmd_handler)
|
|||
|
||||
sys.excepthook = util.my_except_hook
|
||||
|
||||
def start(config_path, is_test=False, time_scheduled=None, requested_collections=None, requested_libraries=None, resume_from=None):
|
||||
def start(attrs):
|
||||
file_logger = os.path.join(default_dir, "logs", "meta.log")
|
||||
should_roll_over = os.path.isfile(file_logger)
|
||||
file_handler = RotatingFileHandler(file_logger, delay=True, mode="w", backupCount=10, encoding="utf-8")
|
||||
|
@ -115,107 +115,117 @@ def start(config_path, is_test=False, time_scheduled=None, requested_collections
|
|||
logger.info(util.centered("| __/| | __/> < | | | | __/ || (_| | | | | | (_| | | | | (_| | (_| | __/ | "))
|
||||
logger.info(util.centered("|_| |_|\\___/_/\\_\\ |_| |_|\\___|\\__\\__,_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_| "))
|
||||
logger.info(util.centered(" |___/ "))
|
||||
logger.info(util.centered(" Version: 1.12.2-develop0930 "))
|
||||
if time_scheduled: start_type = f"{time_scheduled} "
|
||||
elif is_test: start_type = "Test "
|
||||
elif requested_collections: start_type = "Collections "
|
||||
elif requested_libraries: start_type = "Libraries "
|
||||
logger.info(util.centered(" Version: 1.12.2-develop1004 "))
|
||||
if "time" in attrs: start_type = f"{attrs['time']} "
|
||||
elif "test" in attrs: start_type = "Test "
|
||||
elif "collections" in attrs: start_type = "Collections "
|
||||
elif "libraries" in attrs: start_type = "Libraries "
|
||||
else: start_type = ""
|
||||
start_time = datetime.now()
|
||||
if time_scheduled is None:
|
||||
time_scheduled = start_time.strftime("%H:%M")
|
||||
if "time" not in attrs:
|
||||
attrs["time"] = start_time.strftime("%H:%M")
|
||||
util.separator(f"Starting {start_type}Run")
|
||||
try:
|
||||
config = Config(default_dir, config_path=config_path, is_test=is_test,
|
||||
time_scheduled=time_scheduled, requested_collections=requested_collections,
|
||||
requested_libraries=requested_libraries, resume_from=resume_from)
|
||||
update_libraries(config)
|
||||
config = Config(default_dir, attrs)
|
||||
except Exception as e:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, critical=True)
|
||||
else:
|
||||
try:
|
||||
update_libraries(config)
|
||||
except Exception as e:
|
||||
config.notify(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, critical=True)
|
||||
logger.info("")
|
||||
util.separator(f"Finished {start_type}Run\nRun Time: {str(datetime.now() - start_time).split('.')[0]}")
|
||||
logger.removeHandler(file_handler)
|
||||
|
||||
def update_libraries(config):
|
||||
for library in config.libraries:
|
||||
os.makedirs(os.path.join(default_dir, "logs", library.mapping_name, "collections"), exist_ok=True)
|
||||
col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, "library.log")
|
||||
should_roll_over = os.path.isfile(col_file_logger)
|
||||
library_handler = RotatingFileHandler(col_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8")
|
||||
util.apply_formatter(library_handler)
|
||||
if should_roll_over:
|
||||
library_handler.doRollover()
|
||||
logger.addHandler(library_handler)
|
||||
try:
|
||||
os.makedirs(os.path.join(default_dir, "logs", library.mapping_name, "collections"), exist_ok=True)
|
||||
col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, "library.log")
|
||||
should_roll_over = os.path.isfile(col_file_logger)
|
||||
library_handler = RotatingFileHandler(col_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8")
|
||||
util.apply_formatter(library_handler)
|
||||
if should_roll_over:
|
||||
library_handler.doRollover()
|
||||
logger.addHandler(library_handler)
|
||||
|
||||
os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
|
||||
logger.info("")
|
||||
util.separator(f"{library.name} Library")
|
||||
items = None
|
||||
if not library.is_other:
|
||||
os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
|
||||
logger.info("")
|
||||
util.separator(f"Mapping {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
items = library.map_guids()
|
||||
if not config.test_mode and not config.resume_from and not collection_only and library.mass_update:
|
||||
mass_metadata(config, library, items=items)
|
||||
for metadata in library.metadata_files:
|
||||
logger.info("")
|
||||
util.separator(f"Running Metadata File\n{metadata.path}")
|
||||
if not config.test_mode and not config.resume_from and not collection_only:
|
||||
try:
|
||||
metadata.update_metadata()
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
collections_to_run = metadata.get_collections(config.requested_collections)
|
||||
if config.resume_from and config.resume_from not in collections_to_run:
|
||||
util.separator(f"{library.name} Library")
|
||||
items = None
|
||||
if not library.is_other:
|
||||
logger.info("")
|
||||
logger.warning(f"Collection: {config.resume_from} not in Metadata File: {metadata.path}")
|
||||
continue
|
||||
if collections_to_run and not library_only:
|
||||
util.separator(f"Mapping {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
util.separator(f"{'Test ' if config.test_mode else ''}Collections")
|
||||
logger.removeHandler(library_handler)
|
||||
run_collection(config, library, metadata, collections_to_run)
|
||||
logger.addHandler(library_handler)
|
||||
if library.run_sort:
|
||||
logger.info("")
|
||||
util.separator(f"Sorting {library.name} Library's Collections", space=False, border=False)
|
||||
logger.info("")
|
||||
for builder in library.run_sort:
|
||||
items = library.map_guids()
|
||||
if not config.test_mode and not config.resume_from and not collection_only and library.mass_update:
|
||||
mass_metadata(config, library, items=items)
|
||||
for metadata in library.metadata_files:
|
||||
logger.info("")
|
||||
util.separator(f"Sorting {builder.name} Collection", space=False, border=False)
|
||||
util.separator(f"Running Metadata File\n{metadata.path}")
|
||||
if not config.test_mode and not config.resume_from and not collection_only:
|
||||
try:
|
||||
metadata.update_metadata()
|
||||
except Failed as e:
|
||||
library.notify(e)
|
||||
logger.error(e)
|
||||
collections_to_run = metadata.get_collections(config.requested_collections)
|
||||
if config.resume_from and config.resume_from not in collections_to_run:
|
||||
logger.info("")
|
||||
logger.warning(f"Collection: {config.resume_from} not in Metadata File: {metadata.path}")
|
||||
continue
|
||||
if collections_to_run and not library_only:
|
||||
logger.info("")
|
||||
util.separator(f"{'Test ' if config.test_mode else ''}Collections")
|
||||
logger.removeHandler(library_handler)
|
||||
run_collection(config, library, metadata, collections_to_run)
|
||||
logger.addHandler(library_handler)
|
||||
if library.run_sort:
|
||||
logger.info("")
|
||||
builder.sort_collection()
|
||||
util.separator(f"Sorting {library.name} Library's Collections", space=False, border=False)
|
||||
logger.info("")
|
||||
for builder in library.run_sort:
|
||||
logger.info("")
|
||||
util.separator(f"Sorting {builder.name} Collection", space=False, border=False)
|
||||
logger.info("")
|
||||
builder.sort_collection()
|
||||
|
||||
if not config.test_mode and not config.requested_collections and ((library.show_unmanaged and not library_only) or (library.assets_for_all and not collection_only)):
|
||||
logger.info("")
|
||||
util.separator(f"Other {library.name} Library Operations")
|
||||
unmanaged_collections = []
|
||||
for col in library.get_all_collections():
|
||||
if col.title not in library.collections:
|
||||
unmanaged_collections.append(col)
|
||||
if not config.test_mode and not config.requested_collections and ((library.show_unmanaged and not library_only) or (library.assets_for_all and not collection_only)):
|
||||
logger.info("")
|
||||
util.separator(f"Other {library.name} Library Operations")
|
||||
unmanaged_collections = []
|
||||
for col in library.get_all_collections():
|
||||
if col.title not in library.collections:
|
||||
unmanaged_collections.append(col)
|
||||
|
||||
if library.show_unmanaged and not library_only:
|
||||
logger.info("")
|
||||
util.separator(f"Unmanaged Collections in {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
for col in unmanaged_collections:
|
||||
logger.info(col.title)
|
||||
logger.info("")
|
||||
logger.info(f"{len(unmanaged_collections)} Unmanaged Collections")
|
||||
if library.show_unmanaged and not library_only:
|
||||
logger.info("")
|
||||
util.separator(f"Unmanaged Collections in {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
for col in unmanaged_collections:
|
||||
logger.info(col.title)
|
||||
logger.info("")
|
||||
logger.info(f"{len(unmanaged_collections)} Unmanaged Collections")
|
||||
|
||||
if library.assets_for_all and not collection_only:
|
||||
logger.info("")
|
||||
util.separator(f"All {library.type}s Assets Check for {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
for col in unmanaged_collections:
|
||||
poster, background = library.find_collection_assets(col, create=library.create_asset_folders)
|
||||
library.upload_images(col, poster=poster, background=background)
|
||||
for item in library.get_all():
|
||||
library.update_item_from_assets(item, create=library.create_asset_folders)
|
||||
if library.assets_for_all and not collection_only:
|
||||
logger.info("")
|
||||
util.separator(f"All {library.type}s Assets Check for {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
for col in unmanaged_collections:
|
||||
poster, background = library.find_collection_assets(col, create=library.create_asset_folders)
|
||||
library.upload_images(col, poster=poster, background=background)
|
||||
for item in library.get_all():
|
||||
library.update_item_from_assets(item, create=library.create_asset_folders)
|
||||
|
||||
logger.removeHandler(library_handler)
|
||||
logger.removeHandler(library_handler)
|
||||
except Exception as e:
|
||||
library.notify(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, critical=True)
|
||||
|
||||
has_run_again = False
|
||||
for library in config.libraries:
|
||||
|
@ -234,26 +244,32 @@ def update_libraries(config):
|
|||
util.print_end()
|
||||
for library in config.libraries:
|
||||
if library.run_again:
|
||||
col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, f"library.log")
|
||||
library_handler = RotatingFileHandler(col_file_logger, mode="w", backupCount=3, encoding="utf-8")
|
||||
util.apply_formatter(library_handler)
|
||||
logger.addHandler(library_handler)
|
||||
library_handler.addFilter(fmt_filter)
|
||||
os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
|
||||
logger.info("")
|
||||
util.separator(f"{library.name} Library Run Again")
|
||||
logger.info("")
|
||||
library.map_guids()
|
||||
for builder in library.run_again:
|
||||
try:
|
||||
col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, f"library.log")
|
||||
library_handler = RotatingFileHandler(col_file_logger, mode="w", backupCount=3, encoding="utf-8")
|
||||
util.apply_formatter(library_handler)
|
||||
logger.addHandler(library_handler)
|
||||
library_handler.addFilter(fmt_filter)
|
||||
os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
|
||||
logger.info("")
|
||||
util.separator(f"{builder.name} Collection")
|
||||
util.separator(f"{library.name} Library Run Again")
|
||||
logger.info("")
|
||||
try:
|
||||
builder.run_collections_again()
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.removeHandler(library_handler)
|
||||
library.map_guids()
|
||||
for builder in library.run_again:
|
||||
logger.info("")
|
||||
util.separator(f"{builder.name} Collection")
|
||||
logger.info("")
|
||||
try:
|
||||
builder.run_collections_again()
|
||||
except Failed as e:
|
||||
library.notify(e, collection=builder.name, critical=False)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
logger.removeHandler(library_handler)
|
||||
except Exception as e:
|
||||
library.notify(e)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, critical=True)
|
||||
|
||||
used_url = []
|
||||
for library in config.libraries:
|
||||
|
@ -457,7 +473,7 @@ def run_collection(config, library, metadata, requested_collections):
|
|||
collection_log_name, output_str = util.validate_filename(mapping_name)
|
||||
collection_log_folder = os.path.join(default_dir, "logs", library.mapping_name, "collections", collection_log_name)
|
||||
os.makedirs(collection_log_folder, exist_ok=True)
|
||||
col_file_logger = os.path.join(collection_log_folder, f"collection.log")
|
||||
col_file_logger = os.path.join(collection_log_folder, "collection.log")
|
||||
should_roll_over = os.path.isfile(col_file_logger)
|
||||
collection_handler = RotatingFileHandler(col_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8")
|
||||
util.apply_formatter(collection_handler)
|
||||
|
@ -533,6 +549,8 @@ def run_collection(config, library, metadata, requested_collections):
|
|||
library.run_sort.append(builder)
|
||||
# builder.sort_collection()
|
||||
|
||||
builder.send_notifications()
|
||||
|
||||
if builder.item_details and run_item_details:
|
||||
try:
|
||||
builder.load_collection_items()
|
||||
|
@ -546,9 +564,11 @@ def run_collection(config, library, metadata, requested_collections):
|
|||
library.run_again.append(builder)
|
||||
|
||||
except Failed as e:
|
||||
library.notify(e, collection=mapping_name)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
except Exception as e:
|
||||
library.notify(f"Unknown Error: {e}", collection=mapping_name)
|
||||
util.print_stacktrace()
|
||||
logger.error(f"Unknown Error: {e}")
|
||||
logger.info("")
|
||||
|
@ -557,7 +577,13 @@ def run_collection(config, library, metadata, requested_collections):
|
|||
|
||||
try:
|
||||
if run or test or collections or libraries or resume:
|
||||
start(config_file, is_test=test, requested_collections=collections, requested_libraries=libraries, resume_from=resume)
|
||||
start({
|
||||
"config_file": config_file,
|
||||
"test": test,
|
||||
"collections": collections,
|
||||
"libraries": libraries,
|
||||
"resume": resume
|
||||
})
|
||||
else:
|
||||
times_to_run = util.get_list(times)
|
||||
valid_times = []
|
||||
|
@ -570,7 +596,7 @@ try:
|
|||
else:
|
||||
raise Failed(f"Argument Error: blank time argument")
|
||||
for time_to_run in valid_times:
|
||||
schedule.every().day.at(time_to_run).do(start, config_file, time_scheduled=time_to_run)
|
||||
schedule.every().day.at(time_to_run).do(start, {"config_file": config_file, "time": time_to_run})
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
if not no_countdown:
|
||||
|
|
Loading…
Reference in a new issue