diff --git a/addon.xml b/addon.xml index 78c493bb4..90191fc5e 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/changelog.txt b/changelog.txt index afeecd5a5..eb13564cd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -## v7.1.1.1 +## v7.1.1.3 ### Fixed - Fix http server not listening on any interface if listen IP is 0.0.0.0 #927 - Standardise return type of LoginClient.refresh_token #932 @@ -33,6 +33,13 @@ - Attempt to fix possible deadlock on http server shutdown - Fix potential infinite loop with old data from access_manager.json #980 - Reduce unnecessary window navigation fallback attempts +- Fix incorrect duration display in Kodi 18 and Kodi 19 #999 +- Fix possible error with getting names of bookmarks that have not been updated with info +- Fix missing "Live" label in Kodi 18 +- Fix uncaught exception when sign-in does not succeed #985 +- Fix missing "Ask" translation string +- Fix incorrect parameter name breaking auto-remove from Watch Later #993 +- Fix processing of "q" and "channelId" search query params #1004 ### Changed - Improve display and update of bookmarks @@ -49,6 +56,8 @@ - path parameters used for folders and sub-folders - query parameters used for changing display modes, filtering, sorting and inputs - Don't retry server wakeup on error unless settings change +- Allow ISA and MPD in 720p performance preset in Setup Wizard +- Only use OAuth tokens if necessary #994 #996 ### New - Explicitly enable TCP keep alive #913 diff --git a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py index c13a9dbb7..fc4ea0b8a 100644 --- a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py @@ -62,7 +62,6 @@ class XbmcContext(AbstractContext): 'api.personal.failed': 30599, 'api.secret': 30203, 'are_you_sure': 750, - 'ask': 863, 'bookmark': 30101, 'bookmark.channel': 30803, 'bookmark.created': 21362, @@ -124,7 +123,7 @@ class XbmcContext(AbstractContext): 'isa.enable.check': 30579, 'key.requirement': 30731, 'liked.video': 30716, - 'live': 839, + 'live': 19664, 'live.completed': 30647, 'live.upcoming': 30646, 'maintenance.bookmarks': 30800, @@ -190,6 +189,7 @@ class XbmcContext(AbstractContext): 'search.sort.title': 369, 'search.sort.viewCount': 30767, 'search.title': 137, + 'select': 424, 'select.listen.ip': 30644, 'select_video_quality': 30010, 'settings': 10004, diff --git a/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py b/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py index 44ab20b2b..ea6375273 100644 --- a/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py +++ b/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py @@ -113,7 +113,7 @@ def set_info(list_item, item, properties, set_play_count=True, resume=True): list_item.addStreamInfo(info_type, {'duration': duration}) if duration is not None: - info_labels['duration'] = value + info_labels['duration'] = duration elif isinstance(item, DirectoryItem): info_type = 'video' diff --git a/resources/lib/youtube_plugin/kodion/json_store/access_manager.py b/resources/lib/youtube_plugin/kodion/json_store/access_manager.py index a6d8b62d5..e6e9a4b83 100644 --- a/resources/lib/youtube_plugin/kodion/json_store/access_manager.py +++ b/resources/lib/youtube_plugin/kodion/json_store/access_manager.py @@ -456,11 +456,12 @@ def update_access_token(self, } if expiry is not None: - details['token_expires'] = time.time() + ( - min(map(int, [val for val in expiry if val])) - if isinstance(expiry, (list, tuple)) else - int(expiry) - ) + if isinstance(expiry, (list, tuple)): + expiry = [val for val in expiry if val] + expiry = min(map(int, expiry)) if expiry else -1 + else: + expiry = int(expiry) + details['token_expires'] = time.time() + expiry if refresh_token is not None: details['refresh_token'] = ( diff --git a/resources/lib/youtube_plugin/kodion/monitors/player_monitor.py b/resources/lib/youtube_plugin/kodion/monitors/player_monitor.py index 89c208cc4..2ca9e656a 100644 --- a/resources/lib/youtube_plugin/kodion/monitors/player_monitor.py +++ b/resources/lib/youtube_plugin/kodion/monitors/player_monitor.py @@ -244,7 +244,7 @@ def run(self): category='video', playlist_id=watch_later_id, video_id=playlist_item_id, - item_name='', + video_name='', confirmed=True, ) else: diff --git a/resources/lib/youtube_plugin/kodion/script_actions.py b/resources/lib/youtube_plugin/kodion/script_actions.py index 21ab91556..93531a968 100644 --- a/resources/lib/youtube_plugin/kodion/script_actions.py +++ b/resources/lib/youtube_plugin/kodion/script_actions.py @@ -67,7 +67,7 @@ def _config_actions(context, action, *_args): sub_opts = [ localize('none'), - localize('ask'), + localize('select'), localize('subtitles.with_fallback') % (preferred, fallback), preferred, '%s (%s)' % (preferred, localize('subtitles.no_asr')), diff --git a/resources/lib/youtube_plugin/youtube/client/youtube.py b/resources/lib/youtube_plugin/youtube/client/youtube.py index 4ab1dc2de..dafbaa44a 100644 --- a/resources/lib/youtube_plugin/youtube/client/youtube.py +++ b/resources/lib/youtube_plugin/youtube/client/youtube.py @@ -1430,7 +1430,7 @@ def search_with_params(self, params, **kwargs): 'relevanceLanguage': self._language, } - search_query = params.get('q') + search_query = params.get('q', '') if '|' in search_query: search_params['q'] = search_query.replace('|', '%7C') @@ -1444,9 +1444,8 @@ def search_with_params(self, params, **kwargs): channel_id = params.get('channelId') if channel_id == 'mine': + del params['channelId'] params['forMine'] = True - else: - params['channelId'] = channel_id location = params.get('location') if location is True: diff --git a/resources/lib/youtube_plugin/youtube/helper/stream_info.py b/resources/lib/youtube_plugin/youtube/helper/stream_info.py index 50044a8e2..e44833366 100644 --- a/resources/lib/youtube_plugin/youtube/helper/stream_info.py +++ b/resources/lib/youtube_plugin/youtube/helper/stream_info.py @@ -1380,9 +1380,11 @@ def load_stream_info(self, video_id): 'country', 'not available', } - skip_reasons = { + reauth_reasons = { 'age', 'inappropriate', + } + skip_reasons = { 'latest version', } retry_reasons = { @@ -1393,11 +1395,8 @@ def load_stream_info(self, video_id): abort = False client_data = {'json': {'videoId': video_id}} - if self._access_token: - auth = True - client_data['_access_token'] = self._access_token - else: - auth = False + access_token = self._access_token + auth = False for name, clients in self._client_groups.items(): if not clients: @@ -1409,81 +1408,95 @@ def load_stream_info(self, video_id): status = None - for client_name in clients: - _client = self.build_client(client_name, client_data) - if not _client: - continue - - _result = self.request( - video_info_url, - 'POST', - response_hook=self._response_hook_json, - error_title='Player request failed', - error_hook=self._error_hook, - error_hook_kwargs={ - 'video_id': video_id, - 'client': client_name, - 'auth': bool(_client.get('_access_token')), - }, - **_client - ) or {} - - video_details = _result.get('videoDetails', {}) - playability = _result.get('playabilityStatus', {}) - status = playability.get('status', 'ERROR').upper() - reason = playability.get('reason', 'UNKNOWN') - - if video_details and video_id != video_details.get('videoId'): - status = 'CONTENT_NOT_AVAILABLE_IN_THIS_APP' - reason = 'Watch on the latest version of YouTube' - - if (age_gate_enabled - and playability.get('desktopLegacyAgeGateReason')): - abort = True - break - elif status == 'LIVE_STREAM_OFFLINE': - abort = True - break - elif status == 'OK': - break - elif status in { - 'AGE_CHECK_REQUIRED', - 'AGE_VERIFICATION_REQUIRED', - 'CONTENT_CHECK_REQUIRED', - 'LOGIN_REQUIRED', - 'CONTENT_NOT_AVAILABLE_IN_THIS_APP', - 'ERROR', - 'UNPLAYABLE', - }: - log_warning( - 'Failed to retrieve video info' - '\n\tStatus: {status}' - '\n\tReason: {reason}' - '\n\tvideo_id: |{video_id}|' - '\n\tClient: |{client}|' - '\n\tAuth: |{auth}|' - .format( - status=status, - reason=reason or 'UNKNOWN', - video_id=video_id, - client=_client['_name'], - auth=auth, - ) - ) - compare_reason = reason.lower() - if any(why in compare_reason for why in retry_reasons): + restart = False + while 1: + for client_name in clients: + _client = self.build_client(client_name, client_data) + if not _client: continue - if any(why in compare_reason for why in skip_reasons): + + _result = self.request( + video_info_url, + 'POST', + response_hook=self._response_hook_json, + error_title='Player request failed', + error_hook=self._error_hook, + error_hook_kwargs={ + 'video_id': video_id, + 'client': client_name, + 'auth': bool(_client.get('_access_token')), + }, + **_client + ) or {} + + video_details = _result.get('videoDetails', {}) + playability = _result.get('playabilityStatus', {}) + status = playability.get('status', 'ERROR').upper() + reason = playability.get('reason', 'UNKNOWN') + + if (video_details + and video_id != video_details.get('videoId')): + status = 'CONTENT_NOT_AVAILABLE_IN_THIS_APP' + reason = 'Watch on the latest version of YouTube' + + if (age_gate_enabled + and playability.get('desktopLegacyAgeGateReason')): + abort = True break - if any(why in compare_reason for why in abort_reasons): + elif status == 'LIVE_STREAM_OFFLINE': abort = True break + elif status == 'OK': + break + elif status in { + 'AGE_CHECK_REQUIRED', + 'AGE_VERIFICATION_REQUIRED', + 'CONTENT_CHECK_REQUIRED', + 'LOGIN_REQUIRED', + 'CONTENT_NOT_AVAILABLE_IN_THIS_APP', + 'ERROR', + 'UNPLAYABLE', + }: + log_warning( + 'Failed to retrieve video info' + '\n\tStatus: {status}' + '\n\tReason: {reason}' + '\n\tvideo_id: |{video_id}|' + '\n\tClient: |{client}|' + '\n\tAuth: |{auth}|' + .format( + status=status, + reason=reason or 'UNKNOWN', + video_id=video_id, + client=_client['_name'], + auth=auth, + ) + ) + compare_reason = reason.lower() + if any(why in compare_reason for why in reauth_reasons): + if access_token and not auth: + auth = True + client_data['_access_token'] = access_token + restart = True + break + if any(why in compare_reason for why in retry_reasons): + continue + if any(why in compare_reason for why in skip_reasons): + break + if any(why in compare_reason for why in abort_reasons): + abort = True + break + else: + log_debug( + 'Unknown playabilityStatus in player response' + '\n\tplayabilityStatus: {0}' + .format(playability) + ) else: - log_debug( - 'Unknown playabilityStatus in player response' - '\n\tplayabilityStatus: {0}' - .format(playability) - ) + break + if not restart: + break + restart = False if abort: break @@ -1624,7 +1637,7 @@ def load_stream_info(self, video_id): '', '', )) + '||R{{SSM}}|R', - 'token': self._access_token, + 'token': access_token, } break else: diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_login.py b/resources/lib/youtube_plugin/youtube/helper/yt_login.py index f495b993a..3d6ba7d18 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_login.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_login.py @@ -121,7 +121,7 @@ def _do_login(token_type): tokens = ['tv', 'personal'] for token_type, token in enumerate(tokens): - new_token = _do_login(token_type) or (None, 0, None) + new_token = _do_login(token_type) or ('', -1, '') tokens[token_type] = new_token context.log_debug('YouTube Login:' diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py b/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py index 6b211459b..d09ddfdb2 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py @@ -123,11 +123,6 @@ def process_performance_settings(context, step, steps, **_kwargs): 'max_resolution': 3, # 720p 'stream_features': ('avc1', 'mp4a', 'filter'), 'num_items': 10, - 'settings': ( - (settings.use_isa, (False,)), - (settings.use_mpd_videos, (False,)), - (settings.set_subtitle_download, (True,)), - ), }, '1080p30_avc': { 'max_resolution': 4, # 1080p diff --git a/resources/lib/youtube_plugin/youtube/provider.py b/resources/lib/youtube_plugin/youtube/provider.py index a19cb3590..3ae559bf3 100644 --- a/resources/lib/youtube_plugin/youtube/provider.py +++ b/resources/lib/youtube_plugin/youtube/provider.py @@ -1630,6 +1630,7 @@ def _update(new_item): if isinstance(item, float): kind = 'youtube#channel' yt_id = item_id + item_name = '' partial = True elif isinstance(item, BaseItem): partial = False @@ -1644,9 +1645,11 @@ def _update(new_item): else: kind = 'youtube#channel' yt_id = getattr(item, 'channel_id', None) + item_name = item.get_name() else: kind = None yt_id = None + item_name = '' partial = False if not yt_id: @@ -1677,7 +1680,7 @@ def _update(new_item): '_context_menu': { 'context_menu': ( menu_items.bookmark_remove( - context, item_id, item.get_name() + context, item_id, item_name ), menu_items.bookmarks_clear( context diff --git a/resources/settings.xml b/resources/settings.xml index 607bcd33d..ea33b17cb 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -242,7 +242,7 @@ - + @@ -529,7 +529,7 @@ true - + 0 true