mirror of
https://github.com/nwithan8/plex-prerolls
synced 2024-08-30 16:52:17 +00:00
358 lines
12 KiB
Python
358 lines
12 KiB
Python
from datetime import datetime, timedelta, date
|
|
from typing import Tuple, Union
|
|
|
|
from pytz import timezone
|
|
|
|
import modules.logs as logging
|
|
|
|
|
|
def make_plural(word, count: int, suffix_override: str = 's') -> str:
|
|
if count > 1:
|
|
return f"{word}{suffix_override}"
|
|
return word
|
|
|
|
|
|
def quote(string: str) -> str:
|
|
return f"\"{string}\""
|
|
|
|
|
|
def status_code_is_success(status_code: int) -> bool:
|
|
return 200 <= status_code < 300
|
|
|
|
|
|
def milliseconds_to_minutes_seconds(milliseconds: int) -> str:
|
|
seconds = int(milliseconds / 1000)
|
|
minutes = int(seconds / 60)
|
|
if minutes < 10:
|
|
minutes = f"0{minutes}"
|
|
seconds = int(seconds % 60)
|
|
if seconds < 10:
|
|
seconds = f"0{seconds}"
|
|
return f"{minutes}:{seconds}"
|
|
|
|
|
|
def now(timezone_code: str = None) -> datetime:
|
|
if timezone_code:
|
|
return datetime.now(timezone(timezone_code)) # will raise exception if invalid timezone_code
|
|
return datetime.now()
|
|
|
|
|
|
def now_plus_milliseconds(milliseconds: int, timezone_code: str = None) -> datetime:
|
|
if timezone_code:
|
|
_now = datetime.now(timezone(timezone_code)) # will raise exception if invalid timezone_code
|
|
else:
|
|
_now = datetime.now()
|
|
return _now + timedelta(milliseconds=milliseconds)
|
|
|
|
|
|
def now_in_range(start: datetime, end: datetime) -> bool:
|
|
_now = now()
|
|
return start <= _now <= end
|
|
|
|
|
|
def start_of_time() -> datetime:
|
|
return datetime(1970, 1, 1)
|
|
|
|
|
|
def end_of_time() -> datetime:
|
|
return datetime(9999, 12, 31)
|
|
|
|
|
|
def start_of_year(year: int = None) -> datetime:
|
|
_now = now()
|
|
|
|
if not year:
|
|
year = _now.year
|
|
|
|
return datetime(year, 1, 1)
|
|
|
|
|
|
def end_of_year(year: int = None) -> datetime:
|
|
_now = now()
|
|
|
|
if not year:
|
|
year = _now.year
|
|
|
|
return datetime(year, 12, 31)
|
|
|
|
|
|
def start_of_month(month_number: int = None) -> datetime:
|
|
_now = now()
|
|
|
|
if not month_number:
|
|
month_number = _now.month
|
|
|
|
return datetime(_now.year, month_number, 1)
|
|
|
|
|
|
def end_of_month(month_number: int = None) -> datetime:
|
|
_now = now()
|
|
|
|
if not month_number:
|
|
month_number = _now.month
|
|
|
|
if month_number == 12:
|
|
return end_of_year(year=_now.year) # If month is December, return end of year (shortcut)
|
|
else:
|
|
return start_of_month(month_number=month_number + 1) - timedelta(
|
|
days=1) # Subtract one day from start of next month
|
|
|
|
|
|
def start_of_week_number(week_number: int = None) -> datetime:
|
|
_now = now()
|
|
|
|
if not week_number:
|
|
week_number = _now.strftime('%U')
|
|
|
|
return datetime.strptime(f"{_now.year}-W{int(week_number)}-0", "%Y-W%W-%w")
|
|
|
|
|
|
def end_of_week_number(week_number: int = None) -> datetime:
|
|
_now = now()
|
|
|
|
if not week_number:
|
|
week_number = _now.strftime('%U')
|
|
|
|
return datetime.strptime(f"{_now.year}-W{int(week_number)}-6", "%Y-W%W-%w")
|
|
|
|
|
|
def make_midnight(date: datetime) -> datetime:
|
|
return datetime(date.year, date.month, date.day)
|
|
|
|
|
|
def make_right_before_midnight(date: datetime) -> datetime:
|
|
return datetime(date.year, date.month, date.day, 23, 59, 59)
|
|
|
|
|
|
def limit_text_length(text: str, limit: int, suffix: str = "...") -> str:
|
|
if len(text) <= limit:
|
|
return text
|
|
|
|
suffix_length = len(suffix)
|
|
return f"{text[:limit - suffix_length]}{suffix}"
|
|
|
|
|
|
def string_to_datetime(date_string: str, template: str = "%Y-%m-%dT%H:%M:%S") -> datetime:
|
|
"""
|
|
Convert a datetime string to a datetime.datetime object
|
|
|
|
:param date_string: datetime string to convert
|
|
:type date_string: str
|
|
:param template: (Optional) datetime template to use when parsing string
|
|
:type template: str, optional
|
|
:return: datetime.datetime object
|
|
:rtype: datetime.datetime
|
|
"""
|
|
if date_string.endswith('Z'):
|
|
date_string = date_string[:-5]
|
|
return datetime.strptime(date_string, template)
|
|
|
|
|
|
def datetime_to_string(datetime_object: datetime, template: str = "%Y-%m-%dT%H:%M:%S.000Z") -> str:
|
|
"""
|
|
Convert a datetime.datetime object to a string
|
|
|
|
:param datetime_object: datetime.datetime object to convert
|
|
:type datetime_object: datetime.datetime
|
|
:param template: (Optional) datetime template to use when parsing string
|
|
:type template: str, optional
|
|
:return: str representation of datetime
|
|
:rtype: str
|
|
"""
|
|
return datetime_object.strftime(template)
|
|
|
|
|
|
def wildcard_strings_to_datetimes(start_date_string: str, end_date_string: str) -> \
|
|
Tuple[Union[datetime, None], Union[datetime, None]]:
|
|
"""
|
|
Convert date or datetime strings with wildcards to datetime.datetime objects
|
|
|
|
:param start_date_string: start datetime string to convert
|
|
:type start_date_string: str
|
|
:param end_date_string: end datetime string to convert
|
|
:type end_date_string: str
|
|
:return: datetime.datetime object
|
|
:rtype: datetime.datetime
|
|
"""
|
|
if isinstance(start_date_string, date):
|
|
start_date_string = start_date_string.strftime("%Y-%m-%d")
|
|
if isinstance(end_date_string, date):
|
|
end_date_string = end_date_string.strftime("%Y-%m-%d")
|
|
|
|
start_date_and_time = start_date_string.split(' ')
|
|
end_date_and_time = end_date_string.split(' ')
|
|
template = "%Y-%m-%d %H:%M:%S"
|
|
_now = now()
|
|
|
|
# Sample: xxxx-xx-xx
|
|
# Sample: xxxx-xx-xx xx:xx:xx
|
|
|
|
_start_date = start_date_and_time[0] # xxxx-xx-xx
|
|
_end_date = end_date_and_time[0] # xxxx-xx-xx
|
|
|
|
need_specific_datetime = False
|
|
|
|
_start_time = start_date_and_time[1] if len(start_date_and_time) > 1 else "00:00:00"
|
|
_end_time = end_date_and_time[1] if len(end_date_and_time) > 1 else "23:59:59"
|
|
|
|
# Sample: xxxx-xx-xx xx:xx:xx
|
|
|
|
start_time_parts = _start_time.split(':')
|
|
end_time_parts = _end_time.split(':')
|
|
|
|
start_second = start_time_parts[2]
|
|
end_second = end_time_parts[2]
|
|
|
|
# Can't have a wildcard in one and not the other
|
|
if (start_second != 'xx' and end_second == 'xx') or (start_second == 'xx' and end_second != 'xx'):
|
|
logging.error(message=f"Incompatible second comparison: {start_date_string} - {end_date_string}")
|
|
return None, None
|
|
|
|
# At this point, either they both have wildcards or neither do, we can assume based on start_second
|
|
if start_second == 'xx':
|
|
start_second = '00'
|
|
end_second = '59' # Keep wide to ensure script running time doesn't interfere
|
|
else:
|
|
need_specific_datetime = True
|
|
|
|
# Finalize the seconds
|
|
start_second = int(start_second)
|
|
end_second = int(end_second)
|
|
|
|
start_minute = start_time_parts[1]
|
|
end_minute = end_time_parts[1]
|
|
|
|
# Can't have a wildcard in one and not the other
|
|
if (start_minute != 'xx' and end_minute == 'xx') or (start_minute == 'xx' and end_minute != 'xx'):
|
|
logging.error(message=f"Incompatible minute comparison: {start_date_string} - {end_date_string}")
|
|
return None, None
|
|
|
|
# At this point, either they both have wildcards or neither do, we can assume based on start_minute
|
|
if start_minute == 'xx':
|
|
if need_specific_datetime:
|
|
start_minute = _now.minute
|
|
end_minute = _now.minute + 3 # Give buffer for script running time
|
|
else:
|
|
start_minute = '00'
|
|
end_minute = '59' # Keep wide to ensure script running time doesn't interfere
|
|
else:
|
|
need_specific_datetime = True
|
|
|
|
# Finalize the minutes
|
|
start_minute = int(start_minute)
|
|
end_minute = int(end_minute)
|
|
|
|
start_hour = start_time_parts[0]
|
|
end_hour = end_time_parts[0]
|
|
|
|
# Can't have a wildcard in one and not the other
|
|
if (start_hour != 'xx' and end_hour == 'xx') or (start_hour == 'xx' and end_hour != 'xx'):
|
|
logging.error(message=f"Incompatible hour comparison: {start_date_string} - {end_date_string}")
|
|
return None, None
|
|
|
|
# At this point, either they both have wildcards or neither do, we can assume based on start_hour
|
|
if start_hour == 'xx':
|
|
if need_specific_datetime:
|
|
start_hour = _now.hour
|
|
end_hour = _now.hour
|
|
else:
|
|
start_hour = '00'
|
|
end_hour = '23' # Keep wide to ensure script running time doesn't interfere
|
|
|
|
# Finalize the hours
|
|
start_hour = int(start_hour)
|
|
end_hour = int(end_hour)
|
|
|
|
_start_time = f"{start_hour}:{start_minute}:{start_second}"
|
|
_end_time = f"{end_hour}:{end_minute}:{end_second}"
|
|
|
|
start_date_parts = _start_date.split('-')
|
|
end_date_parts = _end_date.split('-')
|
|
|
|
start_day = start_date_parts[2]
|
|
end_day = end_date_parts[2]
|
|
|
|
# Can't have a wildcard in one and not the other
|
|
if (start_day != 'xx' and end_day == 'xx') or (start_day == 'xx' and end_day != 'xx'):
|
|
logging.error(message=f"Incompatible day comparison: {start_date_string} - {end_date_string}")
|
|
return None, None
|
|
|
|
# At this point, either they both have wildcards or neither do, we can assume based on start_day
|
|
if start_day == 'xx':
|
|
start_day = _now.day
|
|
end_day = _now.day
|
|
else:
|
|
need_specific_datetime = True
|
|
|
|
# Finalize the days
|
|
start_day = int(start_day)
|
|
end_day = int(end_day)
|
|
|
|
start_month = start_date_parts[1]
|
|
end_month = end_date_parts[1]
|
|
|
|
# Can't have a wildcard in one and not the other
|
|
if (start_month != 'xx' and end_month == 'xx') or (start_month == 'xx' and end_month != 'xx'):
|
|
logging.error(message=f"Incompatible month comparison: {start_date_string} - {end_date_string}")
|
|
return None, None
|
|
|
|
# At this point, either they both have wildcards or neither do, we can assume based on start_month
|
|
if start_month == 'xx':
|
|
if need_specific_datetime:
|
|
start_month = _now.month
|
|
end_month = _now.month
|
|
|
|
# Account for crossing a month boundary
|
|
if start_day > end_day: # e.g. Start on the 31st and end on the 1st
|
|
if _now.day < start_day: # Current date is before start day (in the next month)
|
|
start_month -= 1 # TODO: This could break if the start_month is January (1) -> 0
|
|
else:
|
|
end_month += 1 # TODO: This could break if the end_month is December (12) -> 13
|
|
else:
|
|
start_month = start_of_year().month
|
|
end_month = end_of_year().month
|
|
else:
|
|
need_specific_datetime = True
|
|
|
|
# Finalize the months
|
|
start_month = int(start_month)
|
|
end_month = int(end_month)
|
|
|
|
start_year = start_date_parts[0]
|
|
end_year = end_date_parts[0]
|
|
|
|
# Can't have a wildcard in one and not the other
|
|
if (start_year != 'xxxx' and end_year == 'xxxx') or (start_year == 'xxxx' and end_year != 'xxxx'):
|
|
logging.error(message=f"Incompatible year comparison: {start_date_string} - {end_date_string}")
|
|
return None, None
|
|
|
|
# At this point, either they both have wildcards or neither do, we can assume based on start_year
|
|
if start_year == 'xxxx':
|
|
if need_specific_datetime:
|
|
start_year = _now.year
|
|
end_year = _now.year
|
|
|
|
# Account for crossing a year boundary
|
|
# At this point, the start_month and end_month are numerical strings or ints
|
|
if start_month > end_month: # e.g. Start in December (12) and end in January (1)
|
|
if _now.month < start_month: # Current date is before start month (in the next year)
|
|
start_year -= 1
|
|
else:
|
|
end_year += 1
|
|
else:
|
|
start_year = start_of_time().year
|
|
end_year = end_of_time().year
|
|
|
|
# Finalize the years
|
|
start_year = int(start_year)
|
|
end_year = int(end_year)
|
|
|
|
_start_date = f"{start_year}-{start_month}-{start_day}"
|
|
_end_date = f"{end_year}-{end_month}-{end_day}"
|
|
|
|
_start_datetime = f"{_start_date} {_start_time}"
|
|
_end_datetime = f"{_end_date} {_end_time}"
|
|
|
|
return string_to_datetime(date_string=_start_datetime, template=template), \
|
|
string_to_datetime(date_string=_end_datetime, template=template)
|