diff --git a/dizqueTV/dizquetv.py b/dizqueTV/dizquetv.py index a0c5061..9ec92db 100644 --- a/dizqueTV/dizquetv.py +++ b/dizqueTV/dizquetv.py @@ -48,8 +48,10 @@ def make_time_slot_from_dizque_program( :rtype: TimeSlot """ if program.type == "redirect": + program: Redirect = program item = TimeSlotItem(item_type="redirect", item_value=program.channel) elif program.showTitle: + program: Program = program if program.type == "movie": item = TimeSlotItem(item_type="movie", item_value=program.showTitle) else: @@ -104,6 +106,7 @@ def convert_custom_show_to_programs( data["customShowId"] = custom_show.id data["customShowName"] = custom_show.name data["customOrder"] = program.order + data["customShowTag"] = None # need to remove the tag to avoid this being converted back to a custom show programs.append( Program(data=data, dizque_instance=dizque_instance, channel_instance=None) ) @@ -203,7 +206,9 @@ def repeat_and_shuffle_list(items: List, how_many_times: int) -> List: return final_list -def expand_custom_show_items(programs: List, dizque_instance) -> List: +def expand_custom_show_items( + programs: List[Union[Program, Redirect, FillerItem, CustomShow, Video, Movie, Episode, Track]], dizque_instance) \ + -> List[Union[Program, Redirect, FillerItem, Video, Movie, Episode, Track]]: """ Expand all custom shows in a list out to their individual programs @@ -216,13 +221,15 @@ def expand_custom_show_items(programs: List, dizque_instance) -> List: """ all_items = [] for item in programs: - if not helpers._object_has_attribute(obj=item, attribute_name="customShowTag"): - all_items.append(item) - else: - programs = convert_custom_show_to_programs( + if helpers._object_has_attribute(obj=item, attribute_name="customShowTag"): + # this is a custom show + programs: List[Program] = convert_custom_show_to_programs( custom_show=item, dizque_instance=dizque_instance ) all_items.extend(programs) + else: + # this is not a custom show + all_items.append(item) return all_items @@ -1126,10 +1133,22 @@ def add_custom_show( content: List[Union[Program, Video, Movie, Episode, Track]], plex_server: PServer = None, ) -> Union[CustomShow, None]: + """ + Add a dizqueTV custom show + + :param name: Name of custom show to add + :type name: str + :param content: List of content to add to custom show + :type content: List[Union[Program, Video, Movie, Episode, Track]] + :param plex_server: plexapi.server.PlexServer object (required if adding PlexAPI Video, Movie, Episode or Track objects) + :type plex_server: plexapi.server.PlexServer, optional + :return: New CustomShow object if successful, None if unsuccessful + :rtype: CustomShow or None + """ kwargs = {"name": name, "content": []} for item in content: if type(item) == Program: - custom_show_item = convert_program_to_custom_show_item( + custom_show_item: CustomShowItem = convert_program_to_custom_show_item( program=item, dizque_instance=self ) else: @@ -1138,10 +1157,10 @@ def add_custom_show( "You must include a plex_server if you are adding PlexAPI Videos, " "Movies, Episodes or Tracks as programs" ) - program = convert_plex_item_to_program( + program: Program = convert_plex_item_to_program( plex_item=item, plex_server=plex_server ) - custom_show_item = convert_program_to_custom_show_item( + custom_show_item: CustomShowItem = convert_program_to_custom_show_item( program=program, dizque_instance=self ) kwargs["content"].append(custom_show_item._full_data) @@ -1578,7 +1597,9 @@ def convert_program_to_custom_show_item(self, program: Program) -> CustomShowIte program=program, dizque_instance=self ) - def expand_custom_show_items(self, programs: List) -> List: + def expand_custom_show_items(self, + programs: List[Union[Program, FillerItem, CustomShow, Video, Movie, Episode, Track]]) \ + -> List[Union[Program, FillerItem, Redirect, Video, Movie, Episode, Track]]: """ Expand all custom shows in a list out to their individual programs diff --git a/dizqueTV/helpers.py b/dizqueTV/helpers.py index 3a911fa..f576d15 100644 --- a/dizqueTV/helpers.py +++ b/dizqueTV/helpers.py @@ -21,7 +21,7 @@ # Internal Helpers def _multithread( - func, elements: List, element_param_name: str, thread_count: int = 20, **kwargs + func, elements: List, element_param_name: str, thread_count: int = None, **kwargs ) -> List: """ Multithread a function for elements in a list @@ -39,6 +39,11 @@ def _multithread( :return: List of results from the function :rtype: list """ + # Thread count is the smallest of the following: + # - Number of elements in the list + # - Number of CPU cores * 8 (arbitrary) + # Or override with thread_count + thread_count = thread_count or min(len(element_param_name), (os.cpu_count() * 8)) thread_list = [] pool = ThreadPoolExecutor(thread_count) diff --git a/dizqueTV/models/channels.py b/dizqueTV/models/channels.py index 3e2f3ea..cc2ad86 100644 --- a/dizqueTV/models/channels.py +++ b/dizqueTV/models/channels.py @@ -57,7 +57,7 @@ def update(self, use_global_settings: bool = False, **kwargs) -> bool: new_settings_dict=kwargs, default_dict=self._data ) if self._dizque_instance.update_channel( - channel_number=self._channel_instance.number, transcoding=new_settings + channel_number=self._channel_instance.number, transcoding=new_settings ): self._channel_instance.refresh() del self @@ -93,10 +93,10 @@ def update(self, **kwargs) -> bool: new_settings_dict=kwargs, default_dict=self._data ) new_settings["firstProgramModulo"] = ( - self._channel_instance.startTime_datetime.timestamp() * 1000 - ) % new_settings["modulo"] + self._channel_instance.startTime_datetime.timestamp() * 1000 + ) % new_settings["modulo"] if self._dizque_instance.update_channel( - channel_number=self._channel_instance.number, onDemand=new_settings + channel_number=self._channel_instance.number, onDemand=new_settings ): self._channel_instance.refresh() del self @@ -133,7 +133,7 @@ def update(self, **kwargs) -> bool: """ new_watermark_dict = self._dizque_instance.fill_in_watermark_settings(**kwargs) if self._dizque_instance.update_channel( - channel_number=self._channel_instance.number, watermark=new_watermark_dict + channel_number=self._channel_instance.number, watermark=new_watermark_dict ): self._channel_instance.refresh() del self @@ -151,7 +151,7 @@ def __repr__(self): class TimeSlot(BaseObject): def __init__( - self, data: dict, program: TimeSlotItem = None, schedule_instance=None + self, data: dict, program: TimeSlotItem = None, schedule_instance=None ): super().__init__(data) self.time = data.get("time") @@ -229,7 +229,7 @@ def update(self, **kwargs): return self._channel_instance.update_schedule(**new_settings) def add_time_slot( - self, time_slot: TimeSlot = None, time_string: str = None, **kwargs + self, time_slot: TimeSlot = None, time_string: str = None, **kwargs ) -> bool: """ Add a time slot to this Schedule @@ -253,8 +253,8 @@ def add_time_slot( new_dictionary=kwargs, template_dict=TIME_SLOT_SETTINGS_TEMPLATE ) if not helpers._settings_are_complete( - new_settings_dict=new_settings_filtered, - template_settings_dict=TIME_SLOT_SETTINGS_TEMPLATE, + new_settings_dict=new_settings_filtered, + template_settings_dict=TIME_SLOT_SETTINGS_TEMPLATE, ): raise GeneralException("Missing settings required to make a time slot.") @@ -273,7 +273,7 @@ def add_time_slot( return self.update(slots=slots) def edit_time_slot( - self, time_slot: TimeSlot, time_string: str = None, **kwargs + self, time_slot: TimeSlot, time_string: str = None, **kwargs ) -> bool: """ Edit a time slot from this Schedule @@ -410,8 +410,8 @@ def _get_schedulable_items(self) -> List[TimeSlotItem]: schedulable_items = [] for program in self.programs: if ( - program.type == "customShow" - ): # custom shows not schedulable at this time + program.type == "customShow" + ): # custom shows not scheduleable at this time pass elif program.type == "redirect" and program.channel not in used_titles: schedulable_items.append( @@ -450,7 +450,7 @@ def programs(self) -> List[Union[Program, CustomShow]]: @decorators.check_for_dizque_instance def get_program( - self, program_title: str = None, redirect_channel_number: int = None + self, program_title: str = None, redirect_channel_number: int = None ) -> Union[Program, None]: """ Get a specific program on this channel @@ -468,7 +468,7 @@ def get_program( ) for program in self.programs: if (program_title and program.title == program_title) or ( - redirect_channel_number and redirect_channel_number == program.channel + redirect_channel_number and redirect_channel_number == program.channel ): return program return None @@ -544,11 +544,11 @@ def edit(self, **kwargs) -> bool: @decorators.check_for_dizque_instance def add_program( - self, - plex_item: Union[Video, Movie, Episode, Track] = None, - plex_server: PServer = None, - program: Union[Program, CustomShow] = None, - **kwargs, + self, + plex_item: Union[Video, Movie, Episode, Track] = None, + plex_server: PServer = None, + program: Union[Program, CustomShow] = None, + **kwargs, ) -> bool: """ Add a program to this channel @@ -587,9 +587,9 @@ def add_program( elif kwargs["type"] == "redirect": template = REDIRECT_PROGRAM_TEMPLATE if helpers._settings_are_complete( - new_settings_dict=kwargs, - template_settings_dict=template, - ignore_keys=["_id", "id"], + new_settings_dict=kwargs, + template_settings_dict=template, + ignore_keys=["_id", "id"], ): channel_data = self._data if not channel_data.get("duration"): @@ -603,9 +603,9 @@ def add_program( @decorators.check_for_dizque_instance def add_programs( - self, - programs: List[Union[Program, CustomShow, Video, Movie, Episode, Track]], - plex_server: PServer = None, + self, + programs: List[Union[Program, Redirect, FillerItem, CustomShow, Video, Movie, Episode, Track]], + plex_server: PServer = None, ) -> bool: """ Add multiple programs to this channel @@ -623,10 +623,12 @@ def add_programs( "You must provide at least one program to add to the channel." ) - programs = self._dizque_instance.expand_custom_show_items(programs=programs) + programs: List[Union[Program, Redirect, FillerItem, Video, Movie, Episode, Track]] = \ + self._dizque_instance.expand_custom_show_items(programs=programs) for program in programs: - if type(program) not in [Program, Redirect, CustomShowItem]: + if type(program) not in [Program, Redirect, FillerItem]: + # plex item needs to be converted to program if not plex_server and not self.plex_server: raise MissingParametersError( "Please include a plex_server if you are adding PlexAPI Video, " @@ -704,7 +706,7 @@ def update_program(self, program: Program, **kwargs) -> bool: channel_data = self._data for a_program in channel_data["programs"]: if (program.type == "redirect" and a_program["type"] == "redirect") or ( - a_program["title"] == program.title + a_program["title"] == program.title ): if kwargs.get("duration"): channel_data["duration"] -= a_program["duration"] @@ -729,7 +731,7 @@ def delete_program(self, program: Program) -> bool: channel_data = self._data for a_program in channel_data["programs"]: if (program.type == "redirect" and a_program["type"] == "redirect") or ( - a_program["title"] == program.title + a_program["title"] == program.title ): channel_data["duration"] -= a_program["duration"] channel_data["programs"].remove(a_program) @@ -770,10 +772,10 @@ def delete_show(self, show_name: str, season_number: int = None) -> bool: @decorators.check_for_dizque_instance def add_x_number_of_show_episodes( - self, - number_of_episodes: int, - list_of_episodes: List[Union[Program, Episode]], - plex_server: PServer = None, + self, + number_of_episodes: int, + list_of_episodes: List[Union[Program, Episode]], + plex_server: PServer = None, ) -> bool: """ Add the first X number of items from a list of programs to a dizqueTV channel @@ -807,11 +809,11 @@ def add_x_number_of_show_episodes( @decorators.check_for_dizque_instance def add_x_duration_of_show_episodes( - self, - duration_in_milliseconds: int, - list_of_episodes: List[Union[Program, Episode]], - plex_server: PServer = None, - allow_overtime: bool = False, + self, + duration_in_milliseconds: int, + list_of_episodes: List[Union[Program, Episode]], + plex_server: PServer = None, + allow_overtime: bool = False, ) -> bool: """ Add an X duration of items from a list of programs to a dizqueTV channel @@ -845,7 +847,7 @@ def add_x_duration_of_show_episodes( plex_server=(plex_server if plex_server else self.plex_server), ) if ( - total_runtime + list_of_episodes[list_index].duration + total_runtime + list_of_episodes[list_index].duration ) > duration_in_milliseconds: if allow_overtime: channel_data["programs"].append(list_of_episodes[list_index]._data) @@ -890,11 +892,11 @@ def _delete_all_offline_times(self) -> bool: @decorators.check_for_dizque_instance def add_filler_list( - self, - filler_list: FillerList = None, - filler_list_id: str = None, - weight: int = 300, - cooldown: int = 0, + self, + filler_list: FillerList = None, + filler_list_id: str = None, + weight: int = 300, + cooldown: int = 0, ) -> bool: """ Add a filler list to this channel @@ -922,9 +924,9 @@ def add_filler_list( "cooldown": cooldown, } if helpers._settings_are_complete( - new_settings_dict=new_settings_dict, - template_settings_dict=FILLER_LIST_CHANNEL_TEMPLATE, - ignore_keys=["_id", "id"], + new_settings_dict=new_settings_dict, + template_settings_dict=FILLER_LIST_CHANNEL_TEMPLATE, + ignore_keys=["_id", "id"], ): channel_data = self._data channel_data["fillerCollections"].append(new_settings_dict) @@ -934,7 +936,7 @@ def add_filler_list( @decorators.check_for_dizque_instance def delete_filler_list( - self, filler_list: FillerList = None, filler_list_id: str = None + self, filler_list: FillerList = None, filler_list_id: str = None ) -> bool: """ Delete a program from this channel @@ -991,8 +993,8 @@ def add_schedule(self, time_slots: List[TimeSlot], **kwargs) -> bool: template_dict=SCHEDULE_SETTINGS_TEMPLATE, ) if helpers._settings_are_complete( - new_settings_dict=schedule_settings, - template_settings_dict=SCHEDULE_SETTINGS_TEMPLATE, + new_settings_dict=schedule_settings, + template_settings_dict=SCHEDULE_SETTINGS_TEMPLATE, ): schedule = Schedule( data=schedule_settings, dizque_instance=None, channel_instance=self @@ -1019,8 +1021,8 @@ def add_random_schedule(self, time_slots: List[TimeSlot], **kwargs) -> bool: template_dict=RANDOM_SCHEDULE_SETTINGS_TEMPLATE, ) if helpers._settings_are_complete( - new_settings_dict=schedule_settings, - template_settings_dict=RANDOM_SCHEDULE_SETTINGS_TEMPLATE, + new_settings_dict=schedule_settings, + template_settings_dict=RANDOM_SCHEDULE_SETTINGS_TEMPLATE, ): schedule = Schedule( data=schedule_settings, dizque_instance=None, channel_instance=self @@ -1243,8 +1245,8 @@ def remove_redirects(self) -> bool: non_redirects = [] for item in self.programs: if ( - not helpers._object_has_attribute(obj=item, attribute_name="type") - or item.type != "redirect" + not helpers._object_has_attribute(obj=item, attribute_name="type") + or item.type != "redirect" ): non_redirects.append(item) if non_redirects and self.delete_all_programs(): @@ -1264,16 +1266,16 @@ def remove_specials(self) -> bool: item for item in self.programs if ( - helpers._object_has_attribute(obj=item, attribute_name="type") - and item.type != "redirect" + helpers._object_has_attribute(obj=item, attribute_name="type") + and item.type != "redirect" ) ] non_specials = [ item for item in non_redirects if ( - helpers._object_has_attribute(obj=item, attribute_name="season") - and item.season != 0 + helpers._object_has_attribute(obj=item, attribute_name="season") + and item.season != 0 ) ] if non_specials and self.delete_all_programs(): @@ -1314,7 +1316,7 @@ def pad_times(self, start_every_x_minutes: int) -> bool: @decorators.check_for_dizque_instance def add_reruns( - self, start_time: datetime, length_hours: int, times_to_repeat: int + self, start_time: datetime, length_hours: int, times_to_repeat: int ) -> bool: """ Add a block of reruns to a dizqueTV channel @@ -1355,7 +1357,7 @@ def add_reruns( @decorators.check_for_dizque_instance def add_channel_at_night( - self, night_channel_number: int, start_hour: int, end_hour: int + self, night_channel_number: int, start_hour: int, end_hour: int ) -> bool: """ Add a Channel at Night to a dizqueTV channel @@ -1434,7 +1436,7 @@ def add_channel_at_night( @decorators.check_for_dizque_instance def add_channel_at_night_alt( - self, night_channel_number: int, start_hour: int, end_hour: int + self, night_channel_number: int, start_hour: int, end_hour: int ) -> bool: """ Add a Channel at Night to a dizqueTV channel @@ -1477,10 +1479,10 @@ def add_channel_at_night_alt( programs=all_programs, minutes=int(time_until_night_block_start / 1000 / 60) ) if len(programs_to_add) == len( - all_programs + all_programs ): # all programs can play before night channel even starts if ( - total_running_time < time_until_night_block_start + total_running_time < time_until_night_block_start ): # add flex time between last item and night channel time_needed = time_until_night_block_start - total_running_time programs_to_add.append( @@ -1515,7 +1517,7 @@ def add_channel_at_night_alt( minutes=int(time_until_night_block_start / 1000 / 60), ) if ( - total_running_time < time_until_night_block_start + total_running_time < time_until_night_block_start ): # add flex time between last item and night channel time_needed = time_until_night_block_start - total_running_time programs_to_add.append( @@ -1595,13 +1597,13 @@ def balance_programs(self, margin_of_error: float = 0.1) -> bool: @decorators.check_for_dizque_instance def fast_forward( - self, - seconds: int = 0, - minutes: int = 0, - hours: int = 0, - days: int = 0, - months: int = 0, - years: int = 0, + self, + seconds: int = 0, + minutes: int = 0, + hours: int = 0, + days: int = 0, + months: int = 0, + years: int = 0, ) -> bool: """ Fast forward the channel start time by an amount of time @@ -1640,13 +1642,13 @@ def fast_forward( @decorators.check_for_dizque_instance def rewind( - self, - seconds: int = 0, - minutes: int = 0, - hours: int = 0, - days: int = 0, - months: int = 0, - years: int = 0, + self, + seconds: int = 0, + minutes: int = 0, + hours: int = 0, + days: int = 0, + months: int = 0, + years: int = 0, ) -> bool: """ Fast forward the channel start time by an amount of time diff --git a/dizqueTV/models/media.py b/dizqueTV/models/media.py index 58240d7..66c15be 100644 --- a/dizqueTV/models/media.py +++ b/dizqueTV/models/media.py @@ -43,6 +43,12 @@ def __init__(self, data: dict, dizque_instance, channel_instance=None): def __repr__(self): return f"{self.__class__.__name__}({self.title})" + @property + def full_name(self): + if self.type == "episode": + return f"{self.showTitle} - s{self.season}e{self.episode} - {self.title}" + return self.title + class Redirect(BaseMediaItem): def __init__(self, data: dict, dizque_instance, channel_instance):