mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
[25] more minor fixes
This commit is contained in:
parent
6d27ca508c
commit
64e67d4109
10 changed files with 190 additions and 91 deletions
2
VERSION
2
VERSION
|
@ -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 | ❌ |
|
||||
| `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`.
|
||||
|
||||
* 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>
|
||||
<th>Default Value</th>
|
||||
<td colspan="2"><code>03:00</code></td>
|
||||
<td colspan="2"><code>05:00</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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 |
|
||||
| [`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` 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.
|
||||
|
||||
| Attribute | Description | Required |
|
||||
|:--------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|
|
||||
| `name` | Name of the overlay. Each overlay name should be unique. | ✅ |
|
||||
| `file` | Local location of the Overlay Image. | ❌ |
|
||||
| `url` | URL of Overlay Image Online. | ❌ |
|
||||
| `git` | Location in the [Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) 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. Only one overlay with the highest weight per group will be applied.<br>**`weight` is required when using `group`**<br>**Values:** group name | ❌ |
|
||||
| `weight` | Weight of this overlay in its group.<br>**`group` is required when using `weight`**<br>**Values:** Integer | ❌ |
|
||||
| `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% | ❌ |
|
||||
| `horizontal_align` | Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, `right` | ❌ |
|
||||
| `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% | ❌ |
|
||||
| `vertical_align` | Vertical Alignment of the overlay.<br>**Values:** `top`, `center`, `bottom` | ❌ |
|
||||
| `font` | System Font Filename or path to font file for the Text Overlay.<br>**Value:** System Font Filename or path to font file | ❌ |
|
||||
| `font_size` | Font Size for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| `font_color` | Font Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ |
|
||||
| `back_color` | Backdrop Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ |
|
||||
| `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 | ❌ |
|
||||
| `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 | ❌ |
|
||||
| `back_padding` | Backdrop Padding for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| `back_radius` | Backdrop Radius for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| `back_line_color` | Backdrop Line Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ |
|
||||
| `back_line_width` | Backdrop Line Width for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| Attribute | Description | Required |
|
||||
|:---------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|
|
||||
| `name` | Name of the overlay. Each overlay name should be unique. | ✅ |
|
||||
| `file` | Local location of the Overlay Image. | ❌ |
|
||||
| `url` | URL of Overlay Image Online. | ❌ |
|
||||
| `git` | Location in the [Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) of the Overlay Image. | ❌ |
|
||||
| `repo` | Location in the [Custom Repo](../config/settings.md#custom-repo) of the Overlay Image. | ❌ |
|
||||
| [`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 | ❌ |
|
||||
| [`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 | ❌ |
|
||||
| `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 | ❌ |
|
||||
| `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% | ❌ |
|
||||
| `horizontal_align` | Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, `right` | ❌ |
|
||||
| `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% | ❌ |
|
||||
| `vertical_align` | Vertical Alignment of the overlay.<br>**Values:** `top`, `center`, `bottom` | ❌ |
|
||||
| `font` | System Font Filename or path to font file for the Text Overlay.<br>**Value:** System Font Filename or path to font file | ❌ |
|
||||
| `font_size` | Font Size for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| `font_color` | Font Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ |
|
||||
| `back_color` | Backdrop Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ |
|
||||
| `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 | ❌ |
|
||||
| `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 | ❌ |
|
||||
| `back_padding` | Backdrop Padding for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| `back_radius` | Backdrop Radius for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
| `back_line_color` | Backdrop Line Color for the Text Overlay.<br>**Value:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`. | ❌ |
|
||||
| `back_line_width` | Backdrop Line Width for the Text Overlay.<br>**Value:** Integer greater than 0 | ❌ |
|
||||
|
||||
* 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
|
||||
overlay:
|
||||
name: IMDB-Top-250
|
||||
repo: PMM/overlays/images/IMDB-Top-250
|
||||
git: PMM/overlays/images/IMDB-Top-250
|
||||
horizontal_offset: 0
|
||||
horizontal_align: right
|
||||
vertical_offset: 0
|
||||
|
@ -134,7 +136,6 @@ overlays:
|
|||
|
||||
![](blur.png)
|
||||
|
||||
|
||||
### 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.
|
||||
|
@ -173,6 +174,89 @@ overlays:
|
|||
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
|
||||
|
||||
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 = []
|
||||
tvdb_paths = []
|
||||
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.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])
|
||||
|
|
|
@ -79,7 +79,7 @@ class Operations:
|
|||
logger.error(e)
|
||||
continue
|
||||
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:
|
||||
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")
|
||||
|
||||
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}
|
||||
blur_num = 0
|
||||
|
|
|
@ -529,6 +529,10 @@ class Plex(Library):
|
|||
def collection_order_query(self, collection, 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)
|
||||
def reload(self, item, force=False):
|
||||
is_full = False
|
||||
|
@ -640,9 +644,10 @@ class Plex(Library):
|
|||
def scan_user(server, username):
|
||||
try:
|
||||
for playlist in server.playlists():
|
||||
if playlist.title not in playlists:
|
||||
playlists[playlist.title] = []
|
||||
playlists[playlist.title].append(username)
|
||||
if isinstance(playlist, Playlist):
|
||||
if playlist.title not in playlists:
|
||||
playlists[playlist.title] = []
|
||||
playlists[playlist.title].append(username)
|
||||
except requests.exceptions.ConnectionError:
|
||||
pass
|
||||
scan_user(self.PlexServer, self.account.title)
|
||||
|
@ -1244,7 +1249,7 @@ class Plex(Library):
|
|||
if filter_attr == "has_collection":
|
||||
filter_check = len(item.collections) > 0
|
||||
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":
|
||||
filter_check = True
|
||||
break
|
||||
|
|
|
@ -945,7 +945,7 @@ class 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):
|
||||
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):
|
||||
if attr in self.data and self.data[attr]:
|
||||
|
@ -966,7 +966,7 @@ class Overlay:
|
|||
elif back_width >= 0 and back_height >= 0:
|
||||
self.back_box = (back_width, back_height)
|
||||
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")
|
||||
|
||||
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
|
||||
|
||||
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("-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("-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("-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)
|
||||
|
@ -115,8 +116,10 @@ from modules.config import ConfigFile
|
|||
from modules.util import Failed, NotScheduled, Deleted
|
||||
|
||||
def my_except_hook(exctype, value, tb):
|
||||
for _line in traceback.format_exception(etype=exctype, value=value, tb=tb):
|
||||
logger.critical(_line)
|
||||
if issubclass(exctype, KeyboardInterrupt):
|
||||
sys.__excepthook__(exctype, value, tb)
|
||||
else:
|
||||
logger.critical("Uncaught Exception", exc_info=(exctype, value, tb))
|
||||
|
||||
sys.excepthook = my_except_hook
|
||||
|
||||
|
@ -144,6 +147,10 @@ if not 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):
|
||||
logger.add_main_handler()
|
||||
logger.separator()
|
||||
|
@ -845,54 +852,55 @@ def run_playlists(config):
|
|||
logger.remove_playlist_handler(playlist_log_name)
|
||||
return status, stats
|
||||
|
||||
try:
|
||||
if run or test or collections or libraries or metadata_files or resume:
|
||||
start({
|
||||
"config_file": config_file,
|
||||
"test": test,
|
||||
"delete": delete,
|
||||
"ignore_schedules": ignore_schedules,
|
||||
"collections": collections,
|
||||
"libraries": libraries,
|
||||
"metadata_files": metadata_files,
|
||||
"library_first": library_first,
|
||||
"resume": resume,
|
||||
"trace": trace
|
||||
})
|
||||
else:
|
||||
times_to_run = util.get_list(times)
|
||||
valid_times = []
|
||||
for time_to_run in times_to_run:
|
||||
try:
|
||||
valid_times.append(datetime.strftime(datetime.strptime(time_to_run, "%H:%M"), "%H:%M"))
|
||||
except ValueError:
|
||||
if time_to_run:
|
||||
raise Failed(f"Argument Error: time argument invalid: {time_to_run} must be in the HH:MM format between 00:00-23:59")
|
||||
else:
|
||||
raise Failed(f"Argument Error: blank time argument")
|
||||
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})
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
if not no_countdown:
|
||||
current_time = datetime.now().strftime("%H:%M")
|
||||
seconds = None
|
||||
og_time_str = ""
|
||||
for time_to_run in valid_times:
|
||||
new_seconds = (datetime.strptime(time_to_run, "%H:%M") - datetime.strptime(current_time, "%H:%M")).total_seconds()
|
||||
if new_seconds < 0:
|
||||
new_seconds += 86400
|
||||
if (seconds is None or new_seconds < seconds) and new_seconds > 0:
|
||||
seconds = new_seconds
|
||||
og_time_str = time_to_run
|
||||
if seconds is not None:
|
||||
hours = int(seconds // 3600)
|
||||
minutes = int((seconds % 3600) // 60)
|
||||
time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else ""
|
||||
time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}"
|
||||
logger.ghost(f"Current Time: {current_time} | {time_str} until the next run at {og_time_str} | Runs: {', '.join(times_to_run)}")
|
||||
else:
|
||||
logger.error(f"Time Error: {valid_times}")
|
||||
time.sleep(60)
|
||||
except KeyboardInterrupt:
|
||||
logger.separator("Exiting Plex Meta Manager")
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
if run or test or collections or libraries or metadata_files or resume:
|
||||
process({
|
||||
"config_file": config_file,
|
||||
"test": test,
|
||||
"delete": delete,
|
||||
"ignore_schedules": ignore_schedules,
|
||||
"collections": collections,
|
||||
"libraries": libraries,
|
||||
"metadata_files": metadata_files,
|
||||
"library_first": library_first,
|
||||
"resume": resume,
|
||||
"trace": trace
|
||||
})
|
||||
else:
|
||||
times_to_run = util.get_list(times)
|
||||
valid_times = []
|
||||
for time_to_run in times_to_run:
|
||||
try:
|
||||
valid_times.append(datetime.strftime(datetime.strptime(time_to_run, "%H:%M"), "%H:%M"))
|
||||
except ValueError:
|
||||
if time_to_run:
|
||||
raise Failed(f"Argument Error: time argument invalid: {time_to_run} must be in the HH:MM format between 00:00-23:59")
|
||||
else:
|
||||
raise Failed(f"Argument Error: blank time argument")
|
||||
for time_to_run in valid_times:
|
||||
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:
|
||||
schedule.run_pending()
|
||||
if not no_countdown:
|
||||
current_time = datetime.now().strftime("%H:%M")
|
||||
seconds = None
|
||||
og_time_str = ""
|
||||
for time_to_run in valid_times:
|
||||
new_seconds = (datetime.strptime(time_to_run, "%H:%M") - datetime.strptime(current_time, "%H:%M")).total_seconds()
|
||||
if new_seconds < 0:
|
||||
new_seconds += 86400
|
||||
if (seconds is None or new_seconds < seconds) and new_seconds > 0:
|
||||
seconds = new_seconds
|
||||
og_time_str = time_to_run
|
||||
if seconds is not None:
|
||||
hours = int(seconds // 3600)
|
||||
minutes = int((seconds % 3600) // 60)
|
||||
time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else ""
|
||||
time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}"
|
||||
logger.ghost(f"Current Time: {current_time} | {time_str} until the next run at {og_time_str} | Runs: {', '.join(times_to_run)}")
|
||||
else:
|
||||
logger.error(f"Time Error: {valid_times}")
|
||||
time.sleep(60)
|
||||
except KeyboardInterrupt:
|
||||
logger.separator("Exiting Plex Meta Manager")
|
||||
|
|
Loading…
Reference in a new issue