From 7db63bf19606d288e922405f842e76071f29967b Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Fri, 31 May 2024 22:11:47 +1000 Subject: [PATCH] Use consistent return values for function cache to avoid caching errors #782 --- .../kodion/abstract_provider.py | 2 +- .../kodion/plugin/xbmc/xbmc_plugin.py | 2 +- .../kodion/sql_store/function_cache.py | 35 +++++++++++-------- .../youtube_plugin/youtube/client/youtube.py | 25 +++++++------ .../youtube/helper/resource_manager.py | 5 ++- .../youtube/helper/yt_playlist.py | 6 ++-- .../youtube/helper/yt_specials.py | 3 +- .../lib/youtube_plugin/youtube/provider.py | 10 +++--- 8 files changed, 48 insertions(+), 40 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/abstract_provider.py b/resources/lib/youtube_plugin/kodion/abstract_provider.py index 6ffa394a5..89958a08e 100644 --- a/resources/lib/youtube_plugin/kodion/abstract_provider.py +++ b/resources/lib/youtube_plugin/kodion/abstract_provider.py @@ -223,8 +223,8 @@ def reroute(self, context, re_match=None, path=None, params=None): result, options = function_cache.run( self.navigate, seconds=None, - _cacheparams=function_cache.PARAMS_NONE, _refresh=True, + _scope=function_cache.SCOPE_NONE, context=context.clone(path, params), ) finally: diff --git a/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py b/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py index d21ff324d..758f4bb07 100644 --- a/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py +++ b/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py @@ -159,8 +159,8 @@ def run(self, provider, context, focused=None): result, options = function_cache.run( provider.navigate, seconds=None, - _cacheparams=function_cache.PARAMS_NONE, _oneshot=True, + _scope=function_cache.SCOPE_NONE, context=context.clone(route), ) ui.clear_property(REROUTE) diff --git a/resources/lib/youtube_plugin/kodion/sql_store/function_cache.py b/resources/lib/youtube_plugin/kodion/sql_store/function_cache.py index 06297fe42..5578b8791 100644 --- a/resources/lib/youtube_plugin/kodion/sql_store/function_cache.py +++ b/resources/lib/youtube_plugin/kodion/sql_store/function_cache.py @@ -24,9 +24,9 @@ class FunctionCache(Storage): _sql = {} _BUILTIN = str.__module__ - PARAMS_NONE = 0 - PARAMS_BUILTINS = 1 - PARAMS_ALL = 2 + SCOPE_NONE = 0 + SCOPE_BUILTINS = 1 + SCOPE_ALL = 2 def __init__(self, filepath, max_file_size_mb=5): max_file_size_kb = max_file_size_mb * 1024 @@ -49,7 +49,7 @@ def disable(self): self._enabled = False @classmethod - def _create_id_from_func(cls, partial_func, hash_params=PARAMS_ALL): + def _create_id_from_func(cls, partial_func, scope=SCOPE_ALL): """ Creates an id from the given function :param partial_func: @@ -60,7 +60,7 @@ def _create_id_from_func(cls, partial_func, hash_params=PARAMS_ALL): partial_func.func.__module__, partial_func.func.__name__, ) - if hash_params == cls.PARAMS_BUILTINS: + if scope == cls.SCOPE_BUILTINS: signature = chain( signature, (( @@ -74,7 +74,7 @@ def _create_id_from_func(cls, partial_func, hash_params=PARAMS_ALL): (key, type(arg)) ) for key, arg in partial_func.keywords.items()), ) - elif hash_params == cls.PARAMS_ALL: + elif scope == cls.SCOPE_ALL: signature = chain( signature, partial_func.args, @@ -101,27 +101,34 @@ def run(self, func, seconds, *args, **kwargs): :param int|None seconds: max allowable age of cached result :param tuple args: positional arguments passed to the function :param dict kwargs: keyword arguments passed to the function - :keyword _cacheparams: (int) cache result for function and parameters. - 0: function only, - 1: include value of builtin type parameters - 2: include value of all parameters, default 2 + :keyword _scope: (int) cache result if matching: + 0: function only, + 1: function + value of builtin type parameters + 2: function + value of all parameters, default 2 + :keyword _ignore_value: (Any) don't cache func return value if equal to + _ignored_value, default None :keyword _oneshot: (bool) remove previously cached result, default False :keyword _refresh: (bool) updates cache with new result, default False + :keyword _retry_value: (Any) re-evaluate func if cached value is equal + _retry_value, default None :return: """ - cache_params = kwargs.pop('_cacheparams', self.PARAMS_ALL) + scope = kwargs.pop('_scope', self.SCOPE_ALL) + ignore_value = kwargs.pop('_ignore_value', None) oneshot = kwargs.pop('_oneshot', False) refresh = kwargs.pop('_refresh', False) + retry_value = kwargs.pop('_retry_value', None) partial_func = partial(func, *args, **kwargs) # if caching is disabled call the function if not self._enabled: return partial_func() - cache_id = self._create_id_from_func(partial_func, cache_params) - data = None if refresh else self._get(cache_id, seconds=seconds) - if data is None: + cache_id = self._create_id_from_func(partial_func, scope) + data = retry_value if refresh else self._get(cache_id, seconds=seconds) + if data == retry_value: data = partial_func() + if data != ignore_value: self._set(cache_id, data) elif oneshot: self._remove(cache_id) diff --git a/resources/lib/youtube_plugin/youtube/client/youtube.py b/resources/lib/youtube_plugin/youtube/client/youtube.py index 0e824705a..c39daeb6c 100644 --- a/resources/lib/youtube_plugin/youtube/client/youtube.py +++ b/resources/lib/youtube_plugin/youtube/client/youtube.py @@ -486,11 +486,6 @@ def get_recommended_for_home(self, visitor='', page_token='', click_tracking=''): - payload = { - 'kind': 'youtube#activityListResponse', - 'items': [] - } - post_data = {'browseId': 'FEwhat_to_watch'} if page_token: post_data['continuation'] = page_token @@ -511,7 +506,7 @@ def get_recommended_for_home(self, path='browse', post_data=post_data) if not result: - return payload + return None recommended_videos = self.json_traverse( result, @@ -565,7 +560,7 @@ def get_recommended_for_home(self, ) ) if not recommended_videos: - return payload + return None v3_response = { 'kind': 'youtube#activityListResponse', @@ -611,6 +606,8 @@ def get_recommended_for_home(self, if visitor: v3_response['visitorData'] = visitor + if not v3_response['items']: + v3_response = None return v3_response def get_related_for_home(self, page_token='', refresh=False): @@ -1104,7 +1101,7 @@ def get_related_videos(self, post_data=post_data, no_login=True) if not result: - return {} + return None related_videos = self.json_traverse(result, path=( ( @@ -1270,14 +1267,17 @@ def get_related_videos(self, max_results=remaining, **kwargs ) - if 'nextPageToken' in continuation: + if continuation and 'nextPageToken' in continuation: page_token = continuation['nextPageToken'] else: page_token = '' if 'items' in continuation: items.extend(continuation['items']) - v3_response['items'] = items + if items: + v3_response['items'] = items + else: + v3_response = None return v3_response def get_parent_comments(self, @@ -1744,7 +1744,10 @@ def _sort_by_date_time(item, limits=limits): else: items = [] - v3_response['items'] = items + if items: + v3_response['items'] = items + else: + v3_response = None return v3_response def get_saved_playlists(self, page_token, offset): diff --git a/resources/lib/youtube_plugin/youtube/helper/resource_manager.py b/resources/lib/youtube_plugin/youtube/helper/resource_manager.py index 4bd74b2eb..d775cfa04 100644 --- a/resources/lib/youtube_plugin/youtube/helper/resource_manager.py +++ b/resources/lib/youtube_plugin/youtube/helper/resource_manager.py @@ -264,9 +264,8 @@ def get_related_playlists(self, channel_id, defer_cache=False): break if item is None: - return {} - - return item.get('contentDetails', {}).get('relatedPlaylists', {}) + return None + return item.get('contentDetails', {}).get('relatedPlaylists') def get_videos(self, ids, diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_playlist.py b/resources/lib/youtube_plugin/youtube/helper/yt_playlist.py index 5230ebcb8..8096026e0 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_playlist.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_playlist.py @@ -181,14 +181,15 @@ def _process_select_playlist(provider, context): thumb_size = context.get_settings().get_thumbnail_size() default_thumb = context.create_resource_path('media', 'playlist.png') - while True: + while 1: current_page += 1 json_data = function_cache.run(client.get_playlists_of_channel, function_cache.ONE_MINUTE // 3, _refresh=params.get('refresh'), channel_id='mine', page_token=page_token) - + if not json_data: + break playlists = json_data.get('items', []) page_token = json_data.get('nextPageToken', '') @@ -252,7 +253,6 @@ def _process_select_playlist(provider, context): new_params = dict(context.get_params(), playlist_id=result) new_context = context.clone(new_params=new_params) _process_add_video(provider, new_context, keymap_action) - break break diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_specials.py b/resources/lib/youtube_plugin/youtube/helper/yt_specials.py index 8cb2d627f..408034d2a 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_specials.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_specials.py @@ -120,7 +120,8 @@ def _process_browse_channels(provider, context, client): else: function_cache = context.get_function_cache() json_data = function_cache.run(client.get_guide_categories, - function_cache.ONE_MONTH) + function_cache.ONE_MONTH, + _refresh=context.get_param('refresh')) if not json_data: return False diff --git a/resources/lib/youtube_plugin/youtube/provider.py b/resources/lib/youtube_plugin/youtube/provider.py index 4ca8b0f79..0dac7a2bb 100644 --- a/resources/lib/youtube_plugin/youtube/provider.py +++ b/resources/lib/youtube_plugin/youtube/provider.py @@ -413,12 +413,11 @@ def _on_channel_live(self, context, re_match): playlists = function_cache.run(resource_manager.get_related_playlists, function_cache.ONE_DAY, channel_id=channel_id) - upload_playlist = playlists.get('uploads', '') - if upload_playlist: + if playlists and 'uploads' in playlists: json_data = function_cache.run(client.get_playlist_items, function_cache.ONE_MINUTE * 5, _refresh=params.get('refresh'), - playlist_id=upload_playlist, + playlist_id=playlists['uploads'], page_token=page_token) if not json_data: return result @@ -558,12 +557,11 @@ def _on_channel(self, context, re_match): playlists = function_cache.run(resource_manager.get_related_playlists, function_cache.ONE_DAY, channel_id=channel_id) - upload_playlist = playlists.get('uploads', '') - if upload_playlist: + if playlists and 'uploads' in playlists: json_data = function_cache.run(client.get_playlist_items, function_cache.ONE_MINUTE * 5, _refresh=params.get('refresh'), - playlist_id=upload_playlist, + playlist_id=playlists['uploads'], page_token=page_token) if not json_data: return result