[25] more minor fixes

pull/921/head
meisnate12 3 years ago
parent 6d27ca508c
commit 64e67d4109

@ -1 +1 @@
1.17.0-develop24 1.17.0-develop25

@ -24,6 +24,8 @@ plex:
| `empty_trash` | Runs Empty Trash on the Server after all Metadata Files are run | false | ❌ | | `empty_trash` | Runs Empty Trash on the Server after all Metadata Files are run | false | ❌ |
| `optimize` | Runs Optimize on the Server after all Metadata Files are run | false | ❌ | | `optimize` | Runs Optimize on the Server after all Metadata Files are run | false | ❌ |
* **Do Not Use the Plex Token found in Plex's Preferences.xml file**
* This script can be run on a remote Plex server, but be sure that the `url` provided is publicly addressable, and it's recommended to use `HTTPS`. * This script can be run on a remote Plex server, but be sure that the `url` provided is publicly addressable, and it's recommended to use `HTTPS`.
* If you need help finding your Plex authentication token, please see Plex's [support article](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/). * If you need help finding your Plex authentication token, please see Plex's [support article](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/).

@ -106,7 +106,7 @@ Specify the time of day that Plex Meta Manager will run.
</tr> </tr>
<tr> <tr>
<th>Default Value</th> <th>Default Value</th>
<td colspan="2"><code>03:00</code></td> <td colspan="2"><code>05:00</code></td>
</tr> </tr>
<tr> <tr>
<th>Available Values</th> <th>Available Values</th>

@ -16,6 +16,7 @@ These are the attributes which can be used within the Overlay File:
|:--------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------| |:--------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|
| [`templates`](templates) | contains definitions of templates that can be leveraged by multiple overlays | | [`templates`](templates) | contains definitions of templates that can be leveraged by multiple overlays |
| [`external_templates`](templates.md#external-templates) | contains [path types](../config/paths) that point to external templates that can be leveraged by multiple overlays | | [`external_templates`](templates.md#external-templates) | contains [path types](../config/paths) that point to external templates that can be leveraged by multiple overlays |
| [`queues`](#overlay-queues) | contains the positional attributes of queues |
| [`overlays`](#overlays-attributes) | contains definitions of overlays you wish to add | | [`overlays`](#overlays-attributes) | contains definitions of overlays you wish to add |
* `overlays` is required in order to run the Overlay File. * `overlays` is required in order to run the Overlay File.
@ -67,14 +68,15 @@ overlays:
There are many attributes available when using overlays to edit how they work. There are many attributes available when using overlays to edit how they work.
| Attribute | Description | Required | | Attribute | Description | Required |
|:--------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:| |:---------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|
| `name` | Name of the overlay. Each overlay name should be unique. | &#9989; | | `name` | Name of the overlay. Each overlay name should be unique. | &#9989; |
| `file` | Local location of the Overlay Image. | &#10060; | | `file` | Local location of the Overlay Image. | &#10060; |
| `url` | URL of Overlay Image Online. | &#10060; | | `url` | URL of Overlay Image Online. | &#10060; |
| `git` | Location in the [Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) of the Overlay Image. | &#10060; | | `git` | Location in the [Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) of the Overlay Image. | &#10060; |
| `repo` | Location in the [Custom Repo](../config/settings.md#custom-repo) of the Overlay Image. | &#10060; | | `repo` | Location in the [Custom Repo](../config/settings.md#custom-repo) of the Overlay Image. | &#10060; |
| `group` | Name of the Grouping for this overlay. Only one overlay with the highest weight per group will be applied.<br>**`weight` is required when using `group`**<br>**Values:** group name | &#10060; | | [`group`](#overlay-groups) | Name of the Grouping for this overlay. Only one overlay with the highest weight per group will be applied.<br>**`weight` is required when using `group`**<br>**Values:** group name | &#10060; |
| `weight` | Weight of this overlay in its group.<br>**`group` is required when using `weight`**<br>**Values:** Integer | &#10060; | | [`queue`](#overlay-queues) | Name of the Queue for this overlay. Define `queue` positions using the `queues` attribute at the top level of an Overlay File. Overlay with the highest weight is applied to the first position and so on.<br>**`weight` is required when using `queue`**<br>**Values:** queue name | &#10060; |
| `weight` | Weight of this overlay in its group or queue.<br>**`group` or `queue` is required when using `weight`**<br>**Values:** Integer 0 or greater | &#10060; |
| `horizontal_offset` | Horizontal Offset of this overlay. Can be a %.<br>**`vertical_offset` is required when using `horizontal_offset`**<br>**Value:** Integer 0 or greater or 0%-100% | &#10060; | | `horizontal_offset` | Horizontal Offset of this overlay. Can be a %.<br>**`vertical_offset` is required when using `horizontal_offset`**<br>**Value:** Integer 0 or greater or 0%-100% | &#10060; |
| `horizontal_align` | Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, `right` | &#10060; | | `horizontal_align` | Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, `right` | &#10060; |
| `vertical_offset` | Vertical Offset of this overlay. Can be a %.<br>**`horizontal_offset` is required when using `vertical_offset`**<br>**Value:** Integer 0 or greater or 0%-100% | &#10060; | | `vertical_offset` | Vertical Offset of this overlay. Can be a %.<br>**`horizontal_offset` is required when using `vertical_offset`**<br>**Value:** Integer 0 or greater or 0%-100% | &#10060; |
@ -108,7 +110,7 @@ overlays:
imdb_chart: top_movies imdb_chart: top_movies
overlay: overlay:
name: IMDB-Top-250 name: IMDB-Top-250
repo: PMM/overlays/images/IMDB-Top-250 git: PMM/overlays/images/IMDB-Top-250
horizontal_offset: 0 horizontal_offset: 0
horizontal_align: right horizontal_align: right
vertical_offset: 0 vertical_offset: 0
@ -134,7 +136,6 @@ overlays:
![](blur.png) ![](blur.png)
### Text Overlay ### Text Overlay
You can add text as an overlay using the special `text()` overlay name. Anything inside the parentheses will be added as an overlay onto the image. Ex `text(4K)` adds `4K` to the image. You can add text as an overlay using the special `text()` overlay name. Anything inside the parentheses will be added as an overlay onto the image. Ex `text(4K)` adds `4K` to the image.
@ -173,6 +174,89 @@ overlays:
back_height: 105 back_height: 105
``` ```
### Overlay Groups
Overlay groups are defined by the name given to the `group` attribute. Only one overlay with the highest weight per group will be applied.
This is an example where the Multi-Audio overlay will be applied over the Dual-Audio overlay for every item found by both.
```yaml
overlays:
Dual-Audio:
overlay:
name: Dual-Audio
git: PMM/overlays/images/Dual-Audio
group: audio_language
weight: 10
horizontal_offset: 0
horizontal_align: center
vertical_offset: 15
vertical_align: bottom
plex_all: true
filters:
audio_language.count_gt: 1
Multi-Audio:
overlay:
name: Multi-Audio
git: PMM/overlays/images/Multi-Audio
group: audio_language
weight: 20
horizontal_offset: 0
horizontal_align: center
vertical_offset: 15
vertical_align: bottom
plex_all: true
filters:
audio_language.count_gt: 2
```
### Overlay Queues
Overlay queues are defined by the name given to the `queue` attribute. The overlay with the highest weight is put into the first queue position, then the second highest is placed in the second queue position and so on.
You can define the queue positions by using the `queues` attribute at the top level of an Overlay File. You can define as many positions as you want.
```yaml
queues:
custom_queue_name:
- horizontal_offset: 300 # This is the first position
horizontal_align: center
vertical_offset: 1375
vertical_align: top
- horizontal_offset: 300 # This is the second position
horizontal_align: center
vertical_offset: 1250
vertical_align: top
overlays:
IMDb:
imdb_chart: popular_movies
overlay:
name: text(IMDb Popular)
queue: custom_queue_name
weight: 20
font: fonts/Inter-Medium.ttf
font_size: 65
font_color: "#FFFFFF"
back_color: "#00000099"
back_radius: 30
back_width: 380
back_height: 105
TMDb:
tmdb_popular: 100
overlay:
name: text(TMDb Popular)
queue: custom_queue_name
weight: 10
font: fonts/Inter-Medium.ttf
font_size: 65
font_color: "#FFFFFF"
back_color: "#00000099"
back_radius: 30
back_width: 400
back_height: 105
```
## Suppress Overlays ## Suppress Overlays
You can add `suppress_overlays` to an overlay definition and give it a list or comma separated string of overlay names you want suppressed from this item if this overlay is attached to the item. You can add `suppress_overlays` to an overlay definition and give it a list or comma separated string of overlay names you want suppressed from this item if this overlay is attached to the item.

@ -2293,7 +2293,7 @@ class CollectionBuilder:
tmdb_paths = [] tmdb_paths = []
tvdb_paths = [] tvdb_paths = []
for item in self.items: for item in self.items:
if "item_assets" in self.item_details and self.library.asset_directory and "Overlay" not in [la.tag for la in item.labels]: if "item_assets" in self.item_details and self.library.asset_directory and "Overlay" not in [la.tag for la in self.library.item_labels(item)]:
self.library.find_and_upload_assets(item) self.library.find_and_upload_assets(item)
self.library.edit_tags("label", item, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags) self.library.edit_tags("label", item, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags)
path = os.path.dirname(str(item.locations[0])) if self.library.is_movie else str(item.locations[0]) path = os.path.dirname(str(item.locations[0])) if self.library.is_movie else str(item.locations[0])

@ -79,7 +79,7 @@ class Operations:
logger.error(e) logger.error(e)
continue continue
logger.ghost(f"Processing: {i}/{len(items)} {item.title}") logger.ghost(f"Processing: {i}/{len(items)} {item.title}")
current_labels = [la.tag for la in item.labels] if self.library.assets_for_all or self.library.mass_imdb_parental_labels else [] current_labels = [la.tag for la in self.library.item_labels(item)] if self.library.assets_for_all or self.library.mass_imdb_parental_labels else []
if self.library.assets_for_all and self.library.asset_directory and "Overlay" not in current_labels: if self.library.assets_for_all and self.library.asset_directory and "Overlay" not in current_labels:
self.library.find_and_upload_assets(item) self.library.find_and_upload_assets(item)

@ -86,7 +86,7 @@ class Overlays:
image, image_compare, overlay_compare = self.config.Cache.query_image_map(item.ratingKey, f"{self.library.image_table_name}_overlays") image, image_compare, overlay_compare = self.config.Cache.query_image_map(item.ratingKey, f"{self.library.image_table_name}_overlays")
overlay_compare = [] if overlay_compare is None else util.get_list(overlay_compare, split="|") overlay_compare = [] if overlay_compare is None else util.get_list(overlay_compare, split="|")
has_overlay = any([item_tag.tag.lower() == "overlay" for item_tag in item.labels]) has_overlay = any([item_tag.tag.lower() == "overlay" for item_tag in self.library.item_labels(item)])
compare_names = {properties[ov].get_overlay_compare(): ov for ov in over_names} compare_names = {properties[ov].get_overlay_compare(): ov for ov in over_names}
blur_num = 0 blur_num = 0

@ -529,6 +529,10 @@ class Plex(Library):
def collection_order_query(self, collection, data): def collection_order_query(self, collection, data):
collection.sortUpdate(sort=data) collection.sortUpdate(sort=data)
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
def item_labels(self, item):
return item.labels
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
def reload(self, item, force=False): def reload(self, item, force=False):
is_full = False is_full = False
@ -640,6 +644,7 @@ class Plex(Library):
def scan_user(server, username): def scan_user(server, username):
try: try:
for playlist in server.playlists(): for playlist in server.playlists():
if isinstance(playlist, Playlist):
if playlist.title not in playlists: if playlist.title not in playlists:
playlists[playlist.title] = [] playlists[playlist.title] = []
playlists[playlist.title].append(username) playlists[playlist.title].append(username)
@ -1244,7 +1249,7 @@ class Plex(Library):
if filter_attr == "has_collection": if filter_attr == "has_collection":
filter_check = len(item.collections) > 0 filter_check = len(item.collections) > 0
elif filter_attr == "has_overlay": elif filter_attr == "has_overlay":
for label in item.labels: for label in self.item_labels(item):
if label.tag.lower().endswith(" overlay") or label.tag.lower() == "overlay": if label.tag.lower().endswith(" overlay") or label.tag.lower() == "overlay":
filter_check = True filter_check = True
break break

@ -945,7 +945,7 @@ class Overlay:
self.horizontal_align, self.horizontal_offset, self.vertical_align, self.vertical_offset = parse_cords(self.data, "overlay") self.horizontal_align, self.horizontal_offset, self.vertical_align, self.vertical_offset = parse_cords(self.data, "overlay")
if (self.horizontal_offset is None and self.vertical_offset is not None) or (self.vertical_offset is None and self.horizontal_offset is not None): if (self.horizontal_offset is None and self.vertical_offset is not None) or (self.vertical_offset is None and self.horizontal_offset is not None):
raise Failed(f"Overlay Error: overlay attribute's must be used together") raise Failed(f"Overlay Error: overlay attribute's horizontal_offset and vertical_offset must be used together")
def color(attr): def color(attr):
if attr in self.data and self.data[attr]: if attr in self.data and self.data[attr]:
@ -966,7 +966,7 @@ class Overlay:
elif back_width >= 0 and back_height >= 0: elif back_width >= 0 and back_height >= 0:
self.back_box = (back_width, back_height) self.back_box = (back_width, back_height)
self.has_back = True if self.back_color or self.back_line_color else False self.has_back = True if self.back_color or self.back_line_color else False
if self.has_back and not self.has_coordinates(): if self.has_back and not self.has_coordinates() and not self.queue:
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")
def get_and_save_image(image_url): def get_and_save_image(image_url):

@ -1,4 +1,5 @@
import argparse, os, sys, time, traceback, uuid import argparse, os, sys, time, uuid
from concurrent.futures import ProcessPoolExecutor
from datetime import datetime from datetime import datetime
try: try:
@ -18,7 +19,7 @@ parser = argparse.ArgumentParser()
parser.add_argument("-db", "--debug", dest="debug", help=argparse.SUPPRESS, action="store_true", default=False) parser.add_argument("-db", "--debug", dest="debug", help=argparse.SUPPRESS, action="store_true", default=False)
parser.add_argument("-tr", "--trace", dest="trace", help=argparse.SUPPRESS, action="store_true", default=False) parser.add_argument("-tr", "--trace", dest="trace", help=argparse.SUPPRESS, action="store_true", default=False)
parser.add_argument("-c", "--config", dest="config", help="Run with desired *.yml file", type=str) parser.add_argument("-c", "--config", dest="config", help="Run with desired *.yml file", type=str)
parser.add_argument("-t", "--time", "--times", dest="times", help="Times to update each day use format HH:MM (Default: 03:00) (comma-separated list)", default="05:00", type=str) parser.add_argument("-t", "--time", "--times", dest="times", help="Times to update each day use format HH:MM (Default: 05:00) (comma-separated list)", default="05:00", type=str)
parser.add_argument("-re", "--resume", dest="resume", help="Resume collection run from a specific collection", type=str) parser.add_argument("-re", "--resume", dest="resume", help="Resume collection run from a specific collection", type=str)
parser.add_argument("-r", "--run", dest="run", help="Run without the scheduler", action="store_true", default=False) parser.add_argument("-r", "--run", dest="run", help="Run without the scheduler", action="store_true", default=False)
parser.add_argument("-is", "--ignore-schedules", dest="ignore_schedules", help="Run ignoring collection schedules", action="store_true", default=False) parser.add_argument("-is", "--ignore-schedules", dest="ignore_schedules", help="Run ignoring collection schedules", action="store_true", default=False)
@ -115,8 +116,10 @@ from modules.config import ConfigFile
from modules.util import Failed, NotScheduled, Deleted from modules.util import Failed, NotScheduled, Deleted
def my_except_hook(exctype, value, tb): def my_except_hook(exctype, value, tb):
for _line in traceback.format_exception(etype=exctype, value=value, tb=tb): if issubclass(exctype, KeyboardInterrupt):
logger.critical(_line) sys.__excepthook__(exctype, value, tb)
else:
logger.critical("Uncaught Exception", exc_info=(exctype, value, tb))
sys.excepthook = my_except_hook sys.excepthook = my_except_hook
@ -144,6 +147,10 @@ if not uuid_num:
plexapi.BASE_HEADERS["X-Plex-Client-Identifier"] = str(uuid_num) plexapi.BASE_HEADERS["X-Plex-Client-Identifier"] = str(uuid_num)
def process(attrs):
with ProcessPoolExecutor(max_workers=1) as executor:
executor.submit(start, *[attrs])
def start(attrs): def start(attrs):
logger.add_main_handler() logger.add_main_handler()
logger.separator() logger.separator()
@ -845,9 +852,10 @@ def run_playlists(config):
logger.remove_playlist_handler(playlist_log_name) logger.remove_playlist_handler(playlist_log_name)
return status, stats return status, stats
try: if __name__ == "__main__":
try:
if run or test or collections or libraries or metadata_files or resume: if run or test or collections or libraries or metadata_files or resume:
start({ process({
"config_file": config_file, "config_file": config_file,
"test": test, "test": test,
"delete": delete, "delete": delete,
@ -871,7 +879,7 @@ try:
else: else:
raise Failed(f"Argument Error: blank time argument") raise Failed(f"Argument Error: blank time argument")
for time_to_run in valid_times: for time_to_run in valid_times:
schedule.every().day.at(time_to_run).do(start, {"config_file": config_file, "time": time_to_run, "delete": delete, "library_first": library_first, "trace": trace}) schedule.every().day.at(time_to_run).do(process, {"config_file": config_file, "time": time_to_run, "delete": delete, "library_first": library_first, "trace": trace})
while True: while True:
schedule.run_pending() schedule.run_pending()
if not no_countdown: if not no_countdown:
@ -894,5 +902,5 @@ try:
else: else:
logger.error(f"Time Error: {valid_times}") logger.error(f"Time Error: {valid_times}")
time.sleep(60) time.sleep(60)
except KeyboardInterrupt: except KeyboardInterrupt:
logger.separator("Exiting Plex Meta Manager") logger.separator("Exiting Plex Meta Manager")

Loading…
Cancel
Save