add operations

pull/431/head
meisnate12 3 years ago
parent d52ae82a01
commit 203f0e6cf5

@ -3,19 +3,15 @@
libraries: # Library mappings must have a colon (:) placed after them
Movies:
metadata_path:
- file: config/Movies.yml # You have to create this file the other are online
- file: config/Movies.yml # You have to create this file the other is online
- git: meisnate12/MovieCharts
- git: meisnate12/Studios
- git: meisnate12/IMDBGenres
- git: meisnate12/People
TV Shows:
metadata_path:
- file: config/TV Shows.yml # You have to create this file the other are online
- file: config/TV Shows.yml # You have to create this file the other is online
- git: meisnate12/ShowCharts
- git: meisnate12/Networks
Anime:
metadata_path:
- file: config/Anime.yml # You have to create this file the other are online
- file: config/Anime.yml # You have to create this file the other is online
- git: meisnate12/AnimeCharts
settings: # Can be individually specified per library as well
cache: true

@ -385,8 +385,6 @@ class Config:
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)
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)
@ -400,42 +398,54 @@ class Config:
params["collection_creation_webhooks"] = check_for_attribute(lib, "collection_creation_webhooks", parent="settings", var_type="list", default=self.general["collection_creation_webhooks"], do_print=False, save=False)
params["collection_addition_webhooks"] = check_for_attribute(lib, "collection_addition_webhooks", parent="settings", var_type="list", default=self.general["collection_addition_webhooks"], do_print=False, save=False)
params["collection_removing_webhooks"] = check_for_attribute(lib, "collection_removing_webhooks", parent="settings", var_type="list", default=self.general["collection_removing_webhooks"], 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["mass_genre_update"] = check_for_attribute(lib, "mass_genre_update", test_list=mass_update_options, default_is_none=True, save=False, do_print=False)
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=False)
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=False)
params["mass_trakt_rating_update"] = check_for_attribute(lib, "mass_trakt_rating_update", var_type="bool", default=False, save=False, do_print=False)
params["split_duplicates"] = check_for_attribute(lib, "split_duplicates", var_type="bool", default=False, save=False, do_print=False)
params["radarr_add_all"] = check_for_attribute(lib, "radarr_add_all", var_type="bool", default=False, save=False, do_print=False)
params["sonarr_add_all"] = check_for_attribute(lib, "sonarr_add_all", var_type="bool", default=False, save=False, do_print=False)
if lib and "operations" in lib and lib["operations"]:
if isinstance(lib["operations"], dict):
if "assets_for_all" in lib["operations"]:
params["assets_for_all"] = check_for_attribute(lib["operations"], "assets_for_all", var_type="bool", default=False, save=False)
if "delete_unmanaged_collections" in lib["operations"]:
params["delete_unmanaged_collections"] = check_for_attribute(lib["operations"], "delete_unmanaged_collections", var_type="bool", default=False, save=False)
if "delete_collections_with_less" in lib["operations"]:
params["delete_collections_with_less"] = check_for_attribute(lib["operations"], "delete_collections_with_less", var_type="int", default_is_none=True, save=False)
if "mass_genre_update" in lib["operations"]:
params["mass_genre_update"] = check_for_attribute(lib["operations"], "mass_genre_update", test_list=mass_update_options, default_is_none=True, save=False)
if "mass_audience_rating_update" in lib["operations"]:
params["mass_audience_rating_update"] = check_for_attribute(lib["operations"], "mass_audience_rating_update", test_list=mass_update_options, default_is_none=True, save=False)
if "mass_critic_rating_update" in lib["operations"]:
params["mass_critic_rating_update"] = check_for_attribute(lib["operations"], "mass_critic_rating_update", test_list=mass_update_options, default_is_none=True, save=False)
if "mass_trakt_rating_update" in lib["operations"]:
params["mass_trakt_rating_update"] = check_for_attribute(lib["operations"], "mass_trakt_rating_update", var_type="bool", default=False, save=False)
if "split_duplicates" in lib["operations"]:
params["split_duplicates"] = check_for_attribute(lib["operations"], "split_duplicates", var_type="bool", default=False, save=False)
if "radarr_add_all" in lib["operations"]:
params["radarr_add_all"] = check_for_attribute(lib["operations"], "radarr_add_all", var_type="bool", default=False, save=False)
if "sonarr_add_all" in lib["operations"]:
params["sonarr_add_all"] = check_for_attribute(lib["operations"], "sonarr_add_all", var_type="bool", default=False, save=False)
else:
logger.error("Config Error: operations must be a dictionary")
params["assets_for_all"] = check_for_attribute(lib, "assets_for_all", var_type="bool", default=assets_for_all, save=False, do_print=lib and "assets_for_all" in lib)
params["delete_unmanaged_collections"] = check_for_attribute(lib, "delete_unmanaged_collections", var_type="bool", default=False, save=False, do_print=lib and "delete_unmanaged_collections" in lib)
params["delete_collections_with_less"] = check_for_attribute(lib, "delete_collections_with_less", var_type="int", default_is_none=True, save=False, do_print=lib and "delete_collections_with_less" in lib)
def error_check(attr, service):
params[attr] = None
err = f"Config Error: {attr} cannot be omdb without a successful {service} Connection"
self.errors.append(err)
logger.error(err)
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)
error_check("mass_genre_update", "OMDb")
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)
error_check("mass_audience_rating_update", "OMDb")
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)
error_check("mass_critic_rating_update", "OMDb")
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)
error_check("mass_trakt_rating_update", "Trakt")
try:
if lib and "metadata_path" in lib:
@ -448,9 +458,9 @@ class Config:
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)
err = f"Config Error: metadata_path {attr} is blank"
self.errors.append(err)
logger.error(err)
else:
params["metadata_path"].append((name, path[attr]))
check_dict("url", "URL")

@ -67,9 +67,6 @@ class Library(ABC):
self.empty_trash = params["plex"]["empty_trash"] # TODO: Here or just in Plex?
self.optimize = params["plex"]["optimize"] # TODO: Here or just in Plex?
self.mass_update = self.mass_genre_update or self.mass_audience_rating_update or self.mass_critic_rating_update \
or self.mass_trakt_rating_update or self.split_duplicates or self.radarr_add_all or self.sonarr_add_all
metadata = []
for file_type, metadata_file in self.metadata_path:
if file_type == "Folder":

@ -13,6 +13,28 @@ urls = {
"movies": f"{base_url}/movies/", "alt_movies": f"{alt_url}/movies/",
"series_id": f"{base_url}/dereferrer/series/", "movie_id": f"{base_url}/dereferrer/movie/"
}
language_translation = {
"ab": "abk", "aa": "aar", "af": "afr", "ak": "aka", "sq": "sqi", "am": "amh", "ar": "ara", "an": "arg", "hy": "hye",
"as": "asm", "av": "ava", "ae": "ave", "ay": "aym", "az": "aze", "bm": "bam", "ba": "bak", "eu": "eus", "be": "bel",
"bn": "ben", "bi": "bis", "bs": "bos", "br": "bre", "bg": "bul", "my": "mya", "ca": "cat", "ch": "cha", "ce": "che",
"ny": "nya", "zh": "zho", "cv": "chv", "kw": "cor", "co": "cos", "cr": "cre", "hr": "hrv", "cs": "ces", "da": "dan",
"dv": "div", "nl": "nld", "dz": "dzo", "en": "eng", "eo": "epo", "et": "est", "ee": "ewe", "fo": "fao", "fj": "fij",
"fi": "fin", "fr": "fra", "ff": "ful", "gl": "glg", "ka": "kat", "de": "deu", "el": "ell", "gn": "grn", "gu": "guj",
"ht": "hat", "ha": "hau", "he": "heb", "hz": "her", "hi": "hin", "ho": "hmo", "hu": "hun", "ia": "ina", "id": "ind",
"ie": "ile", "ga": "gle", "ig": "ibo", "ik": "ipk", "io": "ido", "is": "isl", "it": "ita", "iu": "iku", "ja": "jpn",
"jv": "jav", "kl": "kal", "kn": "kan", "kr": "kau", "ks": "kas", "kk": "kaz", "km": "khm", "ki": "kik", "rw": "kin",
"ky": "kir", "kv": "kom", "kg": "kon", "ko": "kor", "ku": "kur", "kj": "kua", "la": "lat", "lb": "ltz", "lg": "lug",
"li": "lim", "ln": "lin", "lo": "lao", "lt": "lit", "lu": "lub", "lv": "lav", "gv": "glv", "mk": "mkd", "mg": "mlg",
"ms": "msa", "ml": "mal", "mt": "mlt", "mi": "mri", "mr": "mar", "mh": "mah", "mn": "mon", "na": "nau", "nv": "nav",
"nd": "nde", "ne": "nep", "ng": "ndo", "nb": "nob", "nn": "nno", "no": "nor", "ii": "iii", "nr": "nbl", "oc": "oci",
"oj": "oji", "cu": "chu", "om": "orm", "or": "ori", "os": "oss", "pa": "pan", "pi": "pli", "fa": "fas", "pl": "pol",
"ps": "pus", "pt": "por", "qu": "que", "rm": "roh", "rn": "run", "ro": "ron", "ru": "rus", "sa": "san", "sc": "srd",
"sd": "snd", "se": "sme", "sm": "smo", "sg": "sag", "sr": "srp", "gd": "gla", "sn": "sna", "si": "sin", "sk": "slk",
"sl": "slv", "so": "som", "st": "sot", "es": "spa", "su": "sun", "sw": "swa", "ss": "ssw", "sv": "swe", "ta": "tam",
"te": "tel", "tg": "tgk", "th": "tha", "ti": "tir", "bo": "bod", "tk": "tuk", "tl": "tgl", "tn": "tsn", "to": "ton",
"tr": "tur", "ts": "tso", "tt": "tat", "tw": "twi", "ty": "tah", "ug": "uig", "uk": "ukr", "ur": "urd", "uz": "uzb",
"ve": "ven", "vi": "vie", "vo": "vol", "wa": "wln", "cy": "cym", "wo": "wol", "fy": "fry", "xh": "xho", "yi": "yid",
"yo": "yor", "za": "zha", "zu": "zul"}
class TVDbObj:
def __init__(self, tvdb_url, language, is_movie, config):
@ -46,15 +68,21 @@ class TVDbObj:
parse_results = [r.strip() for r in parse_results if len(r) > 0]
return parse_results[0] if len(parse_results) > 0 else None
self.title = parse_page(f"//div[@class='change_translation_text' and @data-language='{self.language}']/@data-title")
def parse_title_summary(lang=None):
place = "//div[@class='change_translation_text' and "
place += f"@data-language='{lang}'" if lang else "not(@style='display:none')"
return parse_page(f"{place}/@data-title"), parse_page(f"{place}]/p/text()[normalize-space()]")
self.title, self.summary = parse_title_summary(lang=self.language)
if not self.title and self.language in language_translation:
self.title, self.summary = parse_title_summary(lang=language_translation[self.language])
if not self.title:
self.title = parse_page("//div[@class='change_translation_text' and not(@style='display:none')]/@data-title")
self.title, self.summary = parse_title_summary()
if not self.title:
raise Failed(f"TVDb Error: Name not found from TVDb URL: {self.tvdb_url}")
self.poster_path = parse_page("//div[@class='row hidden-xs hidden-sm']/div/img/@src")
self.background_path = parse_page("(//h2[@class='mt-4' and text()='Backgrounds']/following::div/a/@href)[1]")
self.summary = parse_page("//div[@class='change_translation_text' and not(@style='display:none')]/p/text()[normalize-space()]")
if self.is_movie:
self.directors = parse_page("//strong[text()='Directors']/parent::li/span/a/text()[normalize-space()]")
self.writers = parse_page("//strong[text()='Writers']/parent::li/span/a/text()[normalize-space()]")

@ -38,7 +38,7 @@ class Webhooks:
def end_time_hooks(self, start_time, run_time, stats):
if self.run_end_webhooks:
self._request(self.run_end_webhooks, {
"start_time": start_time,
"start_time": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"run_time": run_time,
"collections_created": stats["created"],
"collections_modified": stats["modified"],

@ -180,8 +180,6 @@ def update_libraries(config):
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}")
@ -212,42 +210,8 @@ def update_libraries(config):
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)):
if library.delete_collections_with_less is not None:
logger.info("")
text = f" with less then {library.delete_collections_with_less} item{'s' if library.delete_collections_with_less > 1 else ''}"
util.separator(f"Deleting All Collections{text if library.delete_collections_with_less > 0 else ''}", space=False, border=False)
logger.info("")
unmanaged_collections = []
for col in library.get_all_collections():
if library.delete_collections_with_less is not None and (library.delete_collections_with_less == 0 or col.childCount < library.delete_collections_with_less):
library.query(col.delete)
logger.info(f"{col.title} Deleted")
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:
if library.delete_unmanaged_collections:
library.query(col.delete)
logger.info(f"{col.title} Deleted")
else:
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 not config.test_mode and not collection_only:
library_operations(config, library, items=items)
logger.removeHandler(library_handler)
except Exception as e:
@ -310,17 +274,21 @@ def update_libraries(config):
if library.optimize:
library.query(library.PlexServer.library.optimize)
def mass_metadata(config, library, items=None):
def library_operations(config, library, items=None):
logger.info("")
util.separator(f"Mass Editing {library.type} Library: {library.name}")
util.separator(f"{library.name} Library Operations")
logger.info("")
if items is None:
items = library.get_all()
if library.split_duplicates:
items = library.search(**{"duplicate": True})
for item in items:
item.split()
logger.info(util.adjust_space(f"{item.title[:25]:<25} | Splitting"))
if library.assets_for_all or library.mass_genre_update or library.mass_audience_rating_update or \
library.mass_critic_rating_update or library.mass_trakt_rating_update or library.radarr_add_all or library.sonarr_add_all:
if items is None:
items = library.get_all()
radarr_adds = []
sonarr_adds = []
trakt_ratings = config.Trakt.user_ratings(library.is_movie) if library.mass_trakt_rating_update else []
@ -457,6 +425,9 @@ def mass_metadata(config, library, items=None):
except Failed:
pass
if library.assets_for_all:
library.update_item_from_assets(item, create=library.create_asset_folders)
if library.Radarr and library.radarr_add_all:
try:
library.Radarr.add_tmdb(radarr_adds)
@ -469,6 +440,50 @@ def mass_metadata(config, library, items=None):
except Failed as e:
logger.error(e)
if library.delete_collections_with_less is not None or library.delete_unmanaged_collections:
logger.info("")
suffix = ""
unmanaged = ""
if library.delete_collections_with_less is not None and library.delete_collections_with_less > 0:
suffix = f" with less then {library.delete_collections_with_less} item{'s' if library.delete_collections_with_less > 1 else ''}"
if library.delete_unmanaged_collections:
if library.delete_collections_with_less is None:
unmanaged = "Unmanaged Collections "
elif library.delete_collections_with_less > 0:
unmanaged = "Unmanaged Collections and "
util.separator(f"Deleting All {unmanaged}Collections{suffix}", space=False, border=False)
logger.info("")
unmanaged_collections = []
for col in library.get_all_collections():
if (library.delete_collections_with_less is not None
and (library.delete_collections_with_less == 0 or col.childCount < library.delete_collections_with_less)) \
or (col.title not in library.collections and library.delete_unmanaged_collections):
library.query(col.delete)
logger.info(f"{col.title} Deleted")
elif col.title not in library.collections:
unmanaged_collections.append(col)
if library.show_unmanaged and len(unmanaged_collections) > 0:
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 Collection{'s' if len(unmanaged_collections) > 1 else ''}")
elif library.show_unmanaged:
logger.info("")
util.separator(f"No Unmanaged Collections in {library.name} Library", space=False, border=False)
logger.info("")
if library.assets_for_all and len(unmanaged_collections) > 0:
logger.info("")
util.separator(f"Unmanaged Collection 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)
def run_collection(config, library, metadata, requested_collections):
global stats
logger.info("")

Loading…
Cancel
Save