mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-14 00:37:22 +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 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")
|
[![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. | ❌ |
|
| `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`** | ❌ |
|
| `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`** | ❌ |
|
| `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`** | ❌ |
|
| `x_coordinate` | X Coordinate of this overlay. Can be a %. **`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_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` | 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_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` | ❌ |
|
| `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)`
|
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
|
```yaml
|
||||||
overlays:
|
overlays:
|
||||||
audience_rating:
|
audience_rating:
|
||||||
|
@ -113,7 +117,7 @@ overlays:
|
||||||
name: text(audience_rating)
|
name: text(audience_rating)
|
||||||
x_coordinate: 15
|
x_coordinate: 15
|
||||||
y_coordinate: 15
|
y_coordinate: 15
|
||||||
font: arial.ttf
|
font: Salma.otf
|
||||||
font_size: 200
|
font_size: 200
|
||||||
plex_all: true
|
plex_all: true
|
||||||
```
|
```
|
||||||
|
|
|
@ -715,8 +715,8 @@ class ConfigFile:
|
||||||
logger.error("Config Error: operations must be a dictionary")
|
logger.error("Config Error: operations must be a dictionary")
|
||||||
|
|
||||||
def error_check(attr, service):
|
def error_check(attr, service):
|
||||||
|
logger.error(f"Config Error: Operation {attr} cannot be {params[attr]} without a successful {service} Connection")
|
||||||
params[attr] = None
|
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"]:
|
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:
|
if params[mass_key] == "omdb" and self.OMDb is None:
|
||||||
|
|
|
@ -314,7 +314,6 @@ class MetadataFile(DataFile):
|
||||||
auto_list = {}
|
auto_list = {}
|
||||||
all_keys = []
|
all_keys = []
|
||||||
dynamic_data = None
|
dynamic_data = None
|
||||||
logger.debug(exclude)
|
|
||||||
def _check_dict(check_dict):
|
def _check_dict(check_dict):
|
||||||
for ck, cv in check_dict.items():
|
for ck, cv in check_dict.items():
|
||||||
all_keys.append(ck)
|
all_keys.append(ck)
|
||||||
|
@ -527,7 +526,7 @@ class MetadataFile(DataFile):
|
||||||
for template_name in template_names:
|
for template_name in template_names:
|
||||||
if template_name not in self.templates:
|
if template_name not in self.templates:
|
||||||
raise Failed(f"Config Error: {map_name} template: {template_name} not found")
|
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
|
has_var = True
|
||||||
if not has_var:
|
if not has_var:
|
||||||
raise Failed(f"Config Error: One {map_name} template: {template_names} is required to have the template variable <<value>>")
|
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")
|
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
|
||||||
|
image_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("RGBA") \
|
.convert("RGBA").resize((image_width, image_height), Image.ANTIALIAS)
|
||||||
.resize((1920, 1080) if isinstance(item, Episode) else (1000, 1500), 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 overlay.coordinates:
|
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:
|
else:
|
||||||
new_poster = new_poster.resize(overlay.image.size, Image.ANTIALIAS)
|
new_poster = new_poster.resize(overlay.image.size, Image.ANTIALIAS)
|
||||||
new_poster.paste(overlay.image, (0, 0), overlay.image)
|
new_poster.paste(overlay.image, (0, 0), overlay.image)
|
||||||
|
@ -176,7 +178,6 @@ class Overlays:
|
||||||
drawing = ImageDraw.Draw(new_poster)
|
drawing = ImageDraw.Draw(new_poster)
|
||||||
for over_name in text_names:
|
for over_name in text_names:
|
||||||
overlay = properties[over_name]
|
overlay = properties[over_name]
|
||||||
font = ImageFont.truetype(overlay.font, overlay.font_size) if overlay.font else None
|
|
||||||
text = over_name[5:-1]
|
text = over_name[5:-1]
|
||||||
if text in ["audience_rating", "critic_rating", "user_rating"]:
|
if text in ["audience_rating", "critic_rating", "user_rating"]:
|
||||||
rating_type = text
|
rating_type = text
|
||||||
|
@ -187,7 +188,7 @@ class Overlays:
|
||||||
text = getattr(item, actual)
|
text = getattr(item, actual)
|
||||||
if self.config.Cache:
|
if self.config.Cache:
|
||||||
self.config.Cache.update_overlay_ratings(item.ratingKey, rating_type, text)
|
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")
|
temp = os.path.join(self.library.overlay_folder, f"temp.png")
|
||||||
new_poster.save(temp, "PNG")
|
new_poster.save(temp, "PNG")
|
||||||
self.library.upload_poster(item, temp)
|
self.library.upload_poster(item, temp)
|
||||||
|
@ -277,19 +278,6 @@ class Overlays:
|
||||||
for suppress_name in over_obj.suppress:
|
for suppress_name in over_obj.suppress:
|
||||||
if suppress_name in properties and rk in properties[suppress_name].keys:
|
if suppress_name in properties and rk in properties[suppress_name].keys:
|
||||||
properties[suppress_name].keys.remove(rk)
|
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 overlay_name, over_obj in properties.items():
|
||||||
for over_key in over_obj.keys:
|
for over_key in over_obj.keys:
|
||||||
|
|
|
@ -439,7 +439,7 @@ class Plex(Library):
|
||||||
if label:
|
if label:
|
||||||
label_id = next((c.key for c in self.get_tags("label") if c.title == label), None)
|
label_id = next((c.key for c in self.get_tags("label") if c.title == label), None)
|
||||||
if label_id:
|
if label_id:
|
||||||
args = f"{args}&{label_id}"
|
args = f"{args}&label={label_id}"
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
return self.get_filter_items(args)
|
return self.get_filter_items(args)
|
||||||
|
|
113
modules/util.py
113
modules/util.py
|
@ -4,7 +4,7 @@ from pathvalidate import is_valid_filename, sanitize_filename
|
||||||
from plexapi.audio import Album, Track
|
from plexapi.audio import Album, Track
|
||||||
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
|
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
|
||||||
from plexapi.video import Season, Episode, Movie
|
from plexapi.video import Season, Episode, Movie
|
||||||
from PIL import ImageColor
|
from PIL import Image, ImageColor, ImageDraw, ImageFont
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import msvcrt
|
import msvcrt
|
||||||
|
@ -840,12 +840,16 @@ class Overlay:
|
||||||
self.path = None
|
self.path = None
|
||||||
self.coordinates = None
|
self.coordinates = None
|
||||||
self.font = None
|
self.font = None
|
||||||
|
self.font_name = None
|
||||||
self.font_size = 12
|
self.font_size = 12
|
||||||
self.font_color = None
|
self.font_color = None
|
||||||
logger.debug("")
|
logger.debug("")
|
||||||
logger.debug("Validating Method: overlay")
|
logger.debug("Validating Method: overlay")
|
||||||
logger.debug(f"Value: {self.data}")
|
logger.debug(f"Value: {self.data}")
|
||||||
if isinstance(self.data, dict):
|
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"]:
|
if "name" not in self.data or not self.data["name"]:
|
||||||
raise Failed(f"Overlay Error: overlay must have the name attribute")
|
raise Failed(f"Overlay Error: overlay must have the name attribute")
|
||||||
self.name = str(self.data["name"])
|
self.name = str(self.data["name"])
|
||||||
|
@ -860,18 +864,52 @@ class Overlay:
|
||||||
if ("group" in self.data or "weight" in self.data) and (self.weight is None or not self.group):
|
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")
|
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
|
x_cord = None
|
||||||
y_cord = None
|
|
||||||
if "x_coordinate" in self.data and self.data["x_coordinate"] is not None:
|
if "x_coordinate" in self.data and self.data["x_coordinate"] is not None:
|
||||||
x_cord = check_num(self.data["x_coordinate"])
|
x_cord = self.data["x_coordinate"]
|
||||||
if x_cord is None or x_cord < 0:
|
per = False
|
||||||
raise Failed(f"Overlay Error: overlay x_coordinate: {self.data['x_coordinate']} must be a number 0 or greater")
|
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:
|
if "y_coordinate" in self.data and self.data["y_coordinate"] is not None:
|
||||||
y_cord = check_num(self.data["y_coordinate"])
|
y_cord = self.data["y_coordinate"]
|
||||||
if y_cord is None or y_cord < 0:
|
per = False
|
||||||
raise Failed(f"Overlay Error: overlay y_coordinate: {self.data['y_coordinate']} must be a number 0 or greater")
|
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):
|
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")
|
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:
|
if x_cord is not None or y_cord is not None:
|
||||||
self.coordinates = (x_cord, y_cord)
|
self.coordinates = (x_cord, y_cord)
|
||||||
|
|
||||||
|
@ -920,19 +958,22 @@ class Overlay:
|
||||||
if not match:
|
if not match:
|
||||||
raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}")
|
raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}")
|
||||||
self.name = f"text({match.group(1)})"
|
self.name = f"text({match.group(1)})"
|
||||||
if "font" in self.data and self.data["font"]:
|
if os.path.exists("Salma.otf"):
|
||||||
font = str(self.data["font"])
|
self.font_name = "Salma.otf"
|
||||||
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:
|
if "font_size" in self.data and self.data["font_size"] is not None:
|
||||||
font_size = check_num(self.data["font_size"])
|
font_size = check_num(self.data["font_size"])
|
||||||
if font_size is None or font_size < 1:
|
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")
|
logger.error(f"Overlay Error: overlay font_size: {self.data['font_size']} invalid must be a greater than 0")
|
||||||
else:
|
else:
|
||||||
self.font_size = font_size
|
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"]:
|
if "font_color" in self.data and self.data["font_color"]:
|
||||||
try:
|
try:
|
||||||
color_str = self.data["font_color"]
|
color_str = self.data["font_color"]
|
||||||
|
@ -948,18 +989,46 @@ class Overlay:
|
||||||
self.path = os.path.join(library.overlay_folder, f"{clean_name}.png")
|
self.path = os.path.join(library.overlay_folder, f"{clean_name}.png")
|
||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
raise Failed(f"Overlay Error: Overlay Image not found at: {self.path}")
|
raise Failed(f"Overlay Error: Overlay Image not found at: {self.path}")
|
||||||
else:
|
image_compare = None
|
||||||
self.name = str(self.data)
|
if self.config.Cache:
|
||||||
logger.warning(f"Overlay Warning: No overlay attribute using mapping name {self.data} as the overlay name")
|
_, 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):
|
def get_overlay_compare(self):
|
||||||
output = self.name
|
output = self.name
|
||||||
if self.group:
|
if self.group:
|
||||||
output += f"{self.group}{self.weight}"
|
output += f"{self.group}{self.weight}"
|
||||||
if self.coordinates:
|
if self.coordinates:
|
||||||
output += str(self.coordinates)
|
output += f"{self.coordinates}{self.x_align}{self.y_align}"
|
||||||
if self.font:
|
if self.font_name:
|
||||||
output += f"{self.font}{self.font_size}"
|
output += f"{self.font_name}{self.font_size}"
|
||||||
if self.font_color:
|
if self.font_color:
|
||||||
output += str(self.font_color)
|
output += str(self.font_color)
|
||||||
return output
|
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)
|
config.Cache.delete_list_ids(list_key)
|
||||||
list_key = config.Cache.update_list_cache("library", library.mapping_name, expired, 1)
|
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])
|
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.info("")
|
||||||
logger.separator(f"Mapping {library.name} Library", space=False, border=False)
|
logger.separator(f"Mapping {library.name} Library", space=False, border=False)
|
||||||
logger.info("")
|
logger.info("")
|
||||||
|
|
Loading…
Reference in a new issue