diff --git a/VERSION b/VERSION index 2092df01..da0d6250 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.17.2-develop10 +1.17.2-develop11 diff --git a/docs/metadata/overlay.md b/docs/metadata/overlay.md index 2c6d16f2..4d84636b 100644 --- a/docs/metadata/overlay.md +++ b/docs/metadata/overlay.md @@ -93,7 +93,7 @@ There are many attributes available when using overlays to edit how they work. | `back_radius` | Backdrop Radius for the Text Overlay.
**Value:** Integer greater than 0 | ❌ | | `back_line_color` | Backdrop Line Color for the Text Overlay.
**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ | | `back_line_width` | Backdrop Line Width for the Text Overlay.
**Value:** Integer greater than 0 | ❌ | -| `text_format` | Text Format for Special Text Overlays.
**`text_format` Only works with text overlays**
**Value:** Integer 0 or greater | ❌ | +| `special_text` | Text Format for Special Text Overlays.
**`special_text` Only works with text overlays** | ❌ | | `addon_offset` | Text Addon Image Offset from the text.
**`addon_offset` Only works with text overlays**
**Value:** Integer 0 or greater | ❌ | | `addon_position` | Text Addon Image Alignment in relation to the text.
**`addon_position` Only works with text overlays**
**Values:** `left`, `right`, `top`, `bottom` | ❌ | @@ -171,26 +171,26 @@ overlays: #### Special Text Overlays -You can use the item's metadata to determine the text. +You can use the item's metadata to determine the text. You set the `name` to `text(special_text)` and then use the `special_text` attribute to format the text. -The final text can be formatted using the `text_format` attribute and the format variables. +There are multiple Special Text Variables that can be used when formatting the text. The variables are defined like so `<>` and some can have modifiers like so `<>` where `$` is the modifier. The available options are: -The available options are: +| Special Text Variables | Format Variables & Mods | Movies | Shows | Seasons | Episodes | +|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|:--------:|:--------:|:--------:| +| audience_rating | `<>`: audience rating (`8.7`, `9.0`)
`<>`: audience rating out of 100 (`87`, `90`)
`<>`: audience rating removing `.0` as needed (`8.7`, `9`) | ✅ | ✅ | ❌ | ✅ | +| critic_rating | `<>`: critic rating (`8.7`, `9.0`)
`<>`: critic rating out of 100 (`87`, `90`)
`<>`: critic rating removing `.0` as needed (`8.7`, `9`) | ✅ | ✅ | ❌ | ✅ | +| user_rating | `<>`: user rating (`8.7`, `9.0`)
`<>`: user rating out of 100 (`87`, `90`)
`<>`: user rating removing `.0` as needed (`8.7`, `9`) | ✅ | ✅ | ✅ | ✅ | +| title | `<>`: Title of the Item | ✅ | ✅ | ✅ | ✅ | +| show_title | `<<show_title>>`: Title of the Item's Show | ❌ | ❌ | ✅ | ✅ | +| season_title | `<<season_title>>`: Title of the Item's Season | ❌ | ❌ | ❌ | ✅ | +| original_title | `<<original_title>>`: Original Title of the Item | ✅ | ✅ | ❌ | ❌ | +| content_rating | `<<content_rating>>`: Content Rating of the Item | ✅ | ✅ | ❌ | ✅ | +| episode_count | `<<episode_count>>`: Number of Episodes (`1`)<br>`<<episode_countW>`: Number of Episodes As Words (`One`)<br>`<<episode_count0>`: Number of Episodes With 10s Padding (`01`)<br>`<<episode_count00>>`: Number of Episodes With 100s Padding (`001`) | ❌ | ✅ | ✅ | ❌ | +| season_number | `<<season_number>>`: Season Number (`1`)<br>`<<season_numberW>`: Season Number As Words (`One`)<br>`<<season_number0>`: Season Number With 10s Padding (`01`)<br>`<<season_number00>>`: Season Number With 100s Padding (`001`) | ❌ | ❌ | ✅ | ✅ | +| episode_number | `<<episode_number>>`: Episode Number (`1`)<br>`<<episode_numberW>`: Episode Number As Words (`One`)<br>`<<episode_number0>`: Episode Number With 10s Padding (`01`)<br>`<<episode_number00>>`: Episode Number With 100s Padding (`001`) | ❌ | ❌ | ❌ | ✅ | +| runtime | `<<runtime>>`: Runtime of the Item in minutes<br>`<<runtimeH>>`: Hours in runtime of the Item<br>`<<runtimeM>>`: Minutes remaining in the hour in the runtime of the Item | ✅ | ❌ | ❌ | ✅ | +| originally_available | `<<originally_available>>`: Original Available Date of the Item<br>`<<originally_available[DATE_FORMAT_STRING]>>`: Original Available Date of the Item in the given format. [Format Options](https://strftime.org/) | ✅ | ✅ | ❌ | ✅ | -| Attribute | Notes | Format Variables | -|:---------------------------|:-------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| text(audience_rating) | Doesnt work with Seasons | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) | -| text(critic_rating) | Doesnt work with Seasons | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) | -| text(user_rating) | | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) | -| text(title) | | `<<value>>` -> Title of the Item | -| text(show_title) | Doesnt work with Movies and Shows | `<<value>>` -> Title of the Item's Show | -| text(season_title) | Only works with Episodes | `<<value>>` -> Title of the Item's Season | -| text(original_title) | Only works with Movies and Shows | `<<value>>` -> Original Title of the Item | -| text(episode_count) | Only works with Shows and Seasons | `<<value>>` -> Number of Episodes in the Show or Season | -| text(content_rating) | Doesnt work with Seasons | `<<value>>` -> Content Rating of the Item | -| text(season_episode) | Only works with Seasons and Episodes | `<<season>>` -> Season Number<br>`<<season0>` -> Season Number With 10s Padding<br>`<<season00>>` -> Season Number With 100s Padding<br>`<<episode>>` -> Episode Number<br>`<<episode0>` -> Episode Number With 10s Padding<br>`<<episode00>>` -> Episode Number With 100s Padding | -| text(runtime) | Doesnt work with Shows and Seasons | `<<value>>` -> Runtime of the Item in minutes<br>`<<valueH>>` -> Hours in runtime of the Item<br>`<<valueM>>` -> Minutes remaining in the hour in the runtime of the Item | -| text(originally_available) | Doesnt work with Seasons | `<<value>>` -> Original Available Date of the Item<br>`<<value[DATE_FORMAT_STRING]>>` -> Original Available Date of the Item in the given format. [Format Options](https://strftime.org/) | Note: You can use the `mass_audience_rating_update` or `mass_critic_rating_update` [Library Operation](../config/operations) to update your plex ratings to various services like `tmdb`, `imdb`, `mdb`, `metacritic`, `letterboxd` and many more. @@ -200,8 +200,8 @@ I want to have the audience_rating display with a `%` out of 100 vs 0.0-10.0. overlays: audience_rating: overlay: - name: text(audience_rating) - text_format: <<value%>>% + name: text(special_text) + special_text: <<audience_rating%>>% horizontal_offset: 225 horizontal_align: center vertical_offset: 15 diff --git a/modules/builder.py b/modules/builder.py index 4518f75b..b0c44a41 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -260,7 +260,6 @@ class CollectionBuilder: if level and not self.library.is_movie and not self.playlist: logger.debug("") logger.debug("Validating Method: builder_level") - level = self.data[methods["builder_level"]] if level is None: logger.error(f"{self.Type} Error: builder_level attribute is blank") else: @@ -1670,6 +1669,20 @@ class CollectionBuilder: item = self.fetch_item(rk) if self.playlist and isinstance(item, (Show, Season)): items.extend(item.episodes()) + elif self.builder_level == "movie" and not isinstance(item, Movie): + logger.info(f"Item: {item} is not an Movie") + elif self.builder_level == "show" and not isinstance(item, Show): + logger.info(f"Item: {item} is not an Show") + elif self.builder_level == "episode" and not isinstance(item, Episode): + logger.info(f"Item: {item} is not an Episode") + elif self.builder_level == "season" and not isinstance(item, Season): + logger.info(f"Item: {item} is not a Season") + elif self.builder_level == "artist" and not isinstance(item, Artist): + logger.info(f"Item: {item} is not an Artist") + elif self.builder_level == "album" and not isinstance(item, Album): + logger.info(f"Item: {item} is not an Album") + elif self.builder_level == "track" and not isinstance(item, Track): + logger.info(f"Item: {item} is not a Track") else: items.append(item) except Failed as e: diff --git a/modules/cache.py b/modules/cache.py index 80f5db38..50247df5 100644 --- a/modules/cache.py +++ b/modules/cache.py @@ -867,16 +867,16 @@ class Cache: [(r.name, r.date.strftime("%Y-%m-%d") if r.date else None, expiration_date.strftime("%Y-%m-%d"), r.season, r.round) for r in races]) - def query_overlay_special_text(self, rating_key, data_type): - rating = None + def query_overlay_special_text(self, rating_key): + attrs = {} with sqlite3.connect(self.cache_path) as connection: connection.row_factory = sqlite3.Row with closing(connection.cursor()) as cursor: - cursor.execute("SELECT * FROM overlay_special_text WHERE rating_key = ? AND type = ?", (rating_key, data_type)) - row = cursor.fetchone() - if row: - rating = row["text"] - return rating + cursor.execute("SELECT * FROM overlay_special_text WHERE rating_key = ?", (rating_key, )) + for row in cursor.fetchall(): + if row: + attrs[row["type"]] = row["text"] + return attrs def update_overlay_special_text(self, rating_key, data_type, text): with sqlite3.connect(self.cache_path) as connection: diff --git a/modules/overlay.py b/modules/overlay.py index 5cc9fa18..ef4b055e 100644 --- a/modules/overlay.py +++ b/modules/overlay.py @@ -8,12 +8,43 @@ logger = util.logger portrait_dim = (1000, 1500) landscape_dim = (1920, 1080) -rating_special_text = [f"text({a})" for a in ["audience_rating", "critic_rating", "user_rating"]] -value_overlays = ["title", "show_title", "season_title", "original_title", "episode_count", "content_rating"] -special_overlays = ["season_episode", "runtime", "originally_available"] -special_text_overlays = [f"text({a})" for a in value_overlays + special_overlays] + rating_special_text -old_special_text = [f"text({a}{s})" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["0", "%", "#"]] -all_special_text = special_text_overlays + old_special_text +old_special_text2 = [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "0", "%", "#"]] +float_vars = ["audience_rating", "critic_rating", "user_rating"] +int_vars = ["runtime", "season_number", "episode_number", "episode_count"] +date_vars = ["originally_available"] +types_for_var = { + "movie_show_season_episode_artist_album": ["user_rating", "title"], + "movie_show_episode_album": ["critic_rating", "originally_available"], + "movie_show_episode": ["audience_rating", "content_rating"], + "movie_show": ["original_title"], + "movie_episode": ["runtime"], + "season_episode": ["show_title", "season_number"], + "show_season": ["episode_count"], + "episode": ["season_title", "episode_number"] +} +var_mods = { + "title": [""], + "content_rating": [""], + "original_title": [""], + "show_title": [""], + "season_title": [""], + "user_rating": ["", "%", "#"], + "critic_rating": ["", "%", "#"], + "audience_rating": ["", "%", "#"], + "originally_available": ["", "["], + "runtime": ["", "H", "M"], + "season_number": ["", "W", "0", "00"], + "episode_number": ["", "W", "0", "00"], + "episode_count": ["", "W", "0", "00"], +} +vars_by_type = { + "movie": [f"{item}{m}" for check, sub in types_for_var.items() for item in sub for m in var_mods[item] if "movie" in check], + "show": [f"{item}{m}" for check, sub in types_for_var.items() for item in sub for m in var_mods[item] if "show" in check], + "season": [f"{item}{m}" for check, sub in types_for_var.items() for item in sub for m in var_mods[item] if "season" in check], + "episode": [f"{item}{m}" for check, sub in types_for_var.items() for item in sub for m in var_mods[item] if "episode" in check], + "artist": [f"{item}{m}" for check, sub in types_for_var.items() for item in sub for m in var_mods[item] if "artist" in check], + "album": [f"{item}{m}" for check, sub in types_for_var.items() for item in sub for m in var_mods[item] if "album" in check], +} def parse_cords(data, parent, required=False): horizontal_align = util.parse("Overlay", "horizontal_align", data["horizontal_align"], parent=parent, @@ -95,7 +126,7 @@ class Overlay: self.font_color = None self.addon_offset = 0 self.addon_position = None - self.text_overlay_format = None + self.special_text = None logger.debug("") logger.debug("Validating Method: overlay") @@ -223,6 +254,7 @@ class Overlay: if not match: raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}") self.name = f"text({match.group(1)})" + text = f"{match.group(1)}" self.font_name = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "fonts", "Roboto-Medium.ttf") if "font_size" in self.data: self.font_size = util.parse("Overlay", "font_size", self.data["font_size"], datatype="int", parent="overlay", default=self.font_size) @@ -249,52 +281,23 @@ class Overlay: self.font_color = ImageColor.getcolor(self.data["font_color"], "RGBA") except ValueError: raise Failed(f"Overlay Error: overlay font_color: {self.data['font_color']} invalid") + if text in old_special_text2: + text_mod = text[-1] if text[-1] in ["0", "%", "#"] else None + text = text if text_mod is None else text[:-1] + self.special_text = f"<<{text}#>>" if text_mod == "#" else f"<<{text}%>>{'' if text_mod == '0' else '%'}" + self.name = "text(special_text)" + elif self.name == "text(special_text)": + if "special_text" not in self.data or not self.data["special_text"]: + raise Failed("Overlay Error: text(special_text) requires the special_text attribute") + if "<<originally_available[" in self.data["special_text"]: + match = re.search("<<originally_available\\[(.+)]>>", self.data["special_text"]) + if match: + try: + datetime.now().strftime(match.group(1)) + except ValueError: + raise Failed("Overlay Error: originally_available date format not valid") + self.special_text = self.data["special_text"] - if self.name in all_special_text: - if self.name.startswith("text(critic") and self.level == "season": - raise Failed("Overlay Error: builder_level season doesn't have critic_ratings") - elif self.name.startswith("text(audience") and self.level == "season": - raise Failed("Overlay Error: builder_level season doesn't have audience_ratings") - elif self.name in ["text(season_episode)", "text(show_title)"] and self.level not in ["season", "episode"]: - raise Failed(f"Overlay Error: {self.name[5:-1]} only works with builder_level season and episode") - elif self.name == "text(runtime)" and self.level not in ["movie", "episode"]: - raise Failed("Overlay Error: runtime only works with movies and builder_level: episode") - elif self.name == "text(season_title)" and self.level != "episode": - raise Failed("Overlay Error: season_title only works with builder_level: episode") - elif self.name == "text(original_title)" and self.level not in ["movie", "show"]: - raise Failed("Overlay Error: original_title only works with movies and shows") - elif self.name == "text(episode_count)" and self.level not in ["show", "season"]: - raise Failed("Overlay Error: episode_count only works with shows and builder_level: season") - elif self.name == ["text(content_rating)", "text(originally_available)"] and self.level == "season": - raise Failed(f"Overlay Error: {self.name[5:-1]} only works with movies, shows, and builder_level: episode") - elif self.name in old_special_text: - self.text_overlay_format = "<<value#>>" if self.name[-2] == "#" else f"<<value%>>{'' if self.name[-2] == '0' else '%'}" - self.name = f"{self.name[:-2]})" - elif "text_format" in self.data and self.data["text_format"]: - if self.name in rating_special_text and not any((f"<<value{m}>>" in self.data["text_format"] for m in ["", "#", "%"])): - raise Failed("Overlay Error: text_format must have the value variable") - elif self.name == "text(season_episode)" and self.level == "season" and not any((f"<<season{m}>>" in self.data["text_format"] for m in ["", "W", "0", "00"])): - raise Failed("Overlay Error: text_format must have the season variable") - elif self.name == "text(season_episode)" and self.level == "episode" and not any((f"<<{a}{m}>>" in self.data["text_format"] for a in ["season", "episode"] for m in ["", "W", "0", "00"])): - raise Failed("Overlay Error: text_format must have the season or episode variable") - elif self.name == "text(runtime)" and not any((f"<<value{m}>>" in self.data["text_format"] for m in ["", "M", "H"])): - raise Failed("Overlay Error: text_format must have the value variable") - elif self.name == "text(originally_available)": - match = re.search("<<value\\[(.+)]>>", self.data["text_format"]) - if not match and "<<value>>" not in self.data["text_format"]: - raise Failed("Overlay Error: text_format must have the value variable") - if match: - try: - datetime.now().strftime(match.group(1)) - except ValueError: - raise Failed("Overlay Error: text_format date format not valid") - elif self.name[5:-1] in value_overlays and "<<value>>" not in self.data["text_format"]: - raise Failed("Overlay Error: text_format must have the value variable") - self.text_overlay_format = self.data["text_format"] - elif self.name == "text(season_episode)": - self.text_overlay_format = "S<<season0>>" if self.level == "season" else "S<<season0>>E<<episode0>>" - else: - self.text_overlay_format = "<<value>>" else: box = self.image.size if self.image else None self.portrait, self.portrait_box = self.get_backdrop(portrait_dim, box=box, text=self.name[5:-1]) @@ -430,7 +433,7 @@ class Overlay: output += f"{self.back_box[0]}{self.back_box[1]}{self.back_align}" if self.addon_position is not None: output += f"{self.addon_position}{self.addon_offset}" - for value in [self.font_color, self.back_color, self.back_radius, self.back_padding, self.back_line_color, self.back_line_width, self.text_overlay_format]: + for value in [self.font_color, self.back_color, self.back_radius, self.back_padding, self.back_line_color, self.back_line_width, self.special_text]: if value is not None: output += f"{value}" return output diff --git a/modules/overlays.py b/modules/overlays.py index f5a7cd66..d8e6445d 100644 --- a/modules/overlays.py +++ b/modules/overlays.py @@ -120,17 +120,21 @@ class Overlays: if self.config.Cache: for over_name in over_names: current_overlay = properties[over_name] - if current_overlay.name in overlay.special_text_overlays: - data_type = current_overlay.name[5:-1] - actual = plex.attribute_translation[data_type] if data_type in plex.attribute_translation[data_type] else data_type - cache_value = self.config.Cache.query_overlay_special_text(item.ratingKey, data_type) - if cache_value is None or not hasattr(item, actual) or getattr(item, actual) is None: - continue - if current_overlay.name in overlay.rating_special_text: - cache_value = float(cache_value) - if getattr(item, actual) != cache_value: - overlay_change = True - + if current_overlay.name == "text(special_text)": + for cache_key, cache_value in self.config.Cache.query_overlay_special_text(item.ratingKey).items(): + actual = plex.attribute_translation[cache_key] if cache_key in plex.attribute_translation[cache_key] else cache_key + if cache_value is None or not hasattr(item, actual) or getattr(item, actual) is None: + continue + if cache_key in overlay.float_vars: + cache_value = float(cache_value) + if cache_key in overlay.int_vars: + cache_value = int(cache_value) + + if cache_key in overlay.date_vars: + if getattr(item, actual).strftime("%Y-%m-%d") != cache_value: + overlay_change = True + elif getattr(item, actual) != cache_value: + overlay_change = True try: poster, background, item_dir, name = self.library.find_item_assets(item) if not poster and self.library.assets_for_all: @@ -199,64 +203,71 @@ class Overlays: def get_text(text_overlay): full_text = text_overlay.name[5:-1] - if text_overlay.name in overlay.special_text_overlays: - if full_text == "season_episode" and text_overlay.level == "season": - actual_attr = "seasonNumber" - elif full_text == "show_title": - actual_attr = "parentTitle" if text_overlay.level == "season" else "grandparentTitle" - elif full_text in plex.attribute_translation: - actual_attr = plex.attribute_translation[full_text] - else: - actual_attr = full_text - if not hasattr(item, actual_attr) or getattr(item, actual_attr) is None: - raise Failed(f"Overlay Warning: No {full_text} found") - actual_value = getattr(item, actual_attr) - if self.config.Cache: - self.config.Cache.update_overlay_special_text(item.ratingKey, full_text, actual_value) - full_text = str(text_overlay.text_overlay_format) - if text_overlay.name in overlay.value_overlays + overlay.rating_special_text + ["text(originally_available)"] and "<<value>>" in full_text: - full_text = full_text.replace("<<value>>", actual_value) - if text_overlay.name in overlay.rating_special_text: - if "<<value%>>" in full_text: - full_text = full_text.replace("<<value%>>", f"{int(actual_value * 10)}%") - if "<<value0>>" in full_text: - full_text = full_text.replace("<<value0>>", f"{int(actual_value * 10)}") - if "<<value#>>" in full_text: - full_text = full_text.replace("<<value#>>", str(actual_value)[:-2] if str(actual_value).endswith(".0") else actual_value) - elif text_overlay.name == "text(originally_available)": - if "<<value>>" in full_text: - full_text = full_text.replace("<<value>>", actual_value.strftime("%Y-%m-%d")) - match = re.search("<<value\\[(.+)]>>", full_text) - if match: - full_text = re.sub("<<value\\[(.+)]>>", str(actual_value.strftime(match.group(1))), full_text) - elif text_overlay.name == "text(runtime)": - if "<<value>>" in full_text: - full_text = full_text.replace("<<value>>", actual_value / 60000) - if "<<valueH>>" in full_text: - full_text = full_text.replace("<<valueH>>", (actual_value / 60000) // 60) - if "<<valueM>>" in full_text: - full_text = full_text.replace("<<valueM>>", (actual_value / 60000) % 60) - elif text_overlay.name == "text(season_episode)": - if text_overlay.level == "season": - season = actual_value - episode = None + if full_text == "special_text": + full_text = text_overlay.special_text + for format_var in overlay.vars_by_type[text_overlay.level]: + if f"<<{format_var}" in full_text and format_var == "originally_available[": + mod = re.search("<<originally_available\\[(.+)]>>", full_text).group(1) + format_var = "originally_available" + elif f"<<{format_var}>>" in full_text and format_var.endswith("00"): + mod = "00" + format_var = format_var[:-2] + elif f"<<{format_var}>>" in full_text and format_var.endswith(("%", "#", "H", "M", "0")): + mod = format_var[-1] + format_var = format_var[:-1] + elif f"<<{format_var}>>" in full_text: + mod = "" + else: + continue + if format_var == "show_title": + actual_attr = "parentTitle" if text_overlay.level == "season" else "grandparentTitle" + elif format_var in plex.attribute_translation: + actual_attr = plex.attribute_translation[format_var] + else: + actual_attr = format_var + if not hasattr(item, actual_attr) or getattr(item, actual_attr) is None: + logger.warning(f"Overlay Warning: No {full_text} found") + continue + actual_value = getattr(item, actual_attr) + if self.config.Cache: + cache_store = actual_value.strftime("%Y-%m-%d") if format_var in overlay.date_vars else actual_value + self.config.Cache.update_overlay_special_text(item.ratingKey, format_var, cache_store) + sub_value = None + if format_var == "originally_available": + if mod: + sub_value = "<<originally_available\\[(.+)]>>" + final_value = actual_value.strftime(mod) + else: + final_value = actual_value.strftime("%Y-%m-%d") + elif format_var == "runtime": + if mod == "H": + final_value = (actual_value / 60000) // 60 + elif mod == "M": + final_value = (actual_value / 60000) % 60 + else: + final_value = actual_value / 60000 + elif mod == "%": + final_value = int(actual_value * 10) + elif mod == "#": + final_value = str(actual_value)[:-2] if str(actual_value).endswith(".0") else actual_value + elif mod == "W": + final_value = num2words(int(actual_value)) + elif mod == "0": + final_value = f"{int(actual_value):02}" + elif mod == "00": + final_value = f"{int(actual_value):03}" + else: + final_value = actual_value + if sub_value: + full_text = re.sub(sub_value, str(final_value), full_text) else: - season, episode = actual_value.upper()[1:].split("E") - for attr, attr_val in [("season", season), ("episode", episode)]: - if attr_val and f"<<{attr}>>" in full_text: - full_text = full_text.replace(f"<<{attr}>>", attr_val) - if attr_val and f"<<{attr}W>>" in full_text: - full_text = full_text.replace(f"<<{attr}W>>", num2words(int(attr_val))) - if attr_val and f"<<{attr}0>>" in full_text: - full_text = full_text.replace(f"<<{attr}0>>", f"{int(attr_val):02}") - if attr_val and f"<<{attr}00>>" in full_text: - full_text = full_text.replace(f"<<{attr}00>>", f"{int(attr_val):03}") + full_text = full_text.replace(f"<<{format_var}{mod}>>", str(final_value)) return str(full_text) for over_name in applied_names: current_overlay = properties[over_name] if current_overlay.name.startswith("text"): - if current_overlay.name in overlay.special_text_overlays: + if current_overlay.name == "text(special_text)": image_box = current_overlay.image.size if current_overlay.image else None try: overlay_image, addon_box = current_overlay.get_backdrop((canvas_width, canvas_height), box=image_box, text=get_text(current_overlay)) diff --git a/modules/plex.py b/modules/plex.py index 4645da7e..3b527f45 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -148,9 +148,8 @@ attribute_translation = { "writer": "writers", "mood": "moods", "style": "styles", - "season_episode": "seasonEpisode", - "episode": "episodeNumber", - "season": "seasonNumber", + "episode_number": "episodeNumber", + "season_number": "seasonNumber", "original_title": "originalTitle", "runtime": "duration", "season_title": "parentTitle",