diff --git a/resources/lib/youtube_plugin/kodion/items/base_item.py b/resources/lib/youtube_plugin/kodion/items/base_item.py index fd93a9d51..7d75c9ac1 100644 --- a/resources/lib/youtube_plugin/kodion/items/base_item.py +++ b/resources/lib/youtube_plugin/kodion/items/base_item.py @@ -34,6 +34,8 @@ def __init__(self, name, uri, image=None, fanart=None): self.set_name(name) self._uri = uri + self._available = True + self._callback = None self._image = '' if image: @@ -42,6 +44,7 @@ def __init__(self, name, uri, image=None, fanart=None): if fanart: self.set_fanart(fanart) + self._bookmark_id = None self._bookmark_timestamp = None self._context_menu = None self._added_utc = None @@ -144,6 +147,22 @@ def get_uri(self): """ return self._uri + @property + def available(self): + return self._available + + @available.setter + def available(self, value): + self._available = value + + @property + def callback(self): + return self._callback + + @callback.setter + def callback(self, value): + self._callback = value + def set_image(self, image): if not image: return @@ -238,6 +257,14 @@ def get_count(self): def set_count(self, count): self._count = int(count or 0) + @property + def bookmark_id(self): + return self._bookmark_id + + @bookmark_id.setter + def bookmark_id(self, value): + self._bookmark_id = value + def set_bookmark_timestamp(self, timestamp): self._bookmark_timestamp = timestamp @@ -248,6 +275,10 @@ def get_bookmark_timestamp(self): def playable(self): return self._playable + @playable.setter + def playable(self, value): + self._playable = value + def add_artist(self, artist): if artist: if self._artists is None: @@ -337,3 +368,6 @@ def encode(self, obj, nested=False): if nested: return output return super(_Encoder, self).encode(output) + + def default(self, obj): + pass diff --git a/resources/lib/youtube_plugin/kodion/items/utils.py b/resources/lib/youtube_plugin/kodion/items/utils.py index 6344d986f..7b595da42 100644 --- a/resources/lib/youtube_plugin/kodion/items/utils.py +++ b/resources/lib/youtube_plugin/kodion/items/utils.py @@ -52,12 +52,17 @@ def from_json(json_data, *args): :param json_data: :return: """ - timestamp = args[0][1] if args and args[0] and len(args[0]) == 4 else None + if args and args[0] and len(args[0]) == 4: + bookmark_id = args[0][0] + bookmark_timestamp = args[0][1] + else: + bookmark_id = None + bookmark_timestamp = None if isinstance(json_data, string_type): if json_data == to_str(None): # Channel bookmark that will be updated. Store timestamp for update - return timestamp + return bookmark_timestamp json_data = json.loads(json_data, object_hook=_decoder) item_type = json_data.get('type') @@ -69,6 +74,10 @@ def from_json(json_data, *args): for key, value in json_data.get('data', {}).items(): if hasattr(item, key): setattr(item, key, value) - item.set_bookmark_timestamp(timestamp) + + if bookmark_id: + item.bookmark_id = bookmark_id + if bookmark_timestamp: + item.set_bookmark_timestamp(bookmark_timestamp) return item diff --git a/resources/lib/youtube_plugin/youtube/helper/resource_manager.py b/resources/lib/youtube_plugin/youtube/helper/resource_manager.py index 76b1831fe..9feec0d75 100644 --- a/resources/lib/youtube_plugin/youtube/helper/resource_manager.py +++ b/resources/lib/youtube_plugin/youtube/helper/resource_manager.py @@ -322,7 +322,8 @@ def get_videos(self, for yt_item in batch.get('items', []) if yt_item } - new_data = dict(dict.fromkeys(to_update, {}), **new_data) + new_data = dict(dict.fromkeys(to_update, {'_unavailable': True}), + **new_data) result.update(new_data) self.cache_data(new_data, defer=defer_cache) diff --git a/resources/lib/youtube_plugin/youtube/helper/utils.py b/resources/lib/youtube_plugin/youtube/helper/utils.py index dc8026a4f..8f512db99 100644 --- a/resources/lib/youtube_plugin/youtube/helper/utils.py +++ b/resources/lib/youtube_plugin/youtube/helper/utils.py @@ -571,11 +571,20 @@ def update_video_infos(provider, context, video_id_dict, playlist_match = __RE_PLAYLIST.match(path) for video_id, yt_item in data.items(): - if not yt_item or 'snippet' not in yt_item: + if not yt_item: + continue + + media_item = video_id_dict.get(video_id) + if not media_item: + continue + + if 'snippet' not in yt_item: + if yt_item.get('_unavailable'): + media_item.playable = False + media_item.available = False continue snippet = yt_item['snippet'] - media_item = video_id_dict[video_id] media_item.set_mediatype( CONTENT.AUDIO_TYPE if isinstance(media_item, AudioItem) else @@ -1232,7 +1241,7 @@ def filter_videos(items, return [ item for item in items - if (not item.playable or ( + if ((item.callback and item.callback(item)) or not item.playable or ( (completed and item.completed) or (live and item.live and not item.upcoming) or (premieres and upcoming and item.upcoming and not item.live) diff --git a/resources/lib/youtube_plugin/youtube/helper/v3.py b/resources/lib/youtube_plugin/youtube/helper/v3.py index 9e799f82d..6468bed74 100644 --- a/resources/lib/youtube_plugin/youtube/helper/v3.py +++ b/resources/lib/youtube_plugin/youtube/helper/v3.py @@ -32,7 +32,7 @@ def _process_list_response(provider, context, json_data, item_filter): yt_items = json_data.get('items', []) if not yt_items: context.log_warning('v3 response: Items list is empty') - return [] + return None video_id_dict = {} channel_id_dict = {} @@ -40,7 +40,8 @@ def _process_list_response(provider, context, json_data, item_filter): playlist_item_id_dict = {} subscription_id_dict = {} - result = [] + items = [] + do_callbacks = False new_params = {} params = context.get_params() @@ -81,7 +82,6 @@ def _process_list_response(provider, context, json_data, item_filter): description = strip_html_from_text(localised_info.get('description') or snippet.get('description') or '') - # context.log_debug(f'***********\n{item_id = }, {title = }\n{yt_item = }\n***************') thumbnails = snippet.get('thumbnails') if not thumbnails: @@ -273,13 +273,14 @@ def _process_list_response(provider, context, json_data, item_filter): if isinstance(item, VideoItem): # Set track number from playlist, or set to current list length to # match "Default" (unsorted) sort order - position = snippet.get('position') or len(result) + position = snippet.get('position') or len(items) item.set_track_number(position + 1) if '_callback' in yt_item: - yt_item['_callback'](item) + item.callback = yt_item['_callback'] + do_callbacks = True - result.append(item) + items.append(item) # this will also update the channel_id_dict with the correct channel_id # for each video. @@ -437,7 +438,7 @@ def _fetch(resource): resource['thread'] = new_thread new_thread.start() - return result + return items, do_callbacks _KNOWN_RESPONSE_KINDS = { @@ -479,19 +480,23 @@ def response_to_items(provider, provider, context, json_data, item_filter ) if not result: - return result + return [] + + items, do_callbacks = result + if not items: + return items else: raise KodionException('Unknown kind: %s' % kind) - if item_filter: - result = filter_videos(result, **item_filter) + if item_filter or do_callbacks: + items = filter_videos(items, **item_filter) if sort is not None: - result.sort(key=sort, reverse=reverse) + items.sort(key=sort, reverse=reverse) # no processing of next page item if not process_next_page or params.get('hide_next_page'): - return result + return items # next page """ @@ -516,7 +521,7 @@ def response_to_items(provider, elif current_page: new_params['page_token'] = '' else: - return result + return items page_info = json_data.get('pageInfo', {}) yt_total_results = int(page_info.get('totalResults', 0)) @@ -531,7 +536,7 @@ def response_to_items(provider, next_page = 1 new_params['page'] = 1 else: - return result + return items yt_visitor_data = json_data.get('visitorData') if yt_visitor_data: @@ -547,9 +552,9 @@ def response_to_items(provider, new_params['offset'] = offset next_page_item = NextPageItem(context, new_params) - result.append(next_page_item) + items.append(next_page_item) - return result + return items def _parse_kind(item): diff --git a/resources/lib/youtube_plugin/youtube/provider.py b/resources/lib/youtube_plugin/youtube/provider.py index bfbbbd50d..be056ebae 100644 --- a/resources/lib/youtube_plugin/youtube/provider.py +++ b/resources/lib/youtube_plugin/youtube/provider.py @@ -1424,21 +1424,43 @@ def on_bookmarks(provider, context, re_match): 'items': [] } - def _update_bookmark(_id, timestamp): + def _update_bookmark(context, _id, old_item): def _update(new_item): - new_item.set_bookmark_timestamp(timestamp) - bookmarks_list.update_item(_id, repr(new_item), timestamp) + if isinstance(old_item, float): + bookmark_timestamp = old_item + elif isinstance(old_item, BaseItem): + bookmark_timestamp = old_item.get_bookmark_timestamp() + else: + return + + if new_item.available: + new_item.bookmark_id = _id + new_item.set_bookmark_timestamp(bookmark_timestamp) + new_item.callback = None + bookmarks_list.update_item( + _id, + repr(new_item), + bookmark_timestamp, + ) + else: + new_item.__dict__.update(old_item.__dict__) + new_item.bookmark_id = _id + new_item.set_bookmark_timestamp(bookmark_timestamp) + new_item.available = False + new_item.playable = False + new_item.set_title(context.get_ui().color( + 'AA808080', new_item.get_title() + )) return _update for item_id, item in items.items(): + callback = _update_bookmark(context, item_id, item) if isinstance(item, float): kind = 'youtube#channel' yt_id = item_id - callback = _update_bookmark(item_id, item) partial = True elif isinstance(item, BaseItem): - callback = None partial = False if isinstance(item, VideoItem): @@ -1452,7 +1474,9 @@ def _update(new_item): kind = 'youtube#channel' yt_id = getattr(item, 'channel_id', None) else: + kind = None yt_id = None + partial = False if not yt_id: if isinstance(item, BaseItem): @@ -1467,10 +1491,6 @@ def _update(new_item): continue kind = 'youtube#' + kind partial = True - callback = _update_bookmark( - item_id, - item.get_bookmark_timestamp(), - ) break else: if to_delete: