mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
[77] edit metadata by ID
This commit is contained in:
parent
2f09daa2d7
commit
eae4ee1f66
8 changed files with 123 additions and 62 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.16.5-develop76
|
||||
1.16.5-develop77
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
110
modules/meta.py
110
modules/meta.py
|
@ -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'}")
|
||||
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue