[83] add templates to metadata definitions

pull/1324/head
meisnate12 2 years ago
parent a840643e48
commit 05826f9dff

@ -1 +1 @@
1.18.3-develop82 1.18.3-develop83

@ -145,6 +145,12 @@ html_theme_options = {
("Log Files", "home/logs"), ("Log Files", "home/logs"),
("Run Commands & Environment Variables", "home/environmental"), ("Run Commands & Environment Variables", "home/environmental"),
("Knowledgebase/FAQ", "home/kb"), ("Knowledgebase/FAQ", "home/kb"),
("_menu", "Companion Scripts", "home/scripts", [
("Companion Scripts", "home/scripts"),
("_divider", ),
("PMM Overlay Reset", "home/scripts/overlay-reset"),
("Plex Image Cleanup", "home/scripts/image-cleanup"),
]),
("_divider", ), ("_divider", ),
("YAML File Guide", "home/guides/yaml"), ("YAML File Guide", "home/guides/yaml"),
("Scheduling Guide", "home/guides/scheduling"), ("Scheduling Guide", "home/guides/scheduling"),

@ -0,0 +1,8 @@
# Companion Scripts
Each Companion Script is a separate project that has its own Docker container and GitHub Repository.
| Name | Description | Readme |
|:---------------------|:------------------------------------------------------|:-----------------------------------------------------------------------------------------|
| `PMM Overlay Reset` | Script to completely remove all PMM applied Overlays. | [Wiki](scripts/overlay-reset)/[GitHub](https://github.com/meisnate12/PMM-Overlay-Reset) |
| `Plex Image Cleanup` | Script to clean up Plex's Image Cache. | [Wiki](scripts/image-cleanup)/[GitHub](https://github.com/meisnate12/Plex-Image-Cleanup) |

@ -152,6 +152,7 @@ EMPTY_TRASH=False
CLEAN_BUNDLES=False CLEAN_BUNDLES=False
OPTIMIZE_DB=False OPTIMIZE_DB=False
TRACE=False TRACE=False
LOG_REQUESTS=False
``` ```
### Base Options ### Base Options
@ -269,11 +270,18 @@ Sleep Timer between Empty Trash, Clean Bundles, and Optimize DB in seconds that'
#### Trace #### Trace
Run with every request and file action logged. Run with extra trace logs.
* **Environment Variable:** `TRACE=True` * **Environment Variable:** `TRACE=True`
* **Shell Command:** `-tr` or `--trace` * **Shell Command:** `-tr` or `--trace`
#### Log Requests
Run with every request and file action logged.
* **Environment Variable:** `LOG_REQUESTS=True`
* **Shell Command:** `-lr` or `--log-requests`
### Continuous Schedule ### Continuous Schedule
Plex Image Cleanup can be run either immediately or on a schedule. The default behavior is to run immediately to run using a schedule simply pass in the `Schedule` Option. Plex Image Cleanup can be run either immediately or on a schedule. The default behavior is to run immediately to run using a schedule simply pass in the `Schedule` Option.

@ -120,7 +120,8 @@ Each option can be applied in three ways:
| Flat Assets | PMM Asset Folder uses [Flat Assets Image Paths](https://metamanager.wiki/en/latest/home/guides/assets.html#asset-naming).<br>**Shell Command:** `-f` or `--flat`<br>**Environment Variable:** `PMM_FLAT=True` | &#10060; | | Flat Assets | PMM Asset Folder uses [Flat Assets Image Paths](https://metamanager.wiki/en/latest/home/guides/assets.html#asset-naming).<br>**Shell Command:** `-f` or `--flat`<br>**Environment Variable:** `PMM_FLAT=True` | &#10060; |
| Reset Season Posters | Restore Season posters during run.<br>**Shell Command:** `-s` or `--season`<br>**Environment Variable:** `SEASON=True` | &#10060; | | Reset Season Posters | Restore Season posters during run.<br>**Shell Command:** `-s` or `--season`<br>**Environment Variable:** `SEASON=True` | &#10060; |
| Reset Episode Posters | Restore Episode posters during run.<br>**Shell Command:** `-e` or `--episode`<br>**Environment Variable:** `EPISODE=True` | &#10060; | | Reset Episode Posters | Restore Episode posters during run.<br>**Shell Command:** `-e` or `--episode`<br>**Environment Variable:** `EPISODE=True` | &#10060; |
| Trace Logs | Run with every request logged.<br>**Shell Command:** `-tr` or `--trace`<br>**Environment Variable:** `TRACE=True` | &#10060; | | Trace Logs | Run with extra trace logs.<br>**Shell Command:** `-tr` or `--trace`<br>**Environment Variable:** `TRACE=True` | &#10060; |
| Log Requests | Run with every request logged.<br>**Shell Command:** `-lr` or `--log-requests`<br>**Environment Variable:** `LOG_REQUESTS=True` | &#10060; |
### Example .env File ### Example .env File
@ -138,4 +139,5 @@ PMM_FLAT=False
SEASON=True SEASON=True
EPISODE=True EPISODE=True
TRACE=False TRACE=False
LOG_REQUESTS=False
``` ```

@ -103,14 +103,15 @@ The available attributes for editing movies are as follows
### Special Attributes ### Special Attributes
| Attribute | Allowed Values | | Attribute | Allowed Values |
|:-----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:-------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `title` | Title if different from the mapping value useful when you have multiple movies with the same name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. | | `title` | Title if different from the mapping value useful when you have multiple movies with the same name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
| `alt_title` | Alternative title to look for and then change to the mapping name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. | | `alt_title` | Alternative title to look for and then change to the mapping name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
| `year` | Year of movie for better identification. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. | | `year` | Year of movie for better identification. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
| `edition_filter` | Edition of movie for better identification. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. | | `edition_filter` | Edition of movie for better identification. Can be a list (only one needs to match). See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
| `tmdb_show` | TMDb Show ID to use for metadata useful for miniseries that have been compiled into a movie. **This is not used to say this show is the given ID.** | | `edition_contains` | Edition of movie must contain the given string for better identification. Can be a list (only one needs to match). See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
| `tmdb_movie` | TMDb Movie ID to use for metadata useful for movies that have been split into segments **This is not used to say this show is the given ID.** | | `tmdb_show` | TMDb Show ID to use for metadata useful for miniseries that have been compiled into a movie. **This is not used to say this show is the given ID.** |
| `tmdb_movie` | TMDb Movie ID to use for metadata useful for movies that have been split into segments **This is not used to say this show is the given ID.** |
### General Attributes ### General Attributes

@ -1102,6 +1102,7 @@ class MetadataFile(DataFile):
methods = {mm.lower(): mm for mm in meta} methods = {mm.lower(): mm for mm in meta}
logger.info("") logger.info("")
item = None
if (isinstance(mapping_name, int) or mapping_name.startswith("tt")) and not self.library.is_music: if (isinstance(mapping_name, int) or mapping_name.startswith("tt")) and not self.library.is_music:
if isinstance(mapping_name, int): if isinstance(mapping_name, int):
id_type = "TMDb" if self.library.is_movie else "TVDb" id_type = "TMDb" if self.library.is_movie else "TVDb"
@ -1123,14 +1124,58 @@ class MetadataFile(DataFile):
logger.error(f"Metadata Error: {id_type} ID not mapped") logger.error(f"Metadata Error: {id_type} ID not mapped")
continue continue
title = None title = None
if "title" in methods:
if meta[methods["title"]] is None:
logger.error("Metadata Error: title attribute is blank")
else:
title = meta[methods["title"]]
else: else:
logger.separator(f"{mapping_name} Metadata", space=False, border=False) logger.separator(f"{mapping_name} Metadata", space=False, border=False)
logger.info("") logger.info("")
title = mapping_name
if "template" in methods:
logger.separator(f"Building Definition From Templates", space=False, border=False)
logger.debug("")
named_templates = []
for original_variables in util.get_list(meta[methods["template"]], split=False):
if not isinstance(original_variables, dict):
raise Failed(f"Metadata Error: template attribute is not a dictionary")
elif "name" not in original_variables:
raise Failed(f"Metadata Error: template sub-attribute name is required")
elif not original_variables["name"]:
raise Failed(f"Metadata Error: template sub-attribute name cannot be blank")
named_templates.append(original_variables["name"])
logger.debug(f"Templates Called: {', '.join(named_templates)}")
logger.debug("")
new_variables = {}
if "variables" in methods:
logger.debug("")
logger.debug("Validating Method: variables")
if not isinstance(meta[methods["variables"]], dict):
raise Failed(f"Metadata Error: variables must be a dictionary (key: value pairs)")
logger.trace(meta[methods["variables"]])
new_variables = meta[methods["variables"]]
name = meta[methods["name"]] if "name" in methods else None
new_attributes = self.apply_template(name, mapping_name, meta, meta[methods["template"]], new_variables)
for attr in new_attributes:
if attr.lower() not in methods:
meta[attr] = new_attributes[attr]
methods[attr.lower()] = attr
if "title" in methods:
if meta[methods["title"]] is None:
logger.error("Metadata Error: title attribute is blank")
else:
title = meta[methods["title"]]
edition_titles = None
if "edition_filter" in methods and self.library.is_movie:
edition_titles = util.get_list(meta[methods["edition_filter"]])
if not edition_titles:
edition_titles = [""]
edition_contains = None
if "edition_contains" in methods and self.library.is_movie:
edition_contains = util.get_list(meta[methods["edition_contains"]])
if not edition_contains:
edition_contains = []
if not item:
year = None year = None
if "year" in methods and not self.library.is_music: if "year" in methods and not self.library.is_music:
if meta[methods["year"]] is None: if meta[methods["year"]] is None:
@ -1143,36 +1188,26 @@ class MetadataFile(DataFile):
pass pass
if year is None: if year is None:
raise Failed(f"Metadata Error: year attribute must be an integer between 1800 and {next_year}") raise Failed(f"Metadata Error: year attribute must be an integer between 1800 and {next_year}")
edition_title = edition_titles[0] if len(edition_titles) == 1 else None
edition_title = None
if "edition_filter" in methods and self.library.is_movie:
edition_title = str(meta[methods["edition_filter"]])
if not edition_title:
edition_title = ""
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"]]
item = self.library.search_item(title, year=year, edition=edition_title) item = self.library.search_item(title, year=year, edition=edition_title)
if item is None and "alt_title" in methods: if not item and "alt_title" in methods:
if meta[methods["alt_title"]] is None: if meta[methods["alt_title"]] is None:
logger.error("Metadata Error: alt_title attribute is blank") logger.error("Metadata Error: alt_title attribute is blank")
else: else:
alt_title = meta[methods["alt_title"]] alt_title = meta[methods["alt_title"]]
item = self.library.search_item(alt_title, year=year, edition=edition_title) item = self.library.search_item(alt_title, year=year, edition=edition_title)
if item is None: if not item:
item = self.library.search_item(alt_title, edition=edition_title) item = self.library.search_item(alt_title, edition=edition_title)
if item is None: if not item:
logger.error(f"Skipping {mapping_name}: Item {title} not found") logger.error(f"Skipping {mapping_name}: Item {title} not found")
continue continue
if not isinstance(item, list): if not isinstance(item, list):
item = [item] item = [item]
if edition_titles or edition_contains:
item = [i for i in item if (edition_titles and i.editionTitle in edition_titles) or (edition_contains and any([r in i.editionTitle for r in edition_contains]))]
for i in item: for i in item:
self.update_metadata_item(i, title, mapping_name, meta, methods) self.update_metadata_item(i, title, mapping_name, meta, methods)

@ -1077,10 +1077,7 @@ class Plex(Library):
kwargs["year"] = year kwargs["year"] = year
if edition is not None: if edition is not None:
kwargs["editionTitle"] = edition kwargs["editionTitle"] = edition
for d in self.search(title=str(data), **kwargs): return [d for d in self.search(title=str(data), **kwargs) if d.title == data]
if d.title == data:
return d
return None
def edit_advance(self, item, edits): def edit_advance(self, item, edits):
try: try:

@ -996,7 +996,9 @@ if __name__ == "__main__":
valid_times = [] valid_times = []
for time_to_run in times_to_run: for time_to_run in times_to_run:
try: try:
valid_times.append(datetime.strftime(datetime.strptime(time_to_run, "%H:%M"), "%H:%M")) final_time = datetime.strftime(datetime.strptime(time_to_run, "%H:%M"), "%H:%M")
if final_time not in valid_times:
valid_times.append(final_time)
except ValueError: except ValueError:
if time_to_run: if time_to_run:
raise Failed(f"Argument Error: time argument invalid: {time_to_run} must be in the HH:MM format between 00:00-23:59") raise Failed(f"Argument Error: time argument invalid: {time_to_run} must be in the HH:MM format between 00:00-23:59")
@ -1023,7 +1025,7 @@ if __name__ == "__main__":
minutes = int((seconds % 3600) // 60) minutes = int((seconds % 3600) // 60)
time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else "" time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else ""
time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}" time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}"
logger.ghost(f"Current Time: {current_time} | {time_str} until the next run at {og_time_str} | Runs: {', '.join(times_to_run)}") logger.ghost(f"Current Time: {current_time} | {time_str} until the next run at {og_time_str} | Runs: {', '.join(valid_times)}")
else: else:
logger.error(f"Time Error: {valid_times}") logger.error(f"Time Error: {valid_times}")
time.sleep(60) time.sleep(60)

Loading…
Cancel
Save