[106] small updates

This commit is contained in:
meisnate12 2022-05-18 13:07:15 -04:00
parent 9d94f00ac7
commit 5369dc4fd8
18 changed files with 116 additions and 64 deletions

View file

@ -16,6 +16,4 @@ Please delete options that are not relevant.
## Checklist ## Checklist
- [ ] My code follows the style guidelines of this project - [ ] My code was submitted to the nightly branch of the repository.
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas

View file

@ -8,7 +8,7 @@
[![Discord](https://img.shields.io/discord/822460010649878528?label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB) [![Discord](https://img.shields.io/discord/822460010649878528?label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB)
[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/meisnate12/plex-meta-manager?style=plastic)](https://hub.docker.com/r/meisnate12/plex-meta-manager) [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/meisnate12/plex-meta-manager?style=plastic)](https://hub.docker.com/r/meisnate12/plex-meta-manager)
[![Read the Docs](https://img.shields.io/readthedocs/plex-meta-manager?style=plastic)](https://metamanager.wiki) [![Wiki](https://img.shields.io/readthedocs/plex-meta-manager?style=plastic)](https://metamanager.wiki)
[![Sponsor or Donate](https://img.shields.io/badge/-Sponsor_or_Donate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12) [![Sponsor or Donate](https://img.shields.io/badge/-Sponsor_or_Donate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12)
Plex Meta Manager is an open source Python 3 project that has been designed to ease the creation and maintenance of metadata, collections, and playlists within a Plex Media Server. The script is designed to be run continuously and be able to update information based on sources outside your plex environment. Plex Meta Manager supports Movie/TV/Music libraries and Playlists. Plex Meta Manager is an open source Python 3 project that has been designed to ease the creation and maintenance of metadata, collections, and playlists within a Plex Media Server. The script is designed to be run continuously and be able to update information based on sources outside your plex environment. Plex Meta Manager supports Movie/TV/Music libraries and Playlists.
@ -21,7 +21,7 @@ Plex Meta Manager is an open source Python 3 project that has been designed to e
3. After that you can start updating Metadata and building automatic Collections by creating a [Metadata File](https://metamanager.wiki/en/latest/metadata/metadata.html) for each Library you want to interact with. 3. After that you can start updating Metadata and building automatic Collections by creating a [Metadata File](https://metamanager.wiki/en/latest/metadata/metadata.html) for each Library you want to interact with.
4. After that, explore the Wiki to see all the different Collection Builders that can be used to create collections. 4. After that, explore the [Wiki](https://metamanager.wiki/) to see all the different Collection Builders that can be used to create collections.
## Walkthroughs ## Walkthroughs
@ -39,7 +39,7 @@ If you find steps 1-3 above daunting, there are some walkthroughs available that
The [develop](https://github.com/meisnate12/Plex-Meta-Manager/tree/develop) branch has the most updated **documented** fixes and enhancements to Plex Meta Manager. This version is tested and documented to some degree, but it is still an active development branch, so there may be rough edges. The [develop](https://github.com/meisnate12/Plex-Meta-Manager/tree/develop) branch has the most updated **documented** fixes and enhancements to Plex Meta Manager. This version is tested and documented to some degree, but it is still an active development branch, so there may be rough edges.
If switching to the develop build, it is recommended to also use the [develop](https://metamanager.wiki/en/develop/) branch of the wiki, which documents any changes made from the Master build. If switching to the develop build, it is recommended to also use the [develop branch of the wiki](https://metamanager.wiki/en/develop/), which documents any changes made from the Master build.
### Nightly ### Nightly
@ -81,7 +81,7 @@ Before posting on GitHub about an enhancement, error, or configuration question
If you are unable to use the [Plex Meta Manager Discord Server](https://discord.gg/NfH6mGFuAB), please follow this guidance: If you are unable to use the [Plex Meta Manager Discord Server](https://discord.gg/NfH6mGFuAB), please follow this guidance:
* If you have an idea for how to enhance Plex Meta Manager please open a new [Feature Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+enhancement&2.feature_request.yml&title=%5BFeature%5D%3A+). * If you have an idea for how to enhance Plex Meta Manager please open a new [Feature Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+enhancement&2.feature_request.yml&title=%5BFeature%5D%3A+).
* If you're getting an Error please update to the latest version and then open a [Bug Report](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+bug&template=1.bug_report.yml&title=%5BBug%5D%3A++) if the error persists. * If you're getting an Error please update to the latest version and then open a [Bug Report](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+bug&template=1.bug_report.yml&title=%5BBug%5D%3A++) if the error persists.
* If you see a mistake/typo with the Plex Meta Manager Wiki or have an idea of how we can improve it please open a [Wiki Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+documentation&template=3.docs_request.yml&title=%5BDocs%5D%3A+) * If you see a mistake/typo with the [Plex Meta Manager Wiki](https://metamanager.wiki/) or have an idea of how we can improve it please open a [Wiki Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+documentation&template=3.docs_request.yml&title=%5BDocs%5D%3A+)
* If you have a metadata configuration query please post in the [Discussions](https://github.com/meisnate12/Plex-Meta-Manager/discussions). * If you have a metadata configuration query please post in the [Discussions](https://github.com/meisnate12/Plex-Meta-Manager/discussions).
## Contributing ## Contributing

View file

@ -1 +1 @@
1.16.5-develop105 1.16.5-develop106

View file

@ -36,6 +36,7 @@ libraries:
- git: meisnate12/ShowCharts - git: meisnate12/ShowCharts
- git: meisnate12/Networks - git: meisnate12/Networks
overlay_path: overlay_path:
- remove_overlays: false
- file: config/Overlays.yml - file: config/Overlays.yml
TV Shows On Second Plex: TV Shows On Second Plex:
library_name: TV Shows library_name: TV Shows
@ -121,6 +122,15 @@ plex:
The `metadata_path` attribute is used to define [Metadata Files](../metadata/metadata) by specifying the path type and path of the files that will be executed against the parent library. See [Path Types](paths) for how to define them. The `metadata_path` attribute is used to define [Metadata Files](../metadata/metadata) by specifying the path type and path of the files that will be executed against the parent library. See [Path Types](paths) for how to define them.
```yaml
libraries:
TV Shows:
metadata_path:
- file: config/TV Shows.yml
- git: meisnate12/ShowCharts
- git: meisnate12/Networks
```
By default, when `metadata_path` is missing the script will look within the root PMM directory for a metadata file called `<MAPPING_NAME>.yml`. In this example, Plex Meta Manager will look for a file named `TV Shows.yml`. By default, when `metadata_path` is missing the script will look within the root PMM directory for a metadata file called `<MAPPING_NAME>.yml`. In this example, Plex Meta Manager will look for a file named `TV Shows.yml`.
```yaml ```yaml

View file

@ -6,6 +6,8 @@ Whilst it is possible to have `python plex-meta-manager.py` running in an open w
Instead, it is recommended to set an automated scheduling service so that Plex Meta Manager can run in the background when scheduled to without any visible impact to the user (other than the Plex libraries and playlists updating). Instead, it is recommended to set an automated scheduling service so that Plex Meta Manager can run in the background when scheduled to without any visible impact to the user (other than the Plex libraries and playlists updating).
**To control how individual parts of PMM are scheduled see the [Schedule detail](../../metadata/details/schedule)**
## Docker Run ## Docker Run
<details> <details>

View file

@ -8,7 +8,7 @@
[![Discord](https://img.shields.io/discord/822460010649878528?label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB) [![Discord](https://img.shields.io/discord/822460010649878528?label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB)
[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/meisnate12/plex-meta-manager?style=plastic)](https://hub.docker.com/r/meisnate12/plex-meta-manager) [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/meisnate12/plex-meta-manager?style=plastic)](https://hub.docker.com/r/meisnate12/plex-meta-manager)
[![Read the Docs](https://img.shields.io/readthedocs/plex-meta-manager?style=plastic)](https://metamanager.wiki) [![Wiki](https://img.shields.io/readthedocs/plex-meta-manager?style=plastic)](https://metamanager.wiki)
[![Sponsor or Donate](https://img.shields.io/badge/-Sponsor_or_Donate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12) [![Sponsor or Donate](https://img.shields.io/badge/-Sponsor_or_Donate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12)
Plex Meta Manager is an open source Python 3 project that has been designed to ease the creation and maintenance of metadata, collections, and playlists within a Plex Media Server. The script is designed to be run continuously and be able to update information based on sources outside your plex environment. Plex Meta Manager supports Movie/TV/Music libraries and Playlists. Plex Meta Manager is an open source Python 3 project that has been designed to ease the creation and maintenance of metadata, collections, and playlists within a Plex Media Server. The script is designed to be run continuously and be able to update information based on sources outside your plex environment. Plex Meta Manager supports Movie/TV/Music libraries and Playlists.
@ -21,7 +21,7 @@ Plex Meta Manager is an open source Python 3 project that has been designed to e
3. After that you can start updating Metadata and building automatic Collections by creating a [Metadata File](metadata/metadata) for each Library you want to interact with. 3. After that you can start updating Metadata and building automatic Collections by creating a [Metadata File](metadata/metadata) for each Library you want to interact with.
4. After that, explore the Wiki to see all the different Collection Builders that can be used to create collections. 4. After that, explore the [Wiki](https://metamanager.wiki/) to see all the different Collection Builders that can be used to create collections.
## Walkthroughs ## Walkthroughs
@ -66,7 +66,7 @@ git checkout master
<br/> <br/>
```` ````
If switching to the develop build, it is recommended to also use the [develop](https://metamanager.wiki/en/develop/) branch of the wiki, which documents any changes made from the Master build. If switching to the develop build, it is recommended to also use the [develop branch of the wiki](https://metamanager.wiki/en/develop/), which documents any changes made from the Master build.
### Nightly ### Nightly
@ -136,7 +136,7 @@ Before posting on GitHub about an enhancement, error, or configuration question
If you are unable to use the [Plex Meta Manager Discord Server](https://discord.gg/NfH6mGFuAB), please follow this guidance: If you are unable to use the [Plex Meta Manager Discord Server](https://discord.gg/NfH6mGFuAB), please follow this guidance:
* If you have an idea for how to enhance Plex Meta Manager please open a new [Feature Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+enhancement&template=feature_request.md&title=Feature+Request%3A+). * If you have an idea for how to enhance Plex Meta Manager please open a new [Feature Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+enhancement&template=feature_request.md&title=Feature+Request%3A+).
* If you're getting an Error please update to the latest version and then open a [Bug Report](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+bug&template=bug_report.md&title=Bug%3A+) if the error persists. * If you're getting an Error please update to the latest version and then open a [Bug Report](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+bug&template=bug_report.md&title=Bug%3A+) if the error persists.
* If you see a mistake/typo with the Plex Meta Manager Wiki or have an idea of how we can improve it please open a [Wiki Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+documentation&template=3.docs_request.yml&title=%5BDocs%5D%3A+) * If you see a mistake/typo with the [Plex Meta Manager Wiki](https://metamanager.wiki/) or have an idea of how we can improve it please open a [Wiki Request](https://github.com/meisnate12/Plex-Meta-Manager/issues/new?assignees=meisnate12&labels=status%3Anot-yet-viewed%2C+documentation&template=3.docs_request.yml&title=%5BDocs%5D%3A+)
* If you have a metadata configuration query please post in the [Discussions](https://github.com/meisnate12/Plex-Meta-Manager/discussions). * If you have a metadata configuration query please post in the [Discussions](https://github.com/meisnate12/Plex-Meta-Manager/discussions).
## Contributing ## Contributing

View file

@ -122,7 +122,7 @@ dynamic_collections:
## Metadata Attributes ## Metadata Attributes
Plex Meta Manager can automatically update items in Plex based on what's defined within the `metadata` attribute. Plex Meta Manager can automatically update items in Plex [Movie](metadata/movie), [Show](metadata/movie), and [Music](metadata/movie) Libraries based on what's defined within the `metadata` attribute.
Each metadata requires its own section within the `metadata` attribute. Each item is defined by the mapping name which must be the same as the item name in the library unless an `alt_title` is specified. Each metadata requires its own section within the `metadata` attribute. Each item is defined by the mapping name which must be the same as the item name in the library unless an `alt_title` is specified.

BIN
fonts/Comfortaa-Bold.ttf Normal file

Binary file not shown.

BIN
fonts/Comfortaa-Light.ttf Normal file

Binary file not shown.

BIN
fonts/Comfortaa-Medium.ttf Normal file

Binary file not shown.

BIN
fonts/Comfortaa-Regular.ttf Normal file

Binary file not shown.

Binary file not shown.

View file

@ -127,6 +127,8 @@ class ConfigFile:
if "settings" in self.data["libraries"][library] and self.data["libraries"][library]["settings"]: if "settings" in self.data["libraries"][library] and self.data["libraries"][library]["settings"]:
if "collection_minimum" in self.data["libraries"][library]["settings"]: if "collection_minimum" in self.data["libraries"][library]["settings"]:
self.data["libraries"][library]["settings"]["minimum_items"] = self.data["libraries"][library]["settings"].pop("collection_minimum") self.data["libraries"][library]["settings"]["minimum_items"] = self.data["libraries"][library]["settings"].pop("collection_minimum")
if "save_missing" in self.data["libraries"][library]["settings"]:
self.data["libraries"][library]["settings"]["save_report"] = self.data["libraries"][library]["settings"].pop("save_missing")
if "radarr" in self.data["libraries"][library] and self.data["libraries"][library]["radarr"]: if "radarr" in self.data["libraries"][library] and self.data["libraries"][library]["radarr"]:
if "add" in self.data["libraries"][library]["radarr"]: if "add" in self.data["libraries"][library]["radarr"]:
self.data["libraries"][library]["radarr"]["add_missing"] = self.data["libraries"][library]["radarr"].pop("add") self.data["libraries"][library]["radarr"]["add_missing"] = self.data["libraries"][library]["radarr"].pop("add")
@ -156,6 +158,8 @@ class ConfigFile:
temp["minimum_items"] = temp.pop("collection_minimum") temp["minimum_items"] = temp.pop("collection_minimum")
if "playlist_sync_to_user" in temp: if "playlist_sync_to_user" in temp:
temp["playlist_sync_to_users"] = temp.pop("playlist_sync_to_user") temp["playlist_sync_to_users"] = temp.pop("playlist_sync_to_user")
if "save_missing" in temp:
temp["save_report"] = temp.pop("save_missing")
self.data["settings"] = temp self.data["settings"] = temp
if "webhooks" in self.data: if "webhooks" in self.data:
temp = self.data.pop("webhooks") temp = self.data.pop("webhooks")

View file

@ -11,27 +11,43 @@ class Letterboxd:
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
def _parse_list(self, list_url, language): def _parse_page(self, list_url, language):
if self.config.trace_mode: list_url = list_url.replace("https://letterboxd.com/films", "https://letterboxd.com/films/ajax")
logger.debug(f"URL: {list_url}")
response = self.config.get_html(list_url, headers=util.header(language)) response = self.config.get_html(list_url, headers=util.header(language))
letterboxd_ids = response.xpath("//li[contains(@class, 'poster-container') or contains(@class, 'film-detail')]/div/@data-film-id") letterboxd_ids = response.xpath(
"//li[contains(@class, 'poster-container') or contains(@class, 'film-detail')]/div/@data-film-id")
items = [] items = []
for letterboxd_id in letterboxd_ids: for letterboxd_id in letterboxd_ids:
slugs = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/@data-film-slug") slugs = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/@data-film-slug")
notes = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']/div/p/text()") if list_url.endswith(("/detail", "/detail/")) else None comments = response.xpath(
ratings = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']//span[contains(@class, 'rating')]/@class") if list_url.endswith(("/detail", "/detail/")) else None f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']/div/p/text()")
years = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']/h2/small/a/text()") if list_url.endswith(("/detail", "/detail/")) else None ratings = response.xpath(
f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']//span[contains(@class, 'rating')]/@class")
years = response.xpath(
f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']/h2/small/a/text()")
rating = None rating = None
if ratings: if ratings:
match = re.search("rated-(\\d+)", ratings[0]) match = re.search("rated-(\\d+)", ratings[0])
if match: if match:
rating = int(match.group(1)) rating = int(match.group(1))
items.append((letterboxd_id, slugs[0], int(years[0]) if years else None, notes[0] if notes else None, rating)) items.append((letterboxd_id, slugs[0],
int(years[0]) if years else None,
comments[0] if comments else None,
rating
))
next_url = response.xpath("//a[@class='next']/@href") next_url = response.xpath("//a[@class='next']/@href")
if len(next_url) > 0: return items, next_url
def _parse_list(self, list_url, limit, language):
if self.config.trace_mode:
logger.debug(f"URL: {list_url}")
items, next_url = self._parse_page(list_url, language)
while len(next_url) > 0:
time.sleep(2) time.sleep(2)
items.extend(self._parse_list(f"{base_url}{next_url[0]}", language)) new_items, next_url = self._parse_page(f"{base_url}{next_url[0]}", language)
items.extend(new_items)
if limit and len(items) >= limit:
return items[:limit]
return items return items
def _tmdb(self, letterboxd_url, language): def _tmdb(self, letterboxd_url, language):
@ -60,13 +76,14 @@ class Letterboxd:
dict_methods = {dm.lower(): dm for dm in letterboxd_dict} dict_methods = {dm.lower(): dm for dm in letterboxd_dict}
final = { final = {
"url": util.parse(err_type, "url", letterboxd_dict, methods=dict_methods, parent="letterboxd_list").strip(), "url": util.parse(err_type, "url", letterboxd_dict, methods=dict_methods, parent="letterboxd_list").strip(),
"limit": util.parse(err_type, "limit", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", default=0) if "limit" in dict_methods else 0,
"note": util.parse(err_type, "note", letterboxd_dict, methods=dict_methods, parent="letterboxd_list") if "note" in dict_methods else None, "note": util.parse(err_type, "note", letterboxd_dict, methods=dict_methods, parent="letterboxd_list") if "note" in dict_methods else None,
"rating": util.parse(err_type, "rating", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", maximum=100, range_split="-") if "rating" in dict_methods else None, "rating": util.parse(err_type, "rating", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", maximum=100, range_split="-") if "rating" in dict_methods else None,
"year": util.parse(err_type, "year", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", minimum=1000, maximum=3000, range_split="-") if "year" in dict_methods else None "year": util.parse(err_type, "year", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", minimum=1000, maximum=3000, range_split="-") if "year" in dict_methods else None
} }
if not final["url"].startswith(base_url): if not final["url"].startswith(base_url):
raise Failed(f"{err_type} Error: {final['url']} must begin with: {base_url}") raise Failed(f"{err_type} Error: {final['url']} must begin with: {base_url}")
elif not self._parse_list(final["url"], language): elif not self._parse_page(final["url"], language)[0]:
raise Failed(f"{err_type} Error: {final['url']} failed to parse") raise Failed(f"{err_type} Error: {final['url']} failed to parse")
valid_lists.append(final) valid_lists.append(final)
return valid_lists return valid_lists
@ -74,7 +91,7 @@ class Letterboxd:
def get_tmdb_ids(self, method, data, language): def get_tmdb_ids(self, method, data, language):
if method == "letterboxd_list": if method == "letterboxd_list":
logger.info(f"Processing Letterboxd List: {data}") logger.info(f"Processing Letterboxd List: {data}")
items = self._parse_list(data["url"], language) items = self._parse_list(data["url"], data["limit"], language)
total_items = len(items) total_items = len(items)
if total_items > 0: if total_items > 0:
ids = [] ids = []

View file

@ -515,6 +515,13 @@ class MetadataFile(DataFile):
methods["key_name_override"] = methods.pop("pre_format_override") methods["key_name_override"] = methods.pop("pre_format_override")
title_override = util.parse("Config", "title_override", dynamic, parent=map_name, methods=methods, datatype="strdict") if "title_override" in methods else {} title_override = util.parse("Config", "title_override", dynamic, parent=map_name, methods=methods, datatype="strdict") if "title_override" in methods else {}
key_name_override = util.parse("Config", "key_name_override", dynamic, parent=map_name, methods=methods, datatype="strdict") if "key_name_override" in methods else {} key_name_override = util.parse("Config", "key_name_override", dynamic, parent=map_name, methods=methods, datatype="strdict") if "key_name_override" in methods else {}
test_override = []
for k, v in key_name_override.items():
if v in test_override:
logger.warning(f"Config Error: {v} can only be used once skipping {k}: {v}")
key_name_override.pop(k)
else:
test_override.append(v)
test = util.parse("Config", "test", dynamic, parent=map_name, methods=methods, default=False, datatype="bool") if "test" in methods else False test = util.parse("Config", "test", dynamic, parent=map_name, methods=methods, default=False, datatype="bool") if "test" in methods else False
sync = util.parse("Config", "sync", dynamic, parent=map_name, methods=methods, default=False, datatype="bool") if "sync" in methods else False sync = util.parse("Config", "sync", dynamic, parent=map_name, methods=methods, default=False, datatype="bool") if "sync" in methods else False
if "<<library_type>>" in title_format: if "<<library_type>>" in title_format:

View file

@ -169,8 +169,6 @@ class Overlays:
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((image_width, image_height), Image.ANTIALIAS)
overlay_image = Image.new('RGBA', new_poster.size, (255, 255, 255, 0))
drawing = ImageDraw.Draw(overlay_image)
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:
@ -178,43 +176,24 @@ class Overlays:
if not overlay.has_coordinates(): if not overlay.has_coordinates():
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, overlay.get_coordinates(image_width, image_height), overlay.image) new_poster.paste(overlay.image, overlay.get_coordinates(image_width, image_height), overlay.image)
if text_names: 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] if text in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%"]]:
if text in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%"]]: per = text.endswith("%")
per = text.endswith("%") rating_type = text[:-1] if per else text
rating_type = text[:-1] if per else text actual = plex.attribute_translation[rating_type]
if not hasattr(item, actual) or getattr(item, actual) is None:
actual = plex.attribute_translation[rating_type] logger.warning(f"Overlay Warning: No {rating_type} found")
if not hasattr(item, actual) or getattr(item, actual) is None: continue
logger.error(f"Overlay Error: No {rating_type} found") text = getattr(item, actual)
continue if self.config.Cache:
text = getattr(item, actual) self.config.Cache.update_overlay_ratings(item.ratingKey, rating_type, text)
if self.config.Cache: if per:
self.config.Cache.update_overlay_ratings(item.ratingKey, rating_type, text) text = f"{int(text * 10)}%"
if per: overlay_image = overlay.get_text_overlay(text, image_width, image_height)
text = f"{int(text * 10)}%" else:
x_cord, y_cord = overlay.get_coordinates(image_width, image_height, text=str(text)) overlay_image = overlay.landscape if isinstance(item, Episode) else overlay.image
_, _, width, height = overlay.get_text_size(str(text))
if overlay.back_color:
cords = (
x_cord - overlay.back_padding,
y_cord - overlay.back_padding,
x_cord + (overlay.back_width if overlay.back_width else width) + overlay.back_padding,
y_cord + (overlay.back_height if overlay.back_height else height) + overlay.back_padding
)
if overlay.back_width:
x_cord = int(x_cord + (overlay.back_width - width) / 2)
y_cord = int(y_cord + (overlay.back_height - height) / 2)
if overlay.back_radius:
drawing.rounded_rectangle(cords, fill=overlay.back_color, outline=overlay.back_line_color,
width=overlay.back_line_width, radius=overlay.back_radius)
else:
drawing.rectangle(cords, fill=overlay.back_color, outline=overlay.back_line_color,
width=overlay.back_line_width)
drawing.text((x_cord, y_cord), str(text), font=overlay.font, fill=overlay.font_color, anchor='lt')
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")

View file

@ -151,6 +151,7 @@ method_alias = {
"labels": "label", "labels": "label",
"collection_minimum": "minimum_items", "collection_minimum": "minimum_items",
"playlist_minimum": "minimum_items", "playlist_minimum": "minimum_items",
"save_missing": "save_report",
"rating": "critic_rating", "rating": "critic_rating",
"show_user_rating": "user_rating", "show_user_rating": "user_rating",
"video_resolution": "resolution", "video_resolution": "resolution",

View file

@ -835,6 +835,7 @@ class Overlay:
self.keys = [] self.keys = []
self.updated = False self.updated = False
self.image = None self.image = None
self.landscape = None
self.group = None self.group = None
self.weight = None self.weight = None
self.path = None self.path = None
@ -993,6 +994,16 @@ class Overlay:
self.back_height = parse("Overlay", "back_height", self.data["back_height"], datatype="int", parent="overlay") self.back_height = parse("Overlay", "back_height", self.data["back_height"], datatype="int", parent="overlay")
if (self.back_width and not self.back_height) or (self.back_height and not self.back_width): if (self.back_width and not self.back_height) or (self.back_height and not self.back_width):
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")
text = self.name[5:-1]
if text not in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%"]]:
self.image = self.get_text_overlay(text, 1000, 1500)
self.landscape = self.get_text_overlay(text, 1920, 1080)
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 '|'")
@ -1013,6 +1024,29 @@ class Overlay:
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_text_overlay(self, text, image_width, image_height):
overlay_image = Image.new("RGBA", (image_width, image_height), (255, 255, 255, 0))
drawing = ImageDraw.Draw(overlay_image)
x_cord, y_cord = self.get_coordinates(image_width, image_height, text=str(text))
_, _, width, height = self.get_text_size(str(text))
if self.back_color:
cords = (
x_cord - self.back_padding,
y_cord - self.back_padding,
x_cord + (self.back_width if self.back_width else width) + self.back_padding,
y_cord + (self.back_height if self.back_height else height) + self.back_padding
)
if self.back_width:
x_cord = x_cord + (self.back_width - width) // 2
y_cord = y_cord + (self.back_height - height) // 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)
drawing.text((x_cord, y_cord), str(text), font=self.font, fill=self.font_color, anchor="lt")
return overlay_image
def get_overlay_compare(self): def get_overlay_compare(self):
output = self.name output = self.name
if self.group: if self.group: