From 93e63da14ed3c51a3fa5dac027033302027fa9c4 Mon Sep 17 00:00:00 2001 From: Florian Bischof Date: Tue, 2 Jan 2024 11:51:42 +0100 Subject: [PATCH 1/4] feat: Show current episode in season list and mark current episode in episode list --- .../resource.language.en_gb/strings.po | 8 +++- resources/lib/kodi/infolabels.py | 7 +++- .../nfsession/directorybuilder/dir_builder.py | 12 +++++- .../directorybuilder/dir_builder_items.py | 37 ++++++++++++++++--- .../directorybuilder/dir_path_requests.py | 20 +++++++++- resources/lib/utils/api_paths.py | 7 +++- resources/lib/utils/data_types.py | 12 ++++++ resources/settings.xml | 22 +++++++++++ 8 files changed, 113 insertions(+), 12 deletions(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 6d440339b..1a6c3ad74 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -845,7 +845,13 @@ msgctxt "#30216" msgid "Color of the titles marked as \"Remind me\"" msgstr "" -#. Unused 30217 to 30218 +msgctxt "#30217" +msgid "Color of the title of the current episode in a season" +msgstr " + +msgctxt "#30218" +msgid "Show the current episode in the season list" +msgstr " msgctxt "#30219" msgid "Disable notification for synchronization completed" diff --git a/resources/lib/kodi/infolabels.py b/resources/lib/kodi/infolabels.py index d207d466c..2d47a31f1 100644 --- a/resources/lib/kodi/infolabels.py +++ b/resources/lib/kodi/infolabels.py @@ -69,11 +69,14 @@ def get_info(videoid, item, raw_data, profile_language_code='', delayed_db_op=Fa def add_info_list_item(list_item: ListItemW, videoid, item, raw_data, is_in_mylist, common_data, art_item=None, - is_in_remind_me=False): + is_in_remind_me=False, is_current_episode=False): """Add infolabels and art to a ListItem""" infos, quality_infos = get_info(videoid, item, raw_data, delayed_db_op=True, common_data=common_data) list_item.addStreamInfoFromDict(quality_infos) - if is_in_mylist and common_data.get('mylist_titles_color'): + if is_current_episode and common_data.get('current_episode_titles_color'): + # Highlight ListItem title when it is the current episode of the season + list_item.setLabel(_colorize_text(common_data['current_episode_titles_color'], list_item.getLabel())) + elif is_in_mylist and common_data.get('mylist_titles_color'): # Highlight ListItem title when the videoid is contained in "My list" list_item.setLabel(_colorize_text(common_data['mylist_titles_color'], list_item.getLabel())) elif is_in_remind_me: diff --git a/resources/lib/services/nfsession/directorybuilder/dir_builder.py b/resources/lib/services/nfsession/directorybuilder/dir_builder.py index 67e9c8165..434c30dfa 100644 --- a/resources/lib/services/nfsession/directorybuilder/dir_builder.py +++ b/resources/lib/services/nfsession/directorybuilder/dir_builder.py @@ -66,13 +66,21 @@ def get_profiles(self, request_update, preselect_guid=None, detailed_info=True): def get_seasons(self, pathitems, tvshowid_dict, perpetual_range_start): tvshowid = VideoId.from_dict(tvshowid_dict) season_list = self.req_seasons(tvshowid, perpetual_range_start=perpetual_range_start) - return build_season_listing(season_list, tvshowid, pathitems) + if len(season_list.seasons) > 1 and G.ADDON.getSettingBool('show_current_episode'): + current_episode = self.req_current_episode(season_list.current_seasonid) + else: + current_episode = None + return build_season_listing(season_list, tvshowid, current_episode, pathitems) @measure_exec_time_decorator(is_immediate=True) def get_episodes(self, pathitems, seasonid_dict, perpetual_range_start): seasonid = VideoId.from_dict(seasonid_dict) episodes_list = self.req_episodes(seasonid, perpetual_range_start=perpetual_range_start) - return build_episode_listing(episodes_list, seasonid, pathitems) + if G.ADDON.getSettingInt('current_episode_titles_color') > 0: + current_episode = self.req_current_episode(seasonid) + else: + current_episode = None + return build_episode_listing(episodes_list, seasonid, current_episode, pathitems) @measure_exec_time_decorator(is_immediate=True) def get_video_list(self, list_id, menu_data, is_dynamic_id): diff --git a/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py b/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py index 36ce64324..03973a95c 100644 --- a/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py +++ b/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py @@ -129,11 +129,32 @@ def _create_profile_item(profile_guid, is_selected, is_autoselect, is_autoselect @measure_exec_time_decorator(is_immediate=True) -def build_season_listing(season_list, tvshowid, pathitems=None): +def build_season_listing(season_list, tvshowid, current_episode=None, pathitems=None): """Build a season listing""" common_data = get_common_data() directory_items = [_create_season_item(tvshowid, seasonid_value, season, season_list, common_data) for seasonid_value, season in season_list.seasons.items()] + + if current_episode: + common_data_episode = get_common_data() + common_data_episode['params'] = get_param_watched_status_by_profile() + common_data_episode['set_watched_status'] = G.ADDON.getSettingBool('sync_watched_status') + common_data_episode['active_profile_guid'] = G.LOCAL_DB.get_active_profile_guid() + + episodeid_value, episode_data = current_episode.episode + current_episode_item = _create_episode_item(season_list.current_seasonid, episodeid_value, episode_data, current_episode, common_data_episode) + list_item = current_episode_item[1] + + if list_item.getProperty('isPlayable') == 'true': + episode_summary = episode_data['summary']['value'] + label = f"{episode_summary['season']}x{episode_summary['episode']:02d}. {episode_data['title']['value']}" + list_item.setLabel(label) + + episodeid = season_list.current_seasonid.derive_episode(episodeid_value) + add_info_list_item(list_item, episodeid, episode_data, current_episode.data, False, common_data_episode) + + directory_items.insert(0, current_episode_item) + # add_items_previous_next_page use the new value of perpetual_range_selector add_items_previous_next_page(directory_items, pathitems, season_list.perpetual_range_selector, tvshowid) G.CACHE_MANAGEMENT.execute_pending_db_ops() @@ -151,14 +172,20 @@ def _create_season_item(tvshowid, seasonid_value, season, season_list, common_da @measure_exec_time_decorator(is_immediate=True) -def build_episode_listing(episodes_list, seasonid, pathitems=None): +def build_episode_listing(episodes_list, seasonid, current_episode=None, pathitems=None): """Build a episodes listing of a season""" common_data = get_common_data() common_data['params'] = get_param_watched_status_by_profile() common_data['set_watched_status'] = G.ADDON.getSettingBool('sync_watched_status') common_data['active_profile_guid'] = G.LOCAL_DB.get_active_profile_guid() - directory_items = [_create_episode_item(seasonid, episodeid_value, episode, episodes_list, common_data) + if current_episode: + common_data['current_episode_titles_color'] = get_color_name(G.ADDON.getSettingInt('current_episode_titles_color')) + current_episodeid_value = current_episode.episode[0] + else: + current_episodeid_value = None + + directory_items = [_create_episode_item(seasonid, episodeid_value, episode, episodes_list, common_data, current_episodeid_value == episodeid_value) for episodeid_value, episode in episodes_list.episodes.items()] # add_items_previous_next_page use the new value of perpetual_range_selector @@ -168,7 +195,7 @@ def build_episode_listing(episodes_list, seasonid, pathitems=None): 'title': f'{episodes_list.tvshow["title"]["value"]} - {episodes_list.season["summary"]["value"]["name"]}'} -def _create_episode_item(seasonid, episodeid_value, episode, episodes_list, common_data): +def _create_episode_item(seasonid, episodeid_value, episode, episodes_list, common_data, is_current_episode=False): is_playable = episode['availability'].get('value', {}).get('isPlayable', False) episodeid = seasonid.derive_episode(episodeid_value) list_item = ListItemW(label=episode['title']['value']) @@ -176,7 +203,7 @@ def _create_episode_item(seasonid, episodeid_value, episode, episodes_list, comm 'isPlayable': str(is_playable).lower(), 'nf_videoid': episodeid.to_string() }) - add_info_list_item(list_item, episodeid, episode, episodes_list.data, False, common_data) + add_info_list_item(list_item, episodeid, episode, episodes_list.data, False, common_data, None, False, is_current_episode) set_watched_status(list_item, episode, common_data) if is_playable: url = common.build_url(videoid=episodeid, mode=G.MODE_PLAY, params=common_data['params']) diff --git a/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py b/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py index 628cb0749..d85232fcf 100644 --- a/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py +++ b/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py @@ -12,7 +12,7 @@ from resources.lib import common from resources.lib.utils.data_types import (VideoListSorted, SubgenreList, SeasonList, EpisodeList, LoCo, VideoList, SearchVideoList, CustomVideoList, LoLoMoCategory, VideoListSupplemental, - VideosList) + VideosList, CurrentEpisode) from resources.lib.common.exceptions import InvalidVideoListTypeError, InvalidVideoId from resources.lib.utils.api_paths import (VIDEO_LIST_PARTIAL_PATHS, RANGE_PLACEHOLDER, VIDEO_LIST_BASIC_PARTIAL_PATHS, SEASONS_PARTIAL_PATHS, EPISODES_PARTIAL_PATHS, ART_PARTIAL_PATHS, @@ -140,6 +140,24 @@ def req_episodes(self, videoid, perpetual_range_start=None): } path_response = self.nfsession.perpetual_path_request(**call_args) return EpisodeList(videoid, path_response) + + def req_current_episode(self, videoid): + """Retrieve the current episode of a season""" + if videoid.mediatype != common.VideoId.SEASON: + raise InvalidVideoId(f'Cannot request episode list for {videoid}') + LOG.debug('Requesting current episode for {}', videoid) + paths = ([['seasons', videoid.seasonid, 'summary']] + + [['seasons', videoid.seasonid, 'componentSummary']] + + build_paths(['seasons', videoid.seasonid, 'episodes', 'current'], EPISODES_PARTIAL_PATHS) + + build_paths(['videos', videoid.tvshowid], ART_PARTIAL_PATHS + [[['title', 'delivery']]])) + + call_args = { + 'paths': paths, + 'length_params': ['stdlist_wid', ['seasons', videoid.seasonid, 'episodes']] + } + path_response = self.nfsession.perpetual_path_request(**call_args) + + return CurrentEpisode(videoid, path_response) @cache_utils.cache_output(cache_utils.CACHE_COMMON, identify_append_from_kwarg_name='perpetual_range_start', ignore_self_class=True) diff --git a/resources/lib/utils/api_paths.py b/resources/lib/utils/api_paths.py index 762c66261..e0e630263 100644 --- a/resources/lib/utils/api_paths.py +++ b/resources/lib/utils/api_paths.py @@ -69,7 +69,8 @@ SEASONS_PARTIAL_PATHS = [ ['seasonList', RANGE_PLACEHOLDER, 'summary'], - ['title'] + ['title'], + ["seasonList","current","episodes","current","summary"] ] + ART_PARTIAL_PATHS EPISODES_PARTIAL_PATHS = [ @@ -174,6 +175,10 @@ def iterate_references(source): continue yield (index, path) + if 'current' in source: + path = reference_path(source['current']) + if path is not None: + yield ('current', path) def count_references(source): counter = 0 diff --git a/resources/lib/utils/data_types.py b/resources/lib/utils/data_types.py index 914e3be5c..d35cbc81a 100644 --- a/resources/lib/utils/data_types.py +++ b/resources/lib/utils/data_types.py @@ -255,6 +255,11 @@ def __init__(self, videoid, path_response): self.tvshow = self.data['videos'][self.videoid.tvshowid] self.seasons = OrderedDict( resolve_refs(self.tvshow['seasonList'], self.data)) + if self.tvshow['seasonList']['current']: + self.current_seasonid_vaule = self.tvshow['seasonList']['current']['value'][1] + self.current_seasonid = videoid.derive_season(self.current_seasonid_vaule) + self.current_season = self.seasons[self.current_seasonid_vaule] + self.current_episodeid_value = int(self.current_season['episodes']['current']['value'][1]) class EpisodeList: @@ -269,6 +274,13 @@ def __init__(self, videoid, path_response): self.episodes = OrderedDict( resolve_refs(self.season['episodes'], self.data)) +class CurrentEpisode: + def __init__(self, videoid, path_response): + self.data = path_response + self.videoid = videoid + self.tvshow = self.data['videos'][self.videoid.tvshowid] + self.season = self.data['seasons'][self.videoid.seasonid] + self.episode = next(resolve_refs(self.season['episodes'], self.data)) class SubgenreList: """A list of subgenre.""" diff --git a/resources/settings.xml b/resources/settings.xml index 3f889c214..4b04cfa67 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -64,6 +64,11 @@ false + + 0 + true + + 0 1 @@ -115,6 +120,23 @@ + + 0 + 5 + + + + + + + + + + + + + + From ca44da43fb72b42f8ff7662cf3d156c8f735474a Mon Sep 17 00:00:00 2001 From: Florian Bischof Date: Tue, 2 Jan 2024 16:05:06 +0100 Subject: [PATCH 2/4] Remove obsolete lines --- resources/lib/utils/data_types.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/resources/lib/utils/data_types.py b/resources/lib/utils/data_types.py index d35cbc81a..2d9c5410a 100644 --- a/resources/lib/utils/data_types.py +++ b/resources/lib/utils/data_types.py @@ -255,11 +255,8 @@ def __init__(self, videoid, path_response): self.tvshow = self.data['videos'][self.videoid.tvshowid] self.seasons = OrderedDict( resolve_refs(self.tvshow['seasonList'], self.data)) - if self.tvshow['seasonList']['current']: - self.current_seasonid_vaule = self.tvshow['seasonList']['current']['value'][1] - self.current_seasonid = videoid.derive_season(self.current_seasonid_vaule) - self.current_season = self.seasons[self.current_seasonid_vaule] - self.current_episodeid_value = int(self.current_season['episodes']['current']['value'][1]) + if 'current' in self.tvshow['seasonList']: + self.current_seasonid = videoid.derive_season(self.tvshow['seasonList']['current']['value'][1]) class EpisodeList: From 498a294e74f417f3f2aba0960f807038c7a96b48 Mon Sep 17 00:00:00 2001 From: Florian Bischof Date: Tue, 2 Jan 2024 16:11:54 +0100 Subject: [PATCH 3/4] Reduce payload of "current" request --- resources/lib/utils/api_paths.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/utils/api_paths.py b/resources/lib/utils/api_paths.py index e0e630263..0b80f50b5 100644 --- a/resources/lib/utils/api_paths.py +++ b/resources/lib/utils/api_paths.py @@ -70,7 +70,7 @@ SEASONS_PARTIAL_PATHS = [ ['seasonList', RANGE_PLACEHOLDER, 'summary'], ['title'], - ["seasonList","current","episodes","current","summary"] + ["seasonList","current"] ] + ART_PARTIAL_PATHS EPISODES_PARTIAL_PATHS = [ From f62f902f27e1d03565f079b8eac72794a3226892 Mon Sep 17 00:00:00 2001 From: Florian Bischof Date: Wed, 3 Jan 2024 17:42:39 +0100 Subject: [PATCH 4/4] Remove trailing space --- .../services/nfsession/directorybuilder/dir_builder_items.py | 2 +- .../services/nfsession/directorybuilder/dir_path_requests.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py b/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py index 03973a95c..147d61522 100644 --- a/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py +++ b/resources/lib/services/nfsession/directorybuilder/dir_builder_items.py @@ -144,7 +144,7 @@ def build_season_listing(season_list, tvshowid, current_episode=None, pathitems= episodeid_value, episode_data = current_episode.episode current_episode_item = _create_episode_item(season_list.current_seasonid, episodeid_value, episode_data, current_episode, common_data_episode) list_item = current_episode_item[1] - + if list_item.getProperty('isPlayable') == 'true': episode_summary = episode_data['summary']['value'] label = f"{episode_summary['season']}x{episode_summary['episode']:02d}. {episode_data['title']['value']}" diff --git a/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py b/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py index d85232fcf..9a7592001 100644 --- a/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py +++ b/resources/lib/services/nfsession/directorybuilder/dir_path_requests.py @@ -140,7 +140,7 @@ def req_episodes(self, videoid, perpetual_range_start=None): } path_response = self.nfsession.perpetual_path_request(**call_args) return EpisodeList(videoid, path_response) - + def req_current_episode(self, videoid): """Retrieve the current episode of a season""" if videoid.mediatype != common.VideoId.SEASON: @@ -150,7 +150,7 @@ def req_current_episode(self, videoid): [['seasons', videoid.seasonid, 'componentSummary']] + build_paths(['seasons', videoid.seasonid, 'episodes', 'current'], EPISODES_PARTIAL_PATHS) + build_paths(['videos', videoid.tvshowid], ART_PARTIAL_PATHS + [[['title', 'delivery']]])) - + call_args = { 'paths': paths, 'length_params': ['stdlist_wid', ['seasons', videoid.seasonid, 'episodes']]