mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
[95] add alignment tools
This commit is contained in:
parent
65f8da3a21
commit
2b2b4e9a1f
10 changed files with 197 additions and 137 deletions
|
@ -89,6 +89,6 @@ If you are unable to use the [Plex Meta Manager Discord Server](https://discord.
|
|||
|
||||
## IBRACORP Video Walkthrough
|
||||
|
||||
[IBRACORP](https://ibracorp.io/) made a video walkthough for installing Plex Meta Manager on unRAID. While you might not be using unRAID the video goes over many key aspects of Plex Meta Manager and can be a great place to start learning how to use the script.
|
||||
[IBRACORP](https://ibracorp.io/) made a video walkthrough for installing Plex Meta Manager on unRAID. While you might not be using unRAID the video goes over many key aspects of Plex Meta Manager and can be a great place to start learning how to use the script.
|
||||
|
||||
[![Plex Meta Manager](https://img.youtube.com/vi/dF69MNoot3w/0.jpg)](https://www.youtube.com/watch?v=dF69MNoot3w "Plex Meta Manager")
|
||||
|
|
BIN
Salma.otf
Normal file
BIN
Salma.otf
Normal file
Binary file not shown.
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.16.5-develop94
|
||||
1.16.5-develop95
|
||||
|
|
|
@ -63,8 +63,10 @@ Each overlay definition needs to specify what overlay to use. This can happen in
|
|||
| `repo` | Location in the [Custom Repo](../config/settings.md#custom-repo) of the Overlay Image. | ❌ |
|
||||
| `group` | Name of the Grouping for this overlay. **`weight` is required when using `group`** | ❌ |
|
||||
| `weight` | Weight of this overlay in its group. **`group` is required when using `weight`** | ❌ |
|
||||
| `x_coordinate` | Top Left X Coordinate of this overlay. **`y_coordinate` is required when using `x_coordinate`** | ❌ |
|
||||
| `y_coordinate` | Top Left Y Coordinate of this overlay. **`x_coordinate` is required when using `y_coordinate`** | ❌ |
|
||||
| `x_coordinate` | X Coordinate of this overlay. Can be a %. **`y_coordinate` is required when using `x_coordinate`** | ❌ |
|
||||
| `x_align` | Where the `x_coordinate` is calculated from. **Values:** `left`, `center`, `right` | ❌ |
|
||||
| `y_coordinate` | Y Coordinate of this overlay. Can be a %. **`x_coordinate` is required when using `y_coordinate`** | ❌ |
|
||||
| `y_align` | Where the `y_coordinate` is calculated from. **Values:** `top`, `center`, `bottom` | ❌ |
|
||||
| `font` | System Font Filename or path to font file for the Text Overlay | ❌ |
|
||||
| `font_size` | Font Size for the Text Overlay. **Value:** Integer greater than 0 | ❌ |
|
||||
| `font_color` | Font Color for the Text Overlay. **Value:** Color Hex Code. ex `#00FF00` | ❌ |
|
||||
|
@ -106,6 +108,8 @@ The `x_coordinate` and `y_coordinate` overlay attributes are required when using
|
|||
|
||||
You can add an items rating to the image by using `text(audience_rating)`, `text(critic_rating)`, or `text(user_rating)`
|
||||
|
||||
Default font `Salma.otf` provided by [Alifinart Studio](https://www.behance.net/alifinart)
|
||||
|
||||
```yaml
|
||||
overlays:
|
||||
audience_rating:
|
||||
|
@ -113,7 +117,7 @@ overlays:
|
|||
name: text(audience_rating)
|
||||
x_coordinate: 15
|
||||
y_coordinate: 15
|
||||
font: arial.ttf
|
||||
font: Salma.otf
|
||||
font_size: 200
|
||||
plex_all: true
|
||||
```
|
||||
|
|
|
@ -715,8 +715,8 @@ class ConfigFile:
|
|||
logger.error("Config Error: operations must be a dictionary")
|
||||
|
||||
def error_check(attr, service):
|
||||
logger.error(f"Config Error: Operation {attr} cannot be {params[attr]} without a successful {service} Connection")
|
||||
params[attr] = None
|
||||
logger.error(f"Config Error: {attr} cannot be {params[attr]} without a successful {service} Connection")
|
||||
|
||||
for mass_key in ["mass_genre_update", "mass_audience_rating_update", "mass_critic_rating_update", "mass_content_rating_update", "mass_originally_available_update"]:
|
||||
if params[mass_key] == "omdb" and self.OMDb is None:
|
||||
|
|
|
@ -314,7 +314,6 @@ class MetadataFile(DataFile):
|
|||
auto_list = {}
|
||||
all_keys = []
|
||||
dynamic_data = None
|
||||
logger.debug(exclude)
|
||||
def _check_dict(check_dict):
|
||||
for ck, cv in check_dict.items():
|
||||
all_keys.append(ck)
|
||||
|
@ -527,7 +526,7 @@ class MetadataFile(DataFile):
|
|||
for template_name in template_names:
|
||||
if template_name not in self.templates:
|
||||
raise Failed(f"Config Error: {map_name} template: {template_name} not found")
|
||||
if "<<value>>" in str(self.templates[template_name][0]) or f"<<{auto_type}>>" in str(self.templates[template_name][0]):
|
||||
if any([a in str(self.templates[template_name][0]) for a in ["<<value>>", "<<key>>", f"<<{auto_type}>>"]]):
|
||||
has_var = True
|
||||
if not has_var:
|
||||
raise Failed(f"Config Error: One {map_name} template: {template_names} is required to have the template variable <<value>>")
|
||||
|
|
|
@ -160,15 +160,17 @@ class Overlays:
|
|||
logger.error(f"{item_title[:60]:<60} | Overlay Error: No poster found")
|
||||
elif changed_image or overlay_change:
|
||||
try:
|
||||
image_width = 1920 if isinstance(item, Episode) else 1000
|
||||
image_height = 1080 if isinstance(item, Episode) else 1500
|
||||
|
||||
new_poster = Image.open(poster.location if poster else has_original) \
|
||||
.convert("RGBA") \
|
||||
.resize((1920, 1080) if isinstance(item, Episode) else (1000, 1500), Image.ANTIALIAS)
|
||||
.convert("RGBA").resize((image_width, image_height), Image.ANTIALIAS)
|
||||
if blur_num > 0:
|
||||
new_poster = new_poster.filter(ImageFilter.GaussianBlur(blur_num))
|
||||
for over_name in normal_overlays:
|
||||
overlay = properties[over_name]
|
||||
if overlay.coordinates:
|
||||
new_poster.paste(overlay.image, overlay.coordinates, overlay.image)
|
||||
new_poster.paste(overlay.image, overlay.get_coordinates(image_width, image_height), overlay.image)
|
||||
else:
|
||||
new_poster = new_poster.resize(overlay.image.size, Image.ANTIALIAS)
|
||||
new_poster.paste(overlay.image, (0, 0), overlay.image)
|
||||
|
@ -176,7 +178,6 @@ class Overlays:
|
|||
drawing = ImageDraw.Draw(new_poster)
|
||||
for over_name in text_names:
|
||||
overlay = properties[over_name]
|
||||
font = ImageFont.truetype(overlay.font, overlay.font_size) if overlay.font else None
|
||||
text = over_name[5:-1]
|
||||
if text in ["audience_rating", "critic_rating", "user_rating"]:
|
||||
rating_type = text
|
||||
|
@ -187,7 +188,7 @@ class Overlays:
|
|||
text = getattr(item, actual)
|
||||
if self.config.Cache:
|
||||
self.config.Cache.update_overlay_ratings(item.ratingKey, rating_type, text)
|
||||
drawing.text(overlay.coordinates, str(text), font=font, fill=overlay.font_color)
|
||||
drawing.text(overlay.get_coordinates(image_width, image_height, text=str(text)), str(text), font=overlay.font, fill=overlay.font_color)
|
||||
temp = os.path.join(self.library.overlay_folder, f"temp.png")
|
||||
new_poster.save(temp, "PNG")
|
||||
self.library.upload_poster(item, temp)
|
||||
|
@ -277,19 +278,6 @@ class Overlays:
|
|||
for suppress_name in over_obj.suppress:
|
||||
if suppress_name in properties and rk in properties[suppress_name].keys:
|
||||
properties[suppress_name].keys.remove(rk)
|
||||
if not overlay_name.startswith(("blur", "text")):
|
||||
image_compare = None
|
||||
if self.config.Cache:
|
||||
_, image_compare, _ = self.config.Cache.query_image_map(overlay_name, f"{self.library.image_table_name}_overlays")
|
||||
overlay_size = os.stat(over_obj.path).st_size
|
||||
over_obj.updated = not image_compare or str(overlay_size) != str(image_compare)
|
||||
try:
|
||||
over_obj.image = Image.open(over_obj.path).convert("RGBA")
|
||||
if self.config.Cache:
|
||||
self.config.Cache.update_image_map(overlay_name, f"{self.library.image_table_name}_overlays", overlay_name, overlay_size)
|
||||
except OSError:
|
||||
logger.error(f"Overlay Error: overlay image {over_obj.path} failed to load")
|
||||
properties.pop(overlay_name)
|
||||
|
||||
for overlay_name, over_obj in properties.items():
|
||||
for over_key in over_obj.keys:
|
||||
|
|
|
@ -439,7 +439,7 @@ class Plex(Library):
|
|||
if label:
|
||||
label_id = next((c.key for c in self.get_tags("label") if c.title == label), None)
|
||||
if label_id:
|
||||
args = f"{args}&{label_id}"
|
||||
args = f"{args}&label={label_id}"
|
||||
else:
|
||||
return []
|
||||
return self.get_filter_items(args)
|
||||
|
|
287
modules/util.py
287
modules/util.py
|
@ -4,7 +4,7 @@ from pathvalidate import is_valid_filename, sanitize_filename
|
|||
from plexapi.audio import Album, Track
|
||||
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
|
||||
from plexapi.video import Season, Episode, Movie
|
||||
from PIL import ImageColor
|
||||
from PIL import Image, ImageColor, ImageDraw, ImageFont
|
||||
|
||||
try:
|
||||
import msvcrt
|
||||
|
@ -840,126 +840,195 @@ class Overlay:
|
|||
self.path = None
|
||||
self.coordinates = None
|
||||
self.font = None
|
||||
self.font_name = None
|
||||
self.font_size = 12
|
||||
self.font_color = None
|
||||
logger.debug("")
|
||||
logger.debug("Validating Method: overlay")
|
||||
logger.debug(f"Value: {self.data}")
|
||||
if isinstance(self.data, dict):
|
||||
if "name" not in self.data or not self.data["name"]:
|
||||
raise Failed(f"Overlay Error: overlay must have the name attribute")
|
||||
self.name = str(self.data["name"])
|
||||
|
||||
if "group" in self.data and self.data["group"]:
|
||||
self.group = str(self.data["group"])
|
||||
if "weight" in self.data and self.data["weight"] is not None:
|
||||
pri = check_num(self.data["weight"])
|
||||
if pri is None:
|
||||
raise Failed(f"Overlay Error: overlay weight must be a number")
|
||||
self.weight = pri
|
||||
if ("group" in self.data or "weight" in self.data) and (self.weight is None or not self.group):
|
||||
raise Failed(f"Overlay Error: overlay attribute's group and weight must be used together")
|
||||
|
||||
x_cord = None
|
||||
y_cord = None
|
||||
if "x_coordinate" in self.data and self.data["x_coordinate"] is not None:
|
||||
x_cord = check_num(self.data["x_coordinate"])
|
||||
if x_cord is None or x_cord < 0:
|
||||
raise Failed(f"Overlay Error: overlay x_coordinate: {self.data['x_coordinate']} must be a number 0 or greater")
|
||||
if "y_coordinate" in self.data and self.data["y_coordinate"] is not None:
|
||||
y_cord = check_num(self.data["y_coordinate"])
|
||||
if y_cord is None or y_cord < 0:
|
||||
raise Failed(f"Overlay Error: overlay y_coordinate: {self.data['y_coordinate']} must be a number 0 or greater")
|
||||
if ("x_coordinate" in self.data or "y_coordinate" in self.data) and (x_cord is None or y_cord is None):
|
||||
raise Failed(f"Overlay Error: overlay x_coordinate and overlay y_coordinate must be used together")
|
||||
if x_cord is not None or y_cord is not None:
|
||||
self.coordinates = (x_cord, y_cord)
|
||||
|
||||
def get_and_save_image(image_url):
|
||||
response = self.config.get(image_url)
|
||||
if response.status_code >= 400:
|
||||
raise Failed(f"Overlay Error: Overlay Image not found at: {image_url}")
|
||||
if "Content-Type" not in response.headers or response.headers["Content-Type"] != "image/png":
|
||||
raise Failed(f"Overlay Error: Overlay Image not a png: {image_url}")
|
||||
if not os.path.exists(library.overlay_folder) or not os.path.isdir(library.overlay_folder):
|
||||
os.makedirs(library.overlay_folder, exist_ok=False)
|
||||
logger.info(f"Creating Overlay Folder found at: {library.overlay_folder}")
|
||||
clean_image_name, _ = validate_filename(self.name)
|
||||
image_path = os.path.join(library.overlay_folder, f"{clean_image_name}.png")
|
||||
if os.path.exists(image_path):
|
||||
os.remove(image_path)
|
||||
with open(image_path, "wb") as handler:
|
||||
handler.write(response.content)
|
||||
while is_locked(image_path):
|
||||
time.sleep(1)
|
||||
return image_path
|
||||
|
||||
if not self.name.startswith(("blur", "text")):
|
||||
if "file" in self.data and self.data["file"]:
|
||||
self.path = self.data["file"]
|
||||
elif "git" in self.data and self.data["git"]:
|
||||
self.path = get_and_save_image(f"{github_base}{self.data['git']}.png")
|
||||
elif "repo" in self.data and self.data["repo"]:
|
||||
self.path = get_and_save_image(f"{self.config.custom_repo}{self.data['repo']}.png")
|
||||
elif "url" in self.data and self.data["url"]:
|
||||
self.path = get_and_save_image(self.data["url"])
|
||||
|
||||
if self.name.startswith("blur"):
|
||||
try:
|
||||
match = re.search("\\(([^)]+)\\)", self.name)
|
||||
if not match or 0 >= int(match.group(1)) > 100:
|
||||
raise ValueError
|
||||
self.name = f"blur({match.group(1)})"
|
||||
except ValueError:
|
||||
logger.error(f"Overlay Error: failed to parse overlay blur name: {self.name} defaulting to blur(50)")
|
||||
self.name = "blur(50)"
|
||||
elif self.name.startswith("text"):
|
||||
if not self.coordinates:
|
||||
raise Failed(f"Overlay Error: overlay attribute's x_coordinate and y_coordinate are required when using text")
|
||||
match = re.search("\\(([^)]+)\\)", self.name)
|
||||
if not match:
|
||||
raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}")
|
||||
self.name = f"text({match.group(1)})"
|
||||
if "font" in self.data and self.data["font"]:
|
||||
font = str(self.data["font"])
|
||||
if not os.path.exists(font):
|
||||
fonts = get_system_fonts()
|
||||
if font not in fonts:
|
||||
raise Failed(f"Overlay Error: font: {font} not found. Options: {', '.join(fonts)}")
|
||||
self.font = font
|
||||
if "font_size" in self.data and self.data["font_size"] is not None:
|
||||
font_size = check_num(self.data["font_size"])
|
||||
if font_size is None or font_size < 1:
|
||||
logger.error(f"Overlay Error: overlay font_size: {self.data['font_size']} invalid must be a greater than 0")
|
||||
else:
|
||||
self.font_size = font_size
|
||||
if "font_color" in self.data and self.data["font_color"]:
|
||||
try:
|
||||
color_str = self.data["font_color"]
|
||||
color_str = color_str if color_str.startswith("#") else f"#{color_str}"
|
||||
self.font_color = ImageColor.getcolor(color_str, "RGB")
|
||||
except ValueError:
|
||||
logger.error(f"Overlay Error: overlay color: {self.data['color']} invalid")
|
||||
else:
|
||||
if "|" in self.name:
|
||||
raise Failed(f"Overlay Error: Overlay Name: {self.name} cannot contain '|'")
|
||||
if not self.path:
|
||||
clean_name, _ = validate_filename(self.name)
|
||||
self.path = os.path.join(library.overlay_folder, f"{clean_name}.png")
|
||||
if not os.path.exists(self.path):
|
||||
raise Failed(f"Overlay Error: Overlay Image not found at: {self.path}")
|
||||
else:
|
||||
self.name = str(self.data)
|
||||
if not isinstance(self.data, dict):
|
||||
self.data = {"name": str(self.data)}
|
||||
logger.warning(f"Overlay Warning: No overlay attribute using mapping name {self.data} as the overlay name")
|
||||
|
||||
if "name" not in self.data or not self.data["name"]:
|
||||
raise Failed(f"Overlay Error: overlay must have the name attribute")
|
||||
self.name = str(self.data["name"])
|
||||
|
||||
if "group" in self.data and self.data["group"]:
|
||||
self.group = str(self.data["group"])
|
||||
if "weight" in self.data and self.data["weight"] is not None:
|
||||
pri = check_num(self.data["weight"])
|
||||
if pri is None:
|
||||
raise Failed(f"Overlay Error: overlay weight must be a number")
|
||||
self.weight = pri
|
||||
if ("group" in self.data or "weight" in self.data) and (self.weight is None or not self.group):
|
||||
raise Failed(f"Overlay Error: overlay attribute's group and weight must be used together")
|
||||
|
||||
self.x_align = parse("Overlay", "x_align", self.data["x_align"], options=["left", "center", "right"]) if "x_align" in self.data else "left"
|
||||
self.y_align = parse("Overlay", "y_align", self.data["y_align"], options=["top", "center", "bottom"]) if "y_align" in self.data else "top"
|
||||
|
||||
x_cord = None
|
||||
if "x_coordinate" in self.data and self.data["x_coordinate"] is not None:
|
||||
x_cord = self.data["x_coordinate"]
|
||||
per = False
|
||||
if str(x_cord).endswith("%"):
|
||||
x_cord = x_cord[:-1]
|
||||
per = True
|
||||
x_cord = check_num(x_cord)
|
||||
error = f"Overlay Error: overlay x_coordinate: {self.data['x_coordinate']} must be a number"
|
||||
if x_cord is None:
|
||||
raise Failed(error)
|
||||
if self.x_align != "center" and not per and x_cord < 0:
|
||||
raise Failed(f"{error} 0 or greater")
|
||||
elif self.x_align != "center" and per and x_cord > 100:
|
||||
raise Failed(f"{error} between 0% and 100%")
|
||||
elif self.x_align == "center" and per and (x_cord > 50 or x_cord < -50):
|
||||
raise Failed(f"{error} between -50% and 50%")
|
||||
if per:
|
||||
x_cord = f"{x_cord}%"
|
||||
|
||||
y_cord = None
|
||||
if "y_coordinate" in self.data and self.data["y_coordinate"] is not None:
|
||||
y_cord = self.data["y_coordinate"]
|
||||
per = False
|
||||
if str(y_cord).endswith("%"):
|
||||
y_cord = y_cord[:-1]
|
||||
per = True
|
||||
y_cord = check_num(y_cord)
|
||||
error = f"Overlay Error: overlay y_coordinate: {self.data['y_coordinate']} must be a number"
|
||||
if y_cord is None:
|
||||
raise Failed(error)
|
||||
if self.y_align != "center" and not per and y_cord < 0:
|
||||
raise Failed(f"{error} 0 or greater")
|
||||
elif self.y_align != "center" and per and y_cord > 100:
|
||||
raise Failed(f"{error} between 0% and 100%")
|
||||
elif self.y_align == "center" and per and (y_cord > 50 or y_cord < -50):
|
||||
raise Failed(f"{error} between -50% and 50%")
|
||||
if per:
|
||||
y_cord = f"{y_cord}%"
|
||||
|
||||
if ("x_coordinate" in self.data or "y_coordinate" in self.data) and (x_cord is None or y_cord is None):
|
||||
raise Failed(f"Overlay Error: overlay x_coordinate and overlay y_coordinate must be used together")
|
||||
|
||||
if x_cord is not None or y_cord is not None:
|
||||
self.coordinates = (x_cord, y_cord)
|
||||
|
||||
def get_and_save_image(image_url):
|
||||
response = self.config.get(image_url)
|
||||
if response.status_code >= 400:
|
||||
raise Failed(f"Overlay Error: Overlay Image not found at: {image_url}")
|
||||
if "Content-Type" not in response.headers or response.headers["Content-Type"] != "image/png":
|
||||
raise Failed(f"Overlay Error: Overlay Image not a png: {image_url}")
|
||||
if not os.path.exists(library.overlay_folder) or not os.path.isdir(library.overlay_folder):
|
||||
os.makedirs(library.overlay_folder, exist_ok=False)
|
||||
logger.info(f"Creating Overlay Folder found at: {library.overlay_folder}")
|
||||
clean_image_name, _ = validate_filename(self.name)
|
||||
image_path = os.path.join(library.overlay_folder, f"{clean_image_name}.png")
|
||||
if os.path.exists(image_path):
|
||||
os.remove(image_path)
|
||||
with open(image_path, "wb") as handler:
|
||||
handler.write(response.content)
|
||||
while is_locked(image_path):
|
||||
time.sleep(1)
|
||||
return image_path
|
||||
|
||||
if not self.name.startswith(("blur", "text")):
|
||||
if "file" in self.data and self.data["file"]:
|
||||
self.path = self.data["file"]
|
||||
elif "git" in self.data and self.data["git"]:
|
||||
self.path = get_and_save_image(f"{github_base}{self.data['git']}.png")
|
||||
elif "repo" in self.data and self.data["repo"]:
|
||||
self.path = get_and_save_image(f"{self.config.custom_repo}{self.data['repo']}.png")
|
||||
elif "url" in self.data and self.data["url"]:
|
||||
self.path = get_and_save_image(self.data["url"])
|
||||
|
||||
if self.name.startswith("blur"):
|
||||
try:
|
||||
match = re.search("\\(([^)]+)\\)", self.name)
|
||||
if not match or 0 >= int(match.group(1)) > 100:
|
||||
raise ValueError
|
||||
self.name = f"blur({match.group(1)})"
|
||||
except ValueError:
|
||||
logger.error(f"Overlay Error: failed to parse overlay blur name: {self.name} defaulting to blur(50)")
|
||||
self.name = "blur(50)"
|
||||
elif self.name.startswith("text"):
|
||||
if not self.coordinates:
|
||||
raise Failed(f"Overlay Error: overlay attribute's x_coordinate and y_coordinate are required when using text")
|
||||
match = re.search("\\(([^)]+)\\)", self.name)
|
||||
if not match:
|
||||
raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}")
|
||||
self.name = f"text({match.group(1)})"
|
||||
if os.path.exists("Salma.otf"):
|
||||
self.font_name = "Salma.otf"
|
||||
if "font_size" in self.data and self.data["font_size"] is not None:
|
||||
font_size = check_num(self.data["font_size"])
|
||||
if font_size is None or font_size < 1:
|
||||
logger.error(f"Overlay Error: overlay font_size: {self.data['font_size']} invalid must be a greater than 0")
|
||||
else:
|
||||
self.font_size = font_size
|
||||
if "font" in self.data and self.data["font"]:
|
||||
font = str(self.data["font"])
|
||||
if not os.path.exists(font):
|
||||
fonts = get_system_fonts()
|
||||
if font not in fonts:
|
||||
raise Failed(f"Overlay Error: font: {font} not found. Options: {', '.join(fonts)}")
|
||||
self.font_name = font
|
||||
self.font = ImageFont.truetype(self.font_name, self.font_size)
|
||||
if "font_color" in self.data and self.data["font_color"]:
|
||||
try:
|
||||
color_str = self.data["font_color"]
|
||||
color_str = color_str if color_str.startswith("#") else f"#{color_str}"
|
||||
self.font_color = ImageColor.getcolor(color_str, "RGB")
|
||||
except ValueError:
|
||||
logger.error(f"Overlay Error: overlay color: {self.data['color']} invalid")
|
||||
else:
|
||||
if "|" in self.name:
|
||||
raise Failed(f"Overlay Error: Overlay Name: {self.name} cannot contain '|'")
|
||||
if not self.path:
|
||||
clean_name, _ = validate_filename(self.name)
|
||||
self.path = os.path.join(library.overlay_folder, f"{clean_name}.png")
|
||||
if not os.path.exists(self.path):
|
||||
raise Failed(f"Overlay Error: Overlay Image not found at: {self.path}")
|
||||
image_compare = None
|
||||
if self.config.Cache:
|
||||
_, image_compare, _ = self.config.Cache.query_image_map(self.name, f"{self.library.image_table_name}_overlays")
|
||||
overlay_size = os.stat(self.path).st_size
|
||||
self.updated = not image_compare or str(overlay_size) != str(image_compare)
|
||||
try:
|
||||
self.image = Image.open(self.path).convert("RGBA")
|
||||
if self.config.Cache:
|
||||
self.config.Cache.update_image_map(self.name, f"{self.library.image_table_name}_overlays", self.name, overlay_size)
|
||||
except OSError:
|
||||
raise Failed(f"Overlay Error: overlay image {self.path} failed to load")
|
||||
|
||||
def get_overlay_compare(self):
|
||||
output = self.name
|
||||
if self.group:
|
||||
output += f"{self.group}{self.weight}"
|
||||
if self.coordinates:
|
||||
output += str(self.coordinates)
|
||||
if self.font:
|
||||
output += f"{self.font}{self.font_size}"
|
||||
output += f"{self.coordinates}{self.x_align}{self.y_align}"
|
||||
if self.font_name:
|
||||
output += f"{self.font_name}{self.font_size}"
|
||||
if self.font_color:
|
||||
output += str(self.font_color)
|
||||
return output
|
||||
|
||||
def get_coordinates(self, image_width, image_length, text=None):
|
||||
if text:
|
||||
_, _, width, height = ImageDraw.Draw(Image.new("RGB", (0, 0))).textbbox((0, 0), text, font=self.font)
|
||||
else:
|
||||
width, height = self.image.size
|
||||
x_cord, y_cord = self.coordinates
|
||||
if str(x_cord).endswith("%"):
|
||||
x_cord = image_width * 0.01 * int(x_cord[:-1])
|
||||
if str(y_cord).endswith("%"):
|
||||
y_cord = image_length * 0.01 * int(y_cord[:-1])
|
||||
if self.x_align == "right":
|
||||
x_cord = image_width - width - x_cord
|
||||
elif self.x_align == "center":
|
||||
x_cord = (image_width / 2) - (width / 2) + x_cord
|
||||
if self.x_align == "bottom":
|
||||
y_cord = image_length - height - y_cord
|
||||
elif self.x_align == "center":
|
||||
y_cord = (image_length / 2) - (height / 2) + y_cord
|
||||
return x_cord, y_cord
|
||||
|
|
|
@ -444,7 +444,7 @@ def run_libraries(config):
|
|||
config.Cache.delete_list_ids(list_key)
|
||||
list_key = config.Cache.update_list_cache("library", library.mapping_name, expired, 1)
|
||||
config.Cache.update_list_ids(list_key, [(i.ratingKey, i.guid) for i in temp_items])
|
||||
if not library.is_other and not library.is_music:
|
||||
if not library.is_music:
|
||||
logger.info("")
|
||||
logger.separator(f"Mapping {library.name} Library", space=False, border=False)
|
||||
logger.info("")
|
||||
|
|
Loading…
Reference in a new issue