mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-22 20:43:07 +00:00
[16] fix overlays
This commit is contained in:
parent
3cc24d30ae
commit
5fd0162db8
4 changed files with 80 additions and 37 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.17.0-develop15
|
1.17.0-develop16
|
||||||
|
|
|
@ -103,7 +103,7 @@ class MyLogger:
|
||||||
self._logger.removeHandler(self.playlists_handler)
|
self._logger.removeHandler(self.playlists_handler)
|
||||||
|
|
||||||
def add_collection_handler(self, library_key, collection_key):
|
def add_collection_handler(self, library_key, collection_key):
|
||||||
collection_dir = os.path.join(self.log_dir, library_key, COLLECTION_DIR, collection_key)
|
collection_dir = os.path.join(self.log_dir, str(library_key), COLLECTION_DIR, str(collection_key))
|
||||||
os.makedirs(collection_dir, exist_ok=True)
|
os.makedirs(collection_dir, exist_ok=True)
|
||||||
if library_key not in self.collection_handlers:
|
if library_key not in self.collection_handlers:
|
||||||
self.collection_handlers[library_key] = {}
|
self.collection_handlers[library_key] = {}
|
||||||
|
|
|
@ -164,21 +164,24 @@ class Overlays:
|
||||||
logger.error(f"{item_title[:60]:<60} | Overlay Error: No poster found")
|
logger.error(f"{item_title[:60]:<60} | Overlay Error: No poster found")
|
||||||
elif changed_image or overlay_change:
|
elif changed_image or overlay_change:
|
||||||
try:
|
try:
|
||||||
image_width = 1920 if isinstance(item, Episode) else 1000
|
canvas_width = 1920 if isinstance(item, Episode) else 1000
|
||||||
image_height = 1080 if isinstance(item, Episode) else 1500
|
canvas_height = 1080 if isinstance(item, Episode) else 1500
|
||||||
|
|
||||||
new_poster = Image.open(poster.location if poster else has_original) \
|
new_poster = Image.open(poster.location if poster else has_original) \
|
||||||
.convert("RGB").resize((image_width, image_height), Image.ANTIALIAS)
|
.convert("RGB").resize((canvas_width, canvas_height), Image.ANTIALIAS)
|
||||||
if blur_num > 0:
|
if blur_num > 0:
|
||||||
new_poster = new_poster.filter(ImageFilter.GaussianBlur(blur_num))
|
new_poster = new_poster.filter(ImageFilter.GaussianBlur(blur_num))
|
||||||
for over_name in normal_overlays:
|
for over_name in normal_overlays:
|
||||||
overlay = properties[over_name]
|
overlay = properties[over_name]
|
||||||
if not overlay.has_coordinates():
|
if overlay.has_coordinates():
|
||||||
new_poster = new_poster.resize(overlay.image.size, Image.ANTIALIAS)
|
if overlay.portrait is not None:
|
||||||
overlay_image = overlay.image
|
overlay_image = overlay.landscape if isinstance(item, Episode) else overlay.portrait
|
||||||
else:
|
|
||||||
overlay_image = overlay.landscape if isinstance(item, Episode) else overlay.image
|
|
||||||
new_poster.paste(overlay_image, (0, 0), overlay_image)
|
new_poster.paste(overlay_image, (0, 0), overlay_image)
|
||||||
|
overlay_box = overlay.landscape_box if isinstance(item, Episode) else overlay.portrait_box
|
||||||
|
new_poster.paste(overlay.image, overlay_box, overlay.image)
|
||||||
|
else:
|
||||||
|
new_poster = new_poster.resize(overlay.image.size, Image.ANTIALIAS)
|
||||||
|
new_poster.paste(overlay.image, (0, 0), overlay.image)
|
||||||
for over_name in text_names:
|
for over_name in text_names:
|
||||||
overlay = properties[over_name]
|
overlay = properties[over_name]
|
||||||
text = over_name[5:-1]
|
text = over_name[5:-1]
|
||||||
|
@ -197,9 +200,9 @@ class Overlays:
|
||||||
text = f"{int(text * 10)}%"
|
text = f"{int(text * 10)}%"
|
||||||
if flat and str(text).endswith(".0"):
|
if flat and str(text).endswith(".0"):
|
||||||
text = str(text)[:-2]
|
text = str(text)[:-2]
|
||||||
overlay_image = overlay.get_overlay_image(str(text), image_width, image_height)
|
overlay_image = overlay.get_overlay_image(str(text), (canvas_width, canvas_height))
|
||||||
else:
|
else:
|
||||||
overlay_image = overlay.landscape if isinstance(item, Episode) else overlay.image
|
overlay_image = overlay.landscape if isinstance(item, Episode) else overlay.portrait
|
||||||
new_poster.paste(overlay_image, (0, 0), overlay_image)
|
new_poster.paste(overlay_image, (0, 0), overlay_image)
|
||||||
temp = os.path.join(self.library.overlay_folder, f"temp.png")
|
temp = os.path.join(self.library.overlay_folder, f"temp.png")
|
||||||
new_poster.save(temp, "PNG")
|
new_poster.save(temp, "PNG")
|
||||||
|
|
|
@ -838,6 +838,10 @@ class YAML:
|
||||||
with open(self.path, 'w', encoding="utf-8") as fp:
|
with open(self.path, 'w', encoding="utf-8") as fp:
|
||||||
self.yaml.dump(self.data, fp)
|
self.yaml.dump(self.data, fp)
|
||||||
|
|
||||||
|
|
||||||
|
portrait_dim = (1000, 1500)
|
||||||
|
landscape_dim = (1920, 1080)
|
||||||
|
|
||||||
class Overlay:
|
class Overlay:
|
||||||
def __init__(self, config, library, overlay_data, suppress):
|
def __init__(self, config, library, overlay_data, suppress):
|
||||||
self.config = config
|
self.config = config
|
||||||
|
@ -848,6 +852,9 @@ class Overlay:
|
||||||
self.updated = False
|
self.updated = False
|
||||||
self.image = None
|
self.image = None
|
||||||
self.landscape = None
|
self.landscape = None
|
||||||
|
self.landscape_box = None
|
||||||
|
self.portrait = None
|
||||||
|
self.portrait_box = None
|
||||||
self.group = None
|
self.group = None
|
||||||
self.weight = None
|
self.weight = None
|
||||||
self.path = None
|
self.path = None
|
||||||
|
@ -932,10 +939,13 @@ class Overlay:
|
||||||
self.back_line_width = parse("Overlay", "back_line_width", self.data["back_line_width"], datatype="int", parent="overlay") if "back_line_width" in self.data else None
|
self.back_line_width = parse("Overlay", "back_line_width", self.data["back_line_width"], datatype="int", parent="overlay") if "back_line_width" in self.data else None
|
||||||
self.back_line_color = color("back_line_color")
|
self.back_line_color = color("back_line_color")
|
||||||
self.back_padding = parse("Overlay", "back_padding", self.data["back_padding"], datatype="int", parent="overlay", default=0) if "back_padding" in self.data else 0
|
self.back_padding = parse("Overlay", "back_padding", self.data["back_padding"], datatype="int", parent="overlay", default=0) if "back_padding" in self.data else 0
|
||||||
self.back_width = parse("Overlay", "back_width", self.data["back_width"], datatype="int", parent="overlay") if "back_width" in self.data else None
|
self.back_box = None
|
||||||
self.back_height = parse("Overlay", "back_height", self.data["back_height"], datatype="int", parent="overlay") if "back_height" in self.data else None
|
back_width = parse("Overlay", "back_width", self.data["back_width"], datatype="int", parent="overlay", minimum=0) if "back_width" in self.data else -1
|
||||||
if (self.back_width and not self.back_height) or (self.back_height and not self.back_width):
|
back_height = parse("Overlay", "back_height", self.data["back_height"], datatype="int", parent="overlay", minimum=0) if "back_height" in self.data else -1
|
||||||
|
if (back_width >= 0 and back_height < 0) or (back_height >= 0 and back_width < 0):
|
||||||
raise Failed(f"Overlay Error: overlay attributes back_width and back_height must be used together")
|
raise Failed(f"Overlay Error: overlay attributes back_width and back_height must be used together")
|
||||||
|
elif back_width >= 0 and back_height >= 0:
|
||||||
|
self.back_box = (back_width, back_height)
|
||||||
if (self.back_color or self.back_line_color) and not self.has_coordinates():
|
if (self.back_color or self.back_line_color) and not self.has_coordinates():
|
||||||
raise Failed(f"Overlay Error: horizontal_offset and vertical_offset are required when using a backdrop")
|
raise Failed(f"Overlay Error: horizontal_offset and vertical_offset are required when using a backdrop")
|
||||||
|
|
||||||
|
@ -1003,8 +1013,8 @@ class Overlay:
|
||||||
raise Failed(f"Overlay Error: overlay font_color: {self.data['font_color']} invalid")
|
raise Failed(f"Overlay Error: overlay font_color: {self.data['font_color']} invalid")
|
||||||
text = self.name[5:-1]
|
text = self.name[5:-1]
|
||||||
if text not in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%"]]:
|
if text not in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%"]]:
|
||||||
self.image = self.get_overlay_image(text, 1000, 1500)
|
self.portrait = self.get_backdrop(text, portrait_dim)
|
||||||
self.landscape = self.get_overlay_image(text, 1920, 1080)
|
self.landscape = self.get_backdrop(text, landscape_dim)
|
||||||
else:
|
else:
|
||||||
if "|" in self.name:
|
if "|" in self.name:
|
||||||
raise Failed(f"Overlay Error: Overlay Name: {self.name} cannot contain '|'")
|
raise Failed(f"Overlay Error: Overlay Name: {self.name} cannot contain '|'")
|
||||||
|
@ -1019,32 +1029,63 @@ class Overlay:
|
||||||
overlay_size = os.stat(self.path).st_size
|
overlay_size = os.stat(self.path).st_size
|
||||||
self.updated = not image_compare or str(overlay_size) != str(image_compare)
|
self.updated = not image_compare or str(overlay_size) != str(image_compare)
|
||||||
try:
|
try:
|
||||||
temp_image = Image.open(self.path).convert("RGBA")
|
self.image = Image.open(self.path).convert("RGBA")
|
||||||
self.image = self.get_overlay_image(temp_image, 1000, 1500) if self.has_coordinates() else temp_image
|
if self.has_coordinates():
|
||||||
self.landscape = self.get_overlay_image(temp_image, 1920, 1080) if self.has_coordinates() else temp_image
|
self.portrait, self.portrait_box = self.get_backdrop(portrait_dim, self.image.size)
|
||||||
|
self.landscape, self.landscape_box = self.get_backdrop(landscape_dim, self.image.size)
|
||||||
if self.config.Cache:
|
if self.config.Cache:
|
||||||
self.config.Cache.update_image_map(self.name, f"{self.library.image_table_name}_overlays", self.name, overlay_size)
|
self.config.Cache.update_image_map(self.name, f"{self.library.image_table_name}_overlays", self.name, overlay_size)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise Failed(f"Overlay Error: overlay image {self.path} failed to load")
|
raise Failed(f"Overlay Error: overlay image {self.path} failed to load")
|
||||||
|
|
||||||
def get_overlay_image(self, text, image_width, image_height):
|
def get_backdrop(self, canvas_box, box, text=None):
|
||||||
overlay_image = Image.new("RGBA", (image_width, image_height), (255, 255, 255, 0))
|
overlay_image = None
|
||||||
drawing = ImageDraw.Draw(overlay_image)
|
if text is not None:
|
||||||
if isinstance(text, str):
|
|
||||||
_, _, width, height = self.get_text_size(text)
|
_, _, width, height = self.get_text_size(text)
|
||||||
else:
|
box = (width, height)
|
||||||
width, height = text.size
|
x_cord, y_cord = self.get_coordinates(canvas_box, box)
|
||||||
x_cord, y_cord = self.get_coordinates(image_width, image_height, width, height)
|
if text is not None or self.back_color or self.back_line_color:
|
||||||
|
overlay_image = Image.new("RGBA", canvas_box, (255, 255, 255, 0))
|
||||||
|
drawing = ImageDraw.Draw(overlay_image)
|
||||||
if self.back_color or self.back_line_color:
|
if self.back_color or self.back_line_color:
|
||||||
cords = (
|
cords = (
|
||||||
x_cord - self.back_padding,
|
x_cord - self.back_padding,
|
||||||
y_cord - self.back_padding,
|
y_cord - self.back_padding,
|
||||||
x_cord + (self.back_width if self.back_width else width) + self.back_padding,
|
x_cord + (self.back_box[0] if self.back_box else box[0]) + self.back_padding,
|
||||||
y_cord + (self.back_height if self.back_height else height) + self.back_padding
|
y_cord + (self.back_box[1] if self.back_box else box[1]) + self.back_padding
|
||||||
)
|
)
|
||||||
if self.back_width:
|
if self.back_box:
|
||||||
x_cord = x_cord + (self.back_width - width) // 2
|
x_cord = x_cord + (self.back_box[0] - box[0]) // 2
|
||||||
y_cord = y_cord + (self.back_height - height) // 2
|
y_cord = y_cord + (self.back_box[1] - box[1]) // 2
|
||||||
|
|
||||||
|
if self.back_radius:
|
||||||
|
drawing.rounded_rectangle(cords, fill=self.back_color, outline=self.back_line_color, width=self.back_line_width, radius=self.back_radius)
|
||||||
|
else:
|
||||||
|
drawing.rectangle(cords, fill=self.back_color, outline=self.back_line_color, width=self.back_line_width)
|
||||||
|
|
||||||
|
if text is not None:
|
||||||
|
drawing.text((x_cord, y_cord), text, font=self.font, fill=self.font_color, anchor="lt")
|
||||||
|
return overlay_image, (x_cord, y_cord)
|
||||||
|
|
||||||
|
def get_overlay_image(self, text, canvas_box):
|
||||||
|
overlay_image = Image.new("RGBA", canvas_box, (255, 255, 255, 0))
|
||||||
|
drawing = ImageDraw.Draw(overlay_image)
|
||||||
|
if isinstance(text, str):
|
||||||
|
_, _, width, height = self.get_text_size(text)
|
||||||
|
box = (width, height)
|
||||||
|
else:
|
||||||
|
box = text.size
|
||||||
|
x_cord, y_cord = self.get_coordinates(canvas_box, box)
|
||||||
|
if self.back_color or self.back_line_color:
|
||||||
|
cords = (
|
||||||
|
x_cord - self.back_padding,
|
||||||
|
y_cord - self.back_padding,
|
||||||
|
x_cord + (self.back_box[0] if self.back_box else box[0]) + self.back_padding,
|
||||||
|
y_cord + (self.back_box[1] if self.back_box else box[1]) + self.back_padding
|
||||||
|
)
|
||||||
|
if self.back_box:
|
||||||
|
x_cord = x_cord + (self.back_box[0] - box[0]) // 2
|
||||||
|
y_cord = y_cord + (self.back_box[1] - box[1]) // 2
|
||||||
|
|
||||||
if self.back_radius:
|
if self.back_radius:
|
||||||
drawing.rounded_rectangle(cords, fill=self.back_color, outline=self.back_line_color, width=self.back_line_width, radius=self.back_radius)
|
drawing.rounded_rectangle(cords, fill=self.back_color, outline=self.back_line_color, width=self.back_line_width, radius=self.back_radius)
|
||||||
|
@ -1064,8 +1105,8 @@ class Overlay:
|
||||||
output += f"{self.horizontal_align}{self.horizontal_offset}{self.vertical_offset}{self.vertical_align}"
|
output += f"{self.horizontal_align}{self.horizontal_offset}{self.vertical_offset}{self.vertical_align}"
|
||||||
if self.font_name:
|
if self.font_name:
|
||||||
output += f"{self.font_name}{self.font_size}"
|
output += f"{self.font_name}{self.font_size}"
|
||||||
if self.back_width:
|
if self.back_box:
|
||||||
output += f"{self.back_width}{self.back_height}"
|
output += f"{self.back_box[0]}{self.back_box[1]}"
|
||||||
for value in [self.font_color, self.back_color, self.back_radius, self.back_padding, self.back_line_color, self.back_line_width]:
|
for value in [self.font_color, self.back_color, self.back_radius, self.back_padding, self.back_line_color, self.back_line_width]:
|
||||||
if value is not None:
|
if value is not None:
|
||||||
output += f"{value}"
|
output += f"{value}"
|
||||||
|
@ -1077,12 +1118,11 @@ class Overlay:
|
||||||
def get_text_size(self, text):
|
def get_text_size(self, text):
|
||||||
return ImageDraw.Draw(Image.new("RGBA", (0, 0))).textbbox((0, 0), text, font=self.font, anchor='lt')
|
return ImageDraw.Draw(Image.new("RGBA", (0, 0))).textbbox((0, 0), text, font=self.font, anchor='lt')
|
||||||
|
|
||||||
def get_coordinates(self, image_width, image_height, width, height):
|
def get_coordinates(self, canvas_box, box):
|
||||||
if not self.has_coordinates():
|
if not self.has_coordinates():
|
||||||
return 0, 0
|
return 0, 0
|
||||||
if self.back_width:
|
if self.back_box:
|
||||||
width = self.back_width
|
box = self.back_box
|
||||||
height = self.back_height
|
|
||||||
|
|
||||||
def get_cord(value, image_value, over_value, align):
|
def get_cord(value, image_value, over_value, align):
|
||||||
value = int(image_value * 0.01 * int(value[:-1])) if str(value).endswith("%") else value
|
value = int(image_value * 0.01 * int(value[:-1])) if str(value).endswith("%") else value
|
||||||
|
@ -1093,5 +1133,5 @@ class Overlay:
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return get_cord(self.horizontal_offset, image_width, width, self.horizontal_align), \
|
return get_cord(self.horizontal_offset, canvas_box[0], box[0], self.horizontal_align), \
|
||||||
get_cord(self.vertical_offset, image_height, height, self.vertical_align)
|
get_cord(self.vertical_offset, canvas_box[1], box[1], self.vertical_align)
|
||||||
|
|
Loading…
Reference in a new issue