mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
[83] add templates to metadata definitions
This commit is contained in:
parent
a840643e48
commit
05826f9dff
9 changed files with 98 additions and 39 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.18.3-develop82
|
||||
1.18.3-develop83
|
||||
|
|
|
@ -145,6 +145,12 @@ html_theme_options = {
|
|||
("Log Files", "home/logs"),
|
||||
("Run Commands & Environment Variables", "home/environmental"),
|
||||
("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", ),
|
||||
("YAML File Guide", "home/guides/yaml"),
|
||||
("Scheduling Guide", "home/guides/scheduling"),
|
||||
|
|
8
docs/home/scripts.md
Normal file
8
docs/home/scripts.md
Normal file
|
@ -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
|
||||
OPTIMIZE_DB=False
|
||||
TRACE=False
|
||||
LOG_REQUESTS=False
|
||||
```
|
||||
|
||||
### Base Options
|
||||
|
@ -269,11 +270,18 @@ Sleep Timer between Empty Trash, Clean Bundles, and Optimize DB in seconds that'
|
|||
|
||||
#### Trace
|
||||
|
||||
Run with every request and file action logged.
|
||||
Run with extra trace logs.
|
||||
|
||||
* **Environment Variable:** `TRACE=True`
|
||||
* **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
|
||||
|
||||
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` | ❌ |
|
||||
| Reset Season Posters | Restore Season posters during run.<br>**Shell Command:** `-s` or `--season`<br>**Environment Variable:** `SEASON=True` | ❌ |
|
||||
| Reset Episode Posters | Restore Episode posters during run.<br>**Shell Command:** `-e` or `--episode`<br>**Environment Variable:** `EPISODE=True` | ❌ |
|
||||
| Trace Logs | Run with every request logged.<br>**Shell Command:** `-tr` or `--trace`<br>**Environment Variable:** `TRACE=True` | ❌ |
|
||||
| Trace Logs | Run with extra trace logs.<br>**Shell Command:** `-tr` or `--trace`<br>**Environment Variable:** `TRACE=True` | ❌ |
|
||||
| Log Requests | Run with every request logged.<br>**Shell Command:** `-lr` or `--log-requests`<br>**Environment Variable:** `LOG_REQUESTS=True` | ❌ |
|
||||
|
||||
|
||||
### Example .env File
|
||||
|
@ -138,4 +139,5 @@ PMM_FLAT=False
|
|||
SEASON=True
|
||||
EPISODE=True
|
||||
TRACE=False
|
||||
LOG_REQUESTS=False
|
||||
```
|
|
@ -103,14 +103,15 @@ The available attributes for editing movies are as follows
|
|||
|
||||
### Special Attributes
|
||||
|
||||
| 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. |
|
||||
| `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. |
|
||||
| `edition_filter` | Edition of movie for better identification. 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.** |
|
||||
| `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.** |
|
||||
| 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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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_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
|
||||
|
||||
|
|
|
@ -1102,6 +1102,7 @@ class MetadataFile(DataFile):
|
|||
methods = {mm.lower(): mm for mm in meta}
|
||||
|
||||
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):
|
||||
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")
|
||||
continue
|
||||
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:
|
||||
logger.separator(f"{mapping_name} Metadata", space=False, border=False)
|
||||
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
|
||||
if "year" in methods and not self.library.is_music:
|
||||
if meta[methods["year"]] is None:
|
||||
|
@ -1143,36 +1188,26 @@ class MetadataFile(DataFile):
|
|||
pass
|
||||
if year is None:
|
||||
raise Failed(f"Metadata Error: year attribute must be an integer between 1800 and {next_year}")
|
||||
|
||||
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"]]
|
||||
|
||||
edition_title = edition_titles[0] if len(edition_titles) == 1 else None
|
||||
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:
|
||||
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, edition=edition_title)
|
||||
if item is None:
|
||||
if not item:
|
||||
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")
|
||||
continue
|
||||
if not isinstance(item, list):
|
||||
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:
|
||||
self.update_metadata_item(i, title, mapping_name, meta, methods)
|
||||
|
||||
|
|
|
@ -1077,10 +1077,7 @@ class Plex(Library):
|
|||
kwargs["year"] = year
|
||||
if edition is not None:
|
||||
kwargs["editionTitle"] = edition
|
||||
for d in self.search(title=str(data), **kwargs):
|
||||
if d.title == data:
|
||||
return d
|
||||
return None
|
||||
return [d for d in self.search(title=str(data), **kwargs) if d.title == data]
|
||||
|
||||
def edit_advance(self, item, edits):
|
||||
try:
|
||||
|
|
|
@ -996,7 +996,9 @@ if __name__ == "__main__":
|
|||
valid_times = []
|
||||
for time_to_run in times_to_run:
|
||||
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:
|
||||
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")
|
||||
|
@ -1023,7 +1025,7 @@ if __name__ == "__main__":
|
|||
minutes = int((seconds % 3600) // 60)
|
||||
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 ''}"
|
||||
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:
|
||||
logger.error(f"Time Error: {valid_times}")
|
||||
time.sleep(60)
|
||||
|
|
Loading…
Reference in a new issue