[77] edit metadata by ID

This commit is contained in:
meisnate12 2022-05-09 16:40:39 -04:00
parent 2f09daa2d7
commit eae4ee1f66
8 changed files with 123 additions and 62 deletions

View file

@ -1 +1 @@
1.16.5-develop76
1.16.5-develop77

View file

@ -78,7 +78,24 @@ metadata:
## Movies
Each movie is defined by the mapping name which must be the same as the movie name in the library unless an `alt_title` is specified.
Each metadata definition is defined by the mapping name which can link to a movie in multiple ways.
* Mapping name must match the movie name in Plex exactly unless an `alt_title` is specified.
* Mapping name must match the TMDb ID or IMDb ID mapped to the movie.
**Note:** to search for a movie titled with a number surround the number in quotes like in the example below.
```yaml
metadata:
Star Wars: # Matches via the Name "Star Wars"
edits...
299534: # Matches via TMDb ID: 299534
edits...
tt4154756: # Matches via IMDb ID: tt4154756
edits...
"9": # Matches via the Name "9"
edits...
```
## Metadata Edits

View file

@ -59,7 +59,24 @@ metadata:
## Shows
Each show is defined by the mapping name which must be the same as the show name in the library unless an `alt_title` is specified.
Each metadata definition is defined by the mapping name which can link to a show in multiple ways.
* Mapping name must match the show name in Plex exactly unless an `alt_title` is specified.
* Mapping name must match the TVDb ID or IMDb ID mapped to the show.
**Note:** to search for a show titled with a number surround the number in quotes like in the example below.
```yaml
metadata:
Game of Thrones: # Matches via the Name "Game of Thrones"
edits...
366524: # Matches via TVDb ID: 366524
edits...
tt10234724: # Matches via IMDb ID: tt10234724
edits...
"24": # Matches via the Name "24"
edits...
```
### Seasons

View file

@ -2395,7 +2395,12 @@ class CollectionBuilder:
key, options = plex.item_advance_keys[method_name]
if key in prefs and getattr(item, key) != options[method_data]:
advance_edits[key] = options[method_data]
self.library.edit_item(item, item.title, self.collection_level.capitalize(), advance_edits, advanced=True)
if advance_edits:
logger.debug(f"Details Update: {advance_edits}")
if self.library.edit_advance(item, advance_edits):
logger.info(f"{item.title} Advanced Details Update Successful")
else:
logger.error(f"{item.title} Advanced Details Update Failed")
if "item_tmdb_season_titles" in self.item_details and item.ratingKey in self.library.show_rating_key_map:
try:

View file

@ -126,7 +126,7 @@ class Library(ABC):
if meta_obj.collections:
self.collections.extend([c for c in meta_obj.collections])
if meta_obj.metadata:
self.metadatas.extend([c for c in meta_obj.metadata])
self.metadatas.extend([m for m in meta_obj.metadata])
self.metadata_files.append(meta_obj)
except Failed as e:
logger.error(e)
@ -275,8 +275,6 @@ class Library(ABC):
return items
def map_guids(self, items):
logger.separator(f"Mapping {self.type} Library: {self.name}", space=False, border=False)
logger.info("")
for i, item in enumerate(items, 1):
logger.ghost(f"Processing: {i}/{len(items)} {item.title}")
if item.ratingKey not in self.movie_rating_key_map and item.ratingKey not in self.show_rating_key_map:

View file

@ -33,7 +33,7 @@ default_templates = {
"trakt_people_list": {"tmdb_person": f"<<value>>", "plex_search": {"all": {"actor": "tmdb"}}}
}
def get_dict(attribute, attr_data, check_list=None, lower=False):
def get_dict(attribute, attr_data, check_list=None, make_str=False):
if check_list is None:
check_list = []
if attr_data and attribute in attr_data:
@ -41,8 +41,9 @@ def get_dict(attribute, attr_data, check_list=None, lower=False):
if isinstance(attr_data[attribute], dict):
new_dict = {}
for _name, _data in attr_data[attribute].items():
if lower and str(_name).lower() in check_list or not lower and _name in check_list:
logger.warning(f"Config Warning: Skipping duplicate {attribute[:-1] if attribute[-1] == 's' else attribute}: {str(_name).lower() if lower else _name}")
if make_str and str(_name) in check_list or not make_str and _name in check_list:
new_name = f'"{str(_name)}"' if make_str or not isinstance(_name, int) else _name
logger.warning(f"Config Warning: Skipping duplicate {attribute[:-1] if attribute[-1] == 's' else attribute}: {new_name}")
elif _data is None:
logger.warning(f"Config Warning: {attribute[:-1] if attribute[-1] == 's' else attribute}: {_name} has no data")
elif not isinstance(_data, dict):
@ -50,7 +51,7 @@ def get_dict(attribute, attr_data, check_list=None, lower=False):
elif attribute == "templates":
new_dict[str(_name)] = (_data, {})
else:
new_dict[str(_name)] = _data
new_dict[str(_name) if make_str else _name] = _data
return new_dict
else:
logger.error(f"Config Error: {attribute} must be a dictionary")
@ -737,45 +738,68 @@ class MetadataFile(DataFile):
logger.error(f"{description} Details Update Failed")
logger.info("")
logger.separator()
logger.info("")
year = None
if "year" in methods and not self.library.is_music:
if meta[methods["year"]] is None:
raise Failed("Metadata Error: year attribute is blank")
try:
year_value = int(str(meta[methods["year"]]))
if 1800 <= year_value <= next_year:
year = year_value
except ValueError:
pass
if year is None:
raise Failed(f"Metadata Error: year attribute must be an integer between 1800 and {next_year}")
title = mapping_name
if "title" in methods:
if meta[methods["title"]] is None:
logger.error("Metadata Error: title attribute is blank")
if (isinstance(mapping_name, int) or mapping_name.startswith("tt")) and not self.library.is_music:
if isinstance(mapping_name, int):
id_type = "TMDb" if self.library.is_movie else "TVDb"
else:
title = meta[methods["title"]]
item = self.library.search_item(title, year=year)
if item is None and "alt_title" in methods:
if meta[methods["alt_title"]] is None:
logger.error("Metadata Error: alt_title attribute is blank")
id_type = "IMDb"
logger.separator(f"{id_type} ID: {mapping_name} Metadata", space=False, border=False)
logger.info("")
if self.library.is_movie and mapping_name in self.library.movie_map:
item = self.library.fetchItem(self.library.movie_map[mapping_name][0])
elif self.library.is_show and mapping_name in self.library.show_map:
item = self.library.fetchItem(self.library.show_map[mapping_name][0])
elif mapping_name in self.library.imdb_map:
item = self.library.fetchItem(self.library.imdb_map[mapping_name][0])
else:
alt_title = meta["alt_title"]
item = self.library.search_item(alt_title, year=year)
if item is None:
item = self.library.search_item(alt_title)
logger.error(f"Metadata Error: {id_type} ID not mapped")
continue
title = item.title
if "title" in methods:
if meta[methods["title"]] is None:
logger.error("Metadata Error: title attribute is blank")
else:
title = meta[methods["title"]]
else:
logger.separator(f"{mapping_name} Metadata", space=False, border=False)
logger.info("")
year = None
if "year" in methods and not self.library.is_music:
if meta[methods["year"]] is None:
raise Failed("Metadata Error: year attribute is blank")
try:
year_value = int(str(meta[methods["year"]]))
if 1800 <= year_value <= next_year:
year = year_value
except ValueError:
pass
if year is None:
raise Failed(f"Metadata Error: year attribute must be an integer between 1800 and {next_year}")
if item is None:
logger.error(f"Plex Error: Item {mapping_name} not found")
logger.error(f"Skipping {mapping_name}")
continue
title = mapping_name
if "title" in methods:
if meta[methods["title"]] is None:
logger.error("Metadata Error: title attribute is blank")
else:
title = meta[methods["title"]]
logger.info(f"Updating {self.library.type}: {title}...")
item = self.library.search_item(title, year=year)
if item is None and "alt_title" in methods:
if meta[methods["alt_title"]] is None:
logger.error("Metadata Error: alt_title attribute is blank")
else:
alt_title = meta[methods["alt_title"]]
item = self.library.search_item(alt_title, year=year)
if item is None:
item = self.library.search_item(alt_title)
if item is None:
logger.error(f"Plex Error: Item {mapping_name} not found")
logger.error(f"Skipping {mapping_name}")
continue
logger.separator(f"{title} Metadata", space=False, border=False)
tmdb_item = None
tmdb_is_movie = None
@ -853,8 +877,12 @@ class MetadataFile(DataFile):
logger.info(f"Detail: {advance_edit} updated to {method_data}")
else:
logger.error(f"Metadata Error: {advance_edit} attribute is blank")
if self.library.edit_item(item, mapping_name, self.library.type, advance_edits, advanced=True):
updated = True
if advance_edits:
if self.library.edit_advance(item, advance_edits):
updated = True
logger.info(f"{mapping_name} Advanced Details Update Successful")
else:
logger.error(f"{mapping_name} Advanced Details Update Failed")
logger.info(f"{self.library.type}: {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}")

View file

@ -849,19 +849,15 @@ class Plex(Library):
return d
return None
def edit_item(self, item, name, item_type, edits, advanced=False):
if len(edits) > 0:
logger.debug(f"Details Update: {edits}")
try:
self.edit_query(item, edits, advanced=advanced)
if advanced and ("languageOverride" in edits or "useOriginalTitle" in edits):
self.query(item.refresh)
logger.info(f"{item_type}: {name}{' Advanced' if advanced else ''} Details Update Successful")
return True
except BadRequest:
logger.stacktrace()
logger.error(f"{item_type}: {name}{' Advanced' if advanced else ''} Details Update Failed")
return False
def edit_advance(self, item, edits):
try:
self.edit_query(item, edits, advanced=True)
if "languageOverride" in edits or "useOriginalTitle" in edits:
self.query(item.refresh)
return True
except BadRequest:
logger.stacktrace()
return False
def edit_tags(self, attr, obj, add_tags=None, remove_tags=None, sync_tags=None, do_print=True):
display = ""

View file

@ -419,7 +419,7 @@ def update_libraries(config):
if len(title) > longest:
longest = len(title)
def print_status( status):
def print_status(status):
logger.info(f"{'Title':^{longest}} | + | = | - | Run Time | {'Status'}")
breaker = f"{logger.separating_character * longest}|{logger.separating_character * 7}|{logger.separating_character * 7}|{logger.separating_character * 7}|{logger.separating_character * 10}|"
logger.separator(breaker, space=False, border=False, side_space=False, left=True)