[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.
@ -66,29 +67,30 @@ 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; |
| `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; | | `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_align` | Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, `right` | &#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; |
| `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; | | `horizontal_align` | Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, `right` | &#10060; |
| `vertical_align` | Vertical Alignment of the overlay.<br>**Values:** `top`, `center`, `bottom` | &#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; |
| `font` | System Font Filename or path to font file for the Text Overlay.<br>**Value:** System Font Filename or path to font file | &#10060; | | `vertical_align` | Vertical Alignment of the overlay.<br>**Values:** `top`, `center`, `bottom` | &#10060; |
| `font_size` | Font Size for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; | | `font` | System Font Filename or path to font file for the Text Overlay.<br>**Value:** System Font Filename or path to font file | &#10060; |
| `font_color` | Font Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | &#10060; | | `font_size` | Font Size for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; |
| `back_color` | Backdrop Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | &#10060; | | `font_color` | Font Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | &#10060; |
| `back_width` | Backdrop Width for the Text Overlay. If `back_width` is not specified the Backdrop Sizes to the text<br>**`back_height` is required when using `back_width`**<br>**Value:** Integer greater than 0 | &#10060; | | `back_color` | Backdrop Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | &#10060; |
| `back_height` | Backdrop Height for the Text Overlay. If `back_height` is not specified the Backdrop Sizes to the text<br>**`back_width` is required when using `back_height`**<br>**Value:** Integer greater than 0 | &#10060; | | `back_width` | Backdrop Width for the Text Overlay. If `back_width` is not specified the Backdrop Sizes to the text<br>**`back_height` is required when using `back_width`**<br>**Value:** Integer greater than 0 | &#10060; |
| `back_padding` | Backdrop Padding for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; | | `back_height` | Backdrop Height for the Text Overlay. If `back_height` is not specified the Backdrop Sizes to the text<br>**`back_width` is required when using `back_height`**<br>**Value:** Integer greater than 0 | &#10060; |
| `back_radius` | Backdrop Radius for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; | | `back_padding` | Backdrop Padding for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; |
| `back_line_color` | Backdrop Line Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | &#10060; | | `back_radius` | Backdrop Radius for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; |
| `back_line_width` | Backdrop Line Width for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; | | `back_line_color` | Backdrop Line Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | &#10060; |
| `back_line_width` | Backdrop Line Width for the Text Overlay.<br>**Value:** Integer greater than 0 | &#10060; |
* If `url`, `git`, and `repo` are all not defined then PMM will look in your `config/overlays` folder for a `.png` file named the same as the `name` attribute. * If `url`, `git`, and `repo` are all not defined then PMM will look in your `config/overlays` folder for a `.png` file named the same as the `name` attribute.
@ -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,9 +644,10 @@ 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 playlist.title not in playlists: if isinstance(playlist, Playlist):
playlists[playlist.title] = [] if playlist.title not in playlists:
playlists[playlist.title].append(username) playlists[playlist.title] = []
playlists[playlist.title].append(username)
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
pass pass
scan_user(self.PlexServer, self.account.title) scan_user(self.PlexServer, self.account.title)
@ -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,54 +852,55 @@ 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__":
if run or test or collections or libraries or metadata_files or resume: try:
start({ if run or test or collections or libraries or metadata_files or resume:
"config_file": config_file, process({
"test": test, "config_file": config_file,
"delete": delete, "test": test,
"ignore_schedules": ignore_schedules, "delete": delete,
"collections": collections, "ignore_schedules": ignore_schedules,
"libraries": libraries, "collections": collections,
"metadata_files": metadata_files, "libraries": libraries,
"library_first": library_first, "metadata_files": metadata_files,
"resume": resume, "library_first": library_first,
"trace": trace "resume": resume,
}) "trace": trace
else: })
times_to_run = util.get_list(times) else:
valid_times = [] times_to_run = util.get_list(times)
for time_to_run in times_to_run: valid_times = []
try: for time_to_run in times_to_run:
valid_times.append(datetime.strftime(datetime.strptime(time_to_run, "%H:%M"), "%H:%M")) try:
except ValueError: valid_times.append(datetime.strftime(datetime.strptime(time_to_run, "%H:%M"), "%H:%M"))
if time_to_run: except ValueError:
raise Failed(f"Argument Error: time argument invalid: {time_to_run} must be in the HH:MM format between 00:00-23:59") if time_to_run:
else: raise Failed(f"Argument Error: time argument invalid: {time_to_run} must be in the HH:MM format between 00:00-23:59")
raise Failed(f"Argument Error: blank time argument") else:
for time_to_run in valid_times: raise Failed(f"Argument Error: blank time argument")
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}) for time_to_run in valid_times:
while True: 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})
schedule.run_pending() while True:
if not no_countdown: schedule.run_pending()
current_time = datetime.now().strftime("%H:%M") if not no_countdown:
seconds = None current_time = datetime.now().strftime("%H:%M")
og_time_str = "" seconds = None
for time_to_run in valid_times: og_time_str = ""
new_seconds = (datetime.strptime(time_to_run, "%H:%M") - datetime.strptime(current_time, "%H:%M")).total_seconds() for time_to_run in valid_times:
if new_seconds < 0: new_seconds = (datetime.strptime(time_to_run, "%H:%M") - datetime.strptime(current_time, "%H:%M")).total_seconds()
new_seconds += 86400 if new_seconds < 0:
if (seconds is None or new_seconds < seconds) and new_seconds > 0: new_seconds += 86400
seconds = new_seconds if (seconds is None or new_seconds < seconds) and new_seconds > 0:
og_time_str = time_to_run seconds = new_seconds
if seconds is not None: og_time_str = time_to_run
hours = int(seconds // 3600) if seconds is not None:
minutes = int((seconds % 3600) // 60) hours = int(seconds // 3600)
time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else "" minutes = int((seconds % 3600) // 60)
time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}" time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else ""
logger.ghost(f"Current Time: {current_time} | {time_str} until the next run at {og_time_str} | Runs: {', '.join(times_to_run)}") time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}"
else: logger.ghost(f"Current Time: {current_time} | {time_str} until the next run at {og_time_str} | Runs: {', '.join(times_to_run)}")
logger.error(f"Time Error: {valid_times}") else:
time.sleep(60) logger.error(f"Time Error: {valid_times}")
except KeyboardInterrupt: time.sleep(60)
logger.separator("Exiting Plex Meta Manager") except KeyboardInterrupt:
logger.separator("Exiting Plex Meta Manager")

Loading…
Cancel
Save