added datetime suport to schedule ranges. message changes

This commit is contained in:
Brian Lindner 2021-01-03 02:34:20 -05:00
parent 37334d77af
commit f1bfdab87c
No known key found for this signature in database
GPG Key ID: 8A53187BAA2C7197
3 changed files with 122 additions and 59 deletions

View File

@ -58,7 +58,11 @@ git clone https://github.com/BrianLindner/plex-schedule-prerolls.git
### Install Requirements <a id="requirements"></a> ### Install Requirements <a id="requirements"></a>
See `requirements.txt` for Python modules [link](requirements.txt) Requires:
- Python 3.8+ [may work on 3.5+ but not tested]
See `requirements.txt` for Python modules used [link](requirements.txt)
Install Python requirements Install Python requirements
@ -102,8 +106,8 @@ monthly:
date_range: date_range:
enabled: (yes/no) enabled: (yes/no)
ranges: ranges:
- start_date: 2020-01-01 - start_date: 2020-01-01 12:01:00
end_date: 2020-01-01 end_date: 2020-01-01 16:59:59
path: /path/to/video.mp4 path: /path/to/video.mp4
- start_date: 2020-07-03 - start_date: 2020-07-03
end_date: 2020-07-05 end_date: 2020-07-05
@ -124,6 +128,15 @@ default:
path: /path/to/file.mp4;/path/to/file.mp4 path: /path/to/file.mp4;/path/to/file.mp4
``` ```
#### Date Range Section:
Use it for *Day* or *Ranges of Time* needs; \
Now with Time support!
Formatting Supported:
- Dates: yyyy-mm-dd
- DateTime: yyyy-mm-dd hh:mm:ss (24hr time format)
### (Optional) Config `logger.conf` to your needs ### (Optional) Config `logger.conf` to your needs
See: [https://docs.python.org/3/howto/logging.html](https://docs.python.org/3/howto/logging.html) See: [https://docs.python.org/3/howto/logging.html](https://docs.python.org/3/howto/logging.html)
@ -213,4 +226,4 @@ useful if running other scripts/commands, using venv encapsulation, customizing
## Shout out to places to get Pre-Roll ## Shout out to places to get Pre-Roll
[https://prerolls.video](https://prerolls.video) [https://prerolls.video](https://prerolls.video)

View File

@ -10,6 +10,7 @@ Raises:
import os import os
import sys import sys
import logging import logging
import logging.config
from configparser import ConfigParser from configparser import ConfigParser
from plexapi.server import PlexServer, CONFIG from plexapi.server import PlexServer, CONFIG
@ -30,11 +31,12 @@ def getPlexConfig(config_file=None):
Raises: Raises:
KeyError: Config Params not found in config file(s) KeyError: Config Params not found in config file(s)
FileNotFoundError: If cannot find a config file FileNotFoundError: Cannot find a config file
Returns: Returns:
dict: Dict of config params {PLEX_URL, PLEX_TOKEN} dict: Dict of config params {PLEX_URL, PLEX_TOKEN}
""" """
cfg = {} cfg = {}
plex_url = '' plex_url = ''
plex_token = '' plex_token = ''
@ -48,7 +50,7 @@ def getPlexConfig(config_file=None):
if os.path.exists(config_file): if os.path.exists(config_file):
filename = config_file filename = config_file
else: else:
raise FileNotFoundError('Config file -c "{}" not found'.format(config_file)) raise FileNotFoundError('Config file "{}" not found'.format(config_file))
else: else:
filename = 'config.ini' filename = 'config.ini'
@ -66,7 +68,8 @@ def getPlexConfig(config_file=None):
if len(plex_url) > 1 and len(plex_token) > 1: if len(plex_url) > 1 and len(plex_token) > 1:
use_local_config = True use_local_config = True
except KeyError as e: except KeyError as e:
logger.error('Key Value not found {}', exc_info=e) msg = 'Key Value not found {}'
logger.error(msg, exc_info=e)
raise e raise e
else: else:
msg = '[auth] section not found in LOCAL config.ini file' msg = '[auth] section not found in LOCAL config.ini file'
@ -86,7 +89,8 @@ def getPlexConfig(config_file=None):
if len(plex_url) > 1 and len(plex_token) > 1: if len(plex_url) > 1 and len(plex_token) > 1:
use_plexapi_config = True use_plexapi_config = True
except KeyError as e: except KeyError as e:
logger.error('Key Value not found', exc_info=e) msg = 'Key Value not found'
logger.error(msg, exc_info=e)
raise e raise e
else: else:
msg = "[auth] section not found in PlexAPI MAIN config.ini file" msg = "[auth] section not found in PlexAPI MAIN config.ini file"
@ -114,6 +118,7 @@ def setupLogger(log_config):
KeyError: Problems processing logging config files KeyError: Problems processing logging config files
FileNotFoundError: Problems with log file location, other FileNotFoundError: Problems with log file location, other
""" """
if os.path.exists(log_config): if os.path.exists(log_config):
try: try:
logging.config.fileConfig(log_config, disable_existing_loggers=False) logging.config.fileConfig(log_config, disable_existing_loggers=False)
@ -126,10 +131,12 @@ def setupLogger(log_config):
if not os.path.exists(logdir): if not os.path.exists(logdir):
try: try:
logger.debug('Creating log folder "{}"'.format(logdir)) msg = 'Creating log folder "{}"'.format(logdir)
logger.debug(msg)
os.makedirs(logdir, exist_ok=True) os.makedirs(logdir, exist_ok=True)
except Exception as e: except Exception as e:
logger.error('Error creating log folder "{}"'.format(logdir)) msg = 'Error creating log folder "{}"'.format(logdir)
logger.error(msg, exc_info=e)
raise e raise e
elif logger.handlers: elif logger.handlers:
# if logger config loaded, but some file error happened # if logger config loaded, but some file error happened
@ -140,10 +147,13 @@ def setupLogger(log_config):
if not os.path.exists(logdir): if not os.path.exists(logdir):
try: try:
logger.debug('Creating log folder "{}"'.format(logdir)) msg = 'Creating log folder "{}"'.format(logdir)
logger.debug(msg)
os.makedirs(logdir, exist_ok=True) os.makedirs(logdir, exist_ok=True)
except Exception as e: except Exception as e:
logger.error('Error creating log folder "{}"'.format(logdir)) msg = 'Error creating log folder "{}"'.format(logdir)
logger.error(msg, exc_info=e)
raise e raise e
else: else:
# not sure the issue, raise the exception # not sure the issue, raise the exception
@ -153,7 +163,8 @@ def setupLogger(log_config):
logging.config.fileConfig(log_config, disable_existing_loggers=False) logging.config.fileConfig(log_config, disable_existing_loggers=False)
else: else:
logger.debug('Logging Config file "{}" not available, will be using defaults'.format(log_config)) msg = 'Logging Config file "{}" not available, will be using defaults'.format(log_config)
logger.debug(msg)
if __name__ == '__main__': if __name__ == '__main__':
msg = 'Script not meant to be run directly, please import into other scripts.\n\n' + \ msg = 'Script not meant to be run directly, please import into other scripts.\n\n' + \

View File

@ -29,19 +29,20 @@ Raises:
ConfigError: [description] ConfigError: [description]
FileNotFoundError: [description] FileNotFoundError: [description]
""" """
import os import os
import sys import sys
import logging import logging
import logging.config
import requests import requests
import datetime import datetime
from datetime import datetime, date, time, timedelta
import yaml import yaml
from argparse import ArgumentParser from argparse import ArgumentParser
from configparser import ConfigParser from configparser import ConfigParser
from configparser import Error as ConfigError from configparser import Error as ConfigError
from plexapi.server import PlexServer, CONFIG from plexapi.server import PlexServer, CONFIG
# import local modules # import local util modules
import plexutil import plexutil
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -57,16 +58,24 @@ def getArguments():
argparse.Namespace: Namespace object argparse.Namespace: Namespace object
""" """
description = 'Automate scheduling of pre-roll intros for Plex' description = 'Automate scheduling of pre-roll intros for Plex'
version = '0.7.2' version = '0.8.0'
config_default = './config.ini' config_default = './config.ini'
log_config_default = './logging.conf' log_config_default = './logging.conf'
schedule_default = './preroll_schedules.yaml' schedule_default = './preroll_schedules.yaml'
parser = ArgumentParser(description='{}'.format(description)) parser = ArgumentParser(description='{}'.format(description))
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(version), help='show the version number and exit') parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(version),
parser.add_argument('-l', '--logconfig-path', dest='log_config_file', default=log_config_default, action='store', help='Path to logging config file. [Default: {}]'.format(log_config_default)) help='show the version number and exit')
parser.add_argument('-c', '--config-path', dest='config_file', action='store', help='Path to Config.ini to use for Plex Server info. [Default: {}]'.format(config_default)) parser.add_argument('-l', '--logconfig-path',
parser.add_argument('-s', '--schedule-path', dest='schedule_file', action='store', help='Path to pre-roll schedule file (YAML) to be use. [Default: {}]'.format(schedule_default)) dest='log_config_file', action='store',
default=log_config_default,
help='Path to logging config file. [Default: {}]'.format(log_config_default))
parser.add_argument('-c', '--config-path',
dest='config_file', action='store',
help='Path to Config.ini to use for Plex Server info. [Default: {}]'.format(config_default))
parser.add_argument('-s', '--schedule-path',
dest='schedule_file', action='store',
help='Path to pre-roll schedule file (YAML) to be use. [Default: {}]'.format(schedule_default))
args = parser.parse_args() args = parser.parse_args()
return args return args
@ -77,7 +86,8 @@ def getYAMLSchema():
Returns: Returns:
dict: Dict of main schema items dict: Dict of main schema items
""" """
schema = {'default': None, 'monthly': None, 'weekly': None, 'date_range': None, 'misc': None} schema = {'default': None, 'monthly': None,
'weekly': None, 'date_range': None, 'misc': None}
return schema return schema
def getWeekRange(year, weeknum): def getWeekRange(year, weeknum):
@ -91,8 +101,9 @@ def getWeekRange(year, weeknum):
Date: Start date of the Year/Month Date: Start date of the Year/Month
Date: End date of the Year/Month Date: End date of the Year/Month
""" """
start = datetime.datetime.strptime('{}-W{}-0'.format(year, int(weeknum)-1), "%Y-W%W-%w").date() start = datetime.strptime('{}-W{}-0'.format(year, int(weeknum)-1),
end = start + datetime.timedelta(days=6) "%Y-W%W-%w").date()
end = start + timedelta(days=6)
return start, end return start, end
@ -107,9 +118,9 @@ def getMonthRange(year, monthnum):
Date: Start date of the Year/Month Date: Start date of the Year/Month
Date: End date of the Year/Month Date: End date of the Year/Month
""" """
start = datetime.date(year, monthnum, 1) start = date(year, monthnum, 1)
next_month = start.replace(day=28) + datetime.timedelta(days=4) next_month = start.replace(day=28) + timedelta(days=4)
end = next_month - datetime.timedelta(days=next_month.day) end = next_month - timedelta(days=next_month.day)
return start, end return start, end
@ -132,16 +143,17 @@ def getPrerollSchedule(schedule_file=None):
if os.path.exists(schedule_file): if os.path.exists(schedule_file):
filename = schedule_file filename = schedule_file
else: else:
raise FileNotFoundError('Preroll Schedule file -s "{}" not found'.format(schedule_file)) msg = 'Pre-roll Schedule file "{}" not found'.format(schedule_file)
raise FileNotFoundError(msg)
else: else:
for f in default_files: for f in default_files:
if os.path.exists(f): if os.path.exists(f):
filename = f filename = f
break break
# if we still cant find a schedule file, we hae to abort # if we still cant find a schedule file, we abort
if not filename: if not filename:
msg = 'No {} Found'.format(' / '.join(default_files)) msg = 'Missing schedule file: "{}"'.format('" / "'.join(default_files))
logger.critical(msg) logger.critical(msg)
raise FileNotFoundError(msg) raise FileNotFoundError(msg)
@ -149,7 +161,7 @@ def getPrerollSchedule(schedule_file=None):
#contents = yaml.load(file, Loader=yaml.SafeLoader) #contents = yaml.load(file, Loader=yaml.SafeLoader)
contents = yaml.load(file, Loader=yaml.FullLoader) contents = yaml.load(file, Loader=yaml.FullLoader)
today = datetime.date.today() today = date.today()
schedule = [] schedule = []
for schedule_type in getYAMLSchema(): for schedule_type in getYAMLSchema():
if schedule_type == 'weekly': if schedule_type == 'weekly':
@ -172,10 +184,12 @@ def getPrerollSchedule(schedule_file=None):
schedule.append(entry) schedule.append(entry)
except KeyError as e: except KeyError as e:
# skip KeyError for missing Weeks # skip KeyError for missing Weeks
logger.debug('Key Value not found: "{}"->"{}", skipping week'.format(schedule_type, i)) msg = 'Key Value not found: "{}"->"{}", skipping week'.format(schedule_type, i)
logger.debug(msg)
continue continue
except KeyError as e: except KeyError as e:
logger.error('Key Value not found in "{}" section'.format(schedule_type), exc_info=e) msg = 'Key Value not found in "{}" section'.format(schedule_type)
logger.error(msg, exc_info=e)
raise e raise e
elif schedule_type == 'monthly': elif schedule_type == 'monthly':
try: try:
@ -183,7 +197,7 @@ def getPrerollSchedule(schedule_file=None):
if use: if use:
for i in range(1,13): for i in range(1,13):
month_abrev = datetime.date(today.year, i, 1).strftime('%b').lower() month_abrev = date(today.year, i, 1).strftime('%b').lower()
try: try:
path = contents[schedule_type][month_abrev] path = contents[schedule_type][month_abrev]
@ -198,10 +212,12 @@ def getPrerollSchedule(schedule_file=None):
schedule.append(entry) schedule.append(entry)
except KeyError as e: except KeyError as e:
# skip KeyError for missing Months # skip KeyError for missing Months
logger.warning('Key Value not found: "{}"->"{}", skipping month'.format(schedule_type, month_abrev)) msg = 'Key Value not found: "{}"->"{}", skipping month'.format(schedule_type, month_abrev)
logger.warning(msg)
continue continue
except KeyError as e: except KeyError as e:
logger.error('Key Value not found in "{}" section'.format(schedule_type), exc_info=e) msg = 'Key Value not found in "{}" section'.format(schedule_type)
logger.error(msg, exc_info=e)
raise e raise e
elif schedule_type == 'date_range': elif schedule_type == 'date_range':
try: try:
@ -223,7 +239,8 @@ def getPrerollSchedule(schedule_file=None):
#logger.error('Key Value not found: "{}"'.format(schedule_type), exc_info=e) #logger.error('Key Value not found: "{}"'.format(schedule_type), exc_info=e)
raise e raise e
except KeyError as e: except KeyError as e:
logger.error('Key Value not found in "{}" section'.format(schedule_type), exc_info=e) msg = 'Key Value not found in "{}" section'.format(schedule_type)
logger.error(msg, exc_info=e)
raise e raise e
elif schedule_type == 'misc': elif schedule_type == 'misc':
try: try:
@ -235,8 +252,8 @@ def getPrerollSchedule(schedule_file=None):
if path: if path:
entry = {} entry = {}
entry['Type'] = schedule_type entry['Type'] = schedule_type
entry['StartDate'] = datetime.date(today.year, today.month, today.day) entry['StartDate'] = date(today.year, today.month, today.day)
entry['EndDate'] = datetime.date(today.year, today.month, today.day) entry['EndDate'] = date(today.year, today.month, today.day)
entry['Path'] = path entry['Path'] = path
schedule.append(entry) schedule.append(entry)
@ -244,7 +261,8 @@ def getPrerollSchedule(schedule_file=None):
#logger.error('Key Value not found: "{}"'.format(schedule_type), exc_info=e) #logger.error('Key Value not found: "{}"'.format(schedule_type), exc_info=e)
raise e raise e
except KeyError as e: except KeyError as e:
logger.error('Key Value not found in "{}" section'.format(schedule_type), exc_info=e) msg = 'Key Value not found in "{}" section'.format(schedule_type)
logger.error(msg, exc_info=e)
raise e raise e
elif schedule_type == 'default': elif schedule_type == 'default':
try: try:
@ -256,8 +274,9 @@ def getPrerollSchedule(schedule_file=None):
if path: if path:
entry = {} entry = {}
entry['Type'] = schedule_type entry['Type'] = schedule_type
entry['StartDate'] = datetime.date(today.year, today.month, today.day) entry['StartDate'] = date(today.year, today.month, today.day)
entry['EndDate'] = datetime.date(today.year, today.month, today.day) entry['EndDate'] = date(today.year, today.month, today.day)
entry['Path'] = path entry['Path'] = path
schedule.append(entry) schedule.append(entry)
@ -265,7 +284,8 @@ def getPrerollSchedule(schedule_file=None):
#logger.error('Key Value not found: "{}"'.format(schedule_type), exc_info=e) #logger.error('Key Value not found: "{}"'.format(schedule_type), exc_info=e)
raise e raise e
except KeyError as e: except KeyError as e:
logger.error('Key Value not found in "{}" section'.format(schedule_type), exc_info=e) msg = 'Key Value not found in "{}" section'.format(schedule_type)
logger.error(msg, exc_info=e)
raise e raise e
else: else:
@ -296,42 +316,58 @@ def buildListingString(items, play_all=False):
return listing return listing
def getPrerollListingString(schedule, for_date=None): def getPrerollListingString(schedule, for_datetime=None):
"""Return listing of preroll videos to be used by Plex """Return listing of preroll videos to be used by Plex
Args: Args:
schedule (list): List of schedule entries (See: getPrerollSchedule) schedule (list): List of schedule entries (See: getPrerollSchedule)
for_Date (date, optional): Date to process pre-roll string for [Default: Today] for_datetime (datetime, optional): Date to process pre-roll string for [Default: Today]
Useful if wanting to test what different schedules produce Useful if wanting to test what different schedules produce
Returns: Returns:
string: listing of preroll video paths to be used for Extras. CSV style: (;|,) string: listing of preroll video paths to be used for Extras. CSV style: (;|,)
""" """
listing = '' listing = ''
entries = dict(getYAMLSchema()) entries = {}
# prep the storage lists # prep the storage lists
for e in getYAMLSchema(): for y in getYAMLSchema():
entries[e] = [] entries[y] = []
# determine which date to build the listing for # determine which date to build the listing for
if for_date: if for_datetime:
check_date = for_date if isinstance(for_datetime, datetime):
check_datetime = for_datetime
else:
check_datetime = datetime.combine(for_datetime, datetime.now().time())
else: else:
check_date = datetime.date.today() check_datetime = datetime.now()
# process the schedule for the given date # process the schedule for the given date
for entry in schedule: for entry in schedule:
try: try:
if entry['StartDate'] <= check_date <= entry['EndDate']: entry_start = entry['StartDate']
entry_end = entry['EndDate']
if not isinstance(entry_start, datetime):
entry_start = datetime.combine(entry_start, datetime.min.time())
if not isinstance(entry_end, datetime):
entry_end = datetime.combine(entry_end, datetime.max.time())
msg = 'checking "{}" against: "{}" - "{}"'.format(check_datetime, entry_start, entry_end)
logger.debug(msg)
if entry_start <= check_datetime <= entry_end:
entry_type = entry['Type'] entry_type = entry['Type']
path = entry['Path'] path = entry['Path']
msg = 'pass: Using "{}" - "{}"'.format(entry_start, entry_end)
logger.debug(msg)
if path: if path:
entries[entry_type].append(path) entries[entry_type].append(path)
except KeyError as ke: except KeyError as ke:
logger.warning('KeyError with entry "{}"'.format(entry), exc_info=ke) msg = 'KeyError with entry "{}"'.format(entry)
logger.warning(msg, exc_info=ke)
continue continue
# Build the merged output based or order of Priority # Build the merged output based or order of Priority
@ -389,11 +425,14 @@ if __name__ == '__main__':
try: try:
plex = PlexServer(cfg['PLEX_URL'], cfg['PLEX_TOKEN'], session=sess) plex = PlexServer(cfg['PLEX_URL'], cfg['PLEX_TOKEN'], session=sess)
except Exception as e: except Exception as e:
logger.error('Error Connecting to Plex', exc_info=e) msg = 'Error connecting to Plex'
logger.error(msg, exc_info=e)
raise e raise e
schedule = getPrerollSchedule(args.schedule_file) schedule = getPrerollSchedule(args.schedule_file)
prerolls = getPrerollListingString(schedule) prerolls = getPrerollListingString(schedule)
logger.info('Saving Preroll List: "{}"'.format(prerolls)) savePrerollList(plex, prerolls)
savePrerollList(plex, prerolls)
msg = 'Saved pre-roll list to server: "{}"'.format(prerolls)
logger.info(msg)