From f78599744f95ef1b91523f16f1e0df48ce02bf84 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Tue, 16 Apr 2024 22:24:25 +1000 Subject: [PATCH 01/16] Make "Support alternative player" and "Use YouTube website urls with default player" mutually exclusive options --- .../kodion/items/xbmc/xbmc_items.py | 6 +++-- .../kodion/settings/abstract_settings.py | 5 ++--- .../youtube_plugin/youtube/helper/yt_play.py | 2 +- resources/settings.xml | 22 ++++++++++++------- 4 files changed, 21 insertions(+), 14 deletions(-) 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 1ee9b860e..aff4f4803 100644 --- a/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py +++ b/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py @@ -381,8 +381,10 @@ def video_playback_item(context, video_item, show_fanart=None): mime_type = uri.split('mime=', 1)[1].split('&', 1)[0] mime_type = mime_type.replace('%2F', '/') - if (not settings.support_alternative_player() - and headers and uri.startswith('http')): + if (headers and uri.startswith('http') and not ( + settings.default_player_web_urls() + or (is_external and settings.alternative_player_web_urls()) + )): kwargs['path'] = '|'.join((uri, headers)) list_item = xbmcgui.ListItem(**kwargs) diff --git a/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py b/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py index b73e21ae8..38d376e32 100644 --- a/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py +++ b/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py @@ -112,14 +112,13 @@ def default_player_web_urls(self, value=None): if value is not None: return self.set_bool(settings.DEFAULT_PLAYER_WEB_URLS, value) if self.support_alternative_player(): - return self.get_bool(settings.DEFAULT_PLAYER_WEB_URLS, False) - return False + return False + return self.get_bool(settings.DEFAULT_PLAYER_WEB_URLS, False) def alternative_player_web_urls(self, value=None): if value is not None: return self.set_bool(settings.ALTERNATIVE_PLAYER_WEB_URLS, value) if (self.support_alternative_player() - and not self.default_player_web_urls() and not self.alternative_player_adaptive()): return self.get_bool(settings.ALTERNATIVE_PLAYER_WEB_URLS, False) return False diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_play.py b/resources/lib/youtube_plugin/youtube/helper/yt_play.py index 13e944752..f7fe89fa3 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_play.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_play.py @@ -40,7 +40,7 @@ def play_video(provider, context): is_external = ui.get_property(SWITCH_PLAYER_FLAG) if ((is_external and settings.alternative_player_web_urls()) - or (not is_external and settings.default_player_web_urls())): + or settings.default_player_web_urls()): video_stream = { 'url': 'https://www.youtube.com/watch?v={0}'.format(video_id), } diff --git a/resources/settings.xml b/resources/settings.xml index 72c6237f4..c2a28f30d 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -573,16 +573,14 @@ - 0 - false - - - 0 false - true + + true + false + @@ -595,7 +593,6 @@ true false - false @@ -610,7 +607,6 @@ true true - true false @@ -618,6 +614,16 @@ + + 0 + false + + + false + + + + From 762a2d38e8bbe5f2c56e0b994961e9dcd2448839 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Wed, 17 Apr 2024 00:04:54 +1000 Subject: [PATCH 02/16] Further fixes for multiple busy dialog crash workaround # Possibly fix issues reported in comments of #704 --- .../kodion/constants/__init__.py | 2 + .../kodion/player/xbmc/xbmc_playlist.py | 5 ++- .../kodion/plugin/xbmc/xbmc_plugin.py | 38 +++++++++---------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/constants/__init__.py b/resources/lib/youtube_plugin/kodion/constants/__init__.py index 8ab679579..cdc7218d4 100644 --- a/resources/lib/youtube_plugin/kodion/constants/__init__.py +++ b/resources/lib/youtube_plugin/kodion/constants/__init__.py @@ -34,6 +34,7 @@ BUSY_FLAG = 'busy' SWITCH_PLAYER_FLAG = 'switch_player' +PLAYLIST_POSITION = 'playlist_position' WAIT_FLAG = 'builtin_running' __all__ = ( @@ -42,6 +43,7 @@ 'BUSY_FLAG', 'DATA_PATH', 'MEDIA_PATH', + 'PLAYLIST_POSITION', 'RESOURCE_PATH', 'SWITCH_PLAYER_FLAG', 'TEMP_PATH', diff --git a/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist.py b/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist.py index 9b66c6ae2..f35d7369e 100644 --- a/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist.py +++ b/resources/lib/youtube_plugin/kodion/player/xbmc/xbmc_playlist.py @@ -171,12 +171,15 @@ def play_playlist_item(self, position, resume=False): context.log_debug('Playing from playlist position: {0}' .format(position)) + if not resume: + xbmc.Player().play(self._playlist, startpos=position - 1) + return # JSON Player.Open can be too slow but is needed if resuming is enabled jsonrpc(method='Player.Open', params={'item': {'playlistid': self._playlist.getPlayListId(), # Convert 1 indexed to 0 indexed position 'position': position - 1}}, - options={'resume': resume}, + options={'resume': True}, no_response=True) def get_position(self, offset=0): 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 b70cd2d0a..16c3feb6c 100644 --- a/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py +++ b/resources/lib/youtube_plugin/kodion/plugin/xbmc/xbmc_plugin.py @@ -13,7 +13,7 @@ from traceback import format_stack from ..abstract_plugin import AbstractPlugin -from ...constants import BUSY_FLAG +from ...constants import BUSY_FLAG, PLAYLIST_POSITION from ...compatibility import xbmcplugin from ...exceptions import KodionException from ...items import ( @@ -42,27 +42,22 @@ def run(self, provider, context): ui = context.get_ui() if ui.get_property(BUSY_FLAG).lower() == 'true': - ui.clear_property(BUSY_FLAG) if ui.busy_dialog_active(): - playlist = XbmcPlaylist('auto', context, retry=3) - playlist.clear() xbmcplugin.endOfDirectory( self.handle, succeeded=False, updateListing=True, ) + playlist = XbmcPlaylist('auto', context, retry=3) + position, remaining = playlist.get_position() + items = playlist.get_items() if remaining else None + playlist.clear() + context.log_warning('Multiple busy dialogs active - ' 'playlist cleared to avoid Kodi crash') - num_items = 0 - items = ui.get_property('playlist') - position = ui.get_property('position') - - if position and items: - position = int(position) - ui.clear_property('playlist') - + if items: max_wait_time = 30 while ui.busy_dialog_active(): max_wait_time -= 1 @@ -74,10 +69,12 @@ def run(self, provider, context): context.log_warning('Multiple busy dialogs active - ' 'reloading playlist') - num_items = playlist.add_items(items, loads=True) + num_items = playlist.add_items(items) + + old_position = ui.get_property(PLAYLIST_POSITION) + if old_position and position == int(old_position): + position += 1 - if position and num_items: - position += 1 max_wait_time = min(position, num_items) while ui.busy_dialog_active() or playlist.size() < position: max_wait_time -= 1 @@ -89,8 +86,13 @@ def run(self, provider, context): else: playlist.play_playlist_item(position) + ui.clear_property(BUSY_FLAG) + ui.clear_property(PLAYLIST_POSITION) return False + ui.clear_property(BUSY_FLAG) + ui.clear_property(PLAYLIST_POSITION) + if settings.is_setup_wizard_enabled(): provider.run_wizard(context) @@ -169,10 +171,8 @@ def _set_resolved_url(self, context, base_item, show_fanart): if not context.is_plugin_path(uri) and ui.busy_dialog_active(): ui.set_property(BUSY_FLAG, 'true') playlist = XbmcPlaylist('auto', context) - position, remaining = playlist.get_position() - if remaining: - ui.set_property('playlist', playlist.get_items(dumps=True)) - ui.set_property('position', str(position)) + position, _ = playlist.get_position() + ui.set_property(PLAYLIST_POSITION, str(position)) item = playback_item(context, base_item, show_fanart) xbmcplugin.setResolvedUrl(self.handle, From 5ab565d4295c84accc0f70a225f04bfbd2e47c02 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Wed, 17 Apr 2024 01:18:29 +1000 Subject: [PATCH 03/16] Update error checks to avoid unnecessary retries of player request --- .../youtube/helper/video_info.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/resources/lib/youtube_plugin/youtube/helper/video_info.py b/resources/lib/youtube_plugin/youtube/helper/video_info.py index 8f2aea14d..0348a2c4d 100644 --- a/resources/lib/youtube_plugin/youtube/helper/video_info.py +++ b/resources/lib/youtube_plugin/youtube/helper/video_info.py @@ -1140,6 +1140,11 @@ def _get_video_info(self): client_name = reason = status = None client = playability_status = result = None + reasons = ( + self._context.localize(574, 'country').lower(), + self._context.localize(10005, 'not available').lower(), + ) + client_data = {'json': {'videoId': video_id}} if self._access_token: client_data['_access_token'] = self._access_token @@ -1188,14 +1193,18 @@ def _get_video_info(self): # Geo-blocked video with error reasons like: # "This video contains content from XXX, who has blocked it in your country on copyright grounds" # "The uploader has not made this video available in your country" - if status == 'UNPLAYABLE' and 'country' in reason: + # Reason language will vary based on Accept-Language and + # client hl, Kodi localised language is used for comparison + # but may not match reason language + if (status == 'UNPLAYABLE' + and any(why in reason for why in reasons)): break if status != 'ERROR': continue # This is used to check for error like: # "The following content is not available on this app." - # Text will vary depending on Accept-Language and client hl - # YouTube support url is checked instead + # Reason language will vary based on Accept-Language and + # client hl, so YouTube support url is checked instead url = self._get_error_details( playability_status, details=( @@ -1211,9 +1220,9 @@ def _get_video_info(self): if url and url.startswith('//support.google.com/youtube/answer/12318250'): status = 'CONTENT_NOT_AVAILABLE_IN_THIS_APP' continue - if video_id != video_details.get('videoId'): + if video_details and video_id != video_details.get('videoId'): status = 'CONTENT_NOT_AVAILABLE_IN_THIS_APP' - reason = 'WATCH_ON_LATEST_VERSION_OF_YOUTUBE' + reason = 'Watch on the latest version of YouTube' continue break # Only attempt to remove Authorization header if clients iterable From b8d0e557b06318c191e802f488103078aab03ba4 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Wed, 17 Apr 2024 01:35:58 +1000 Subject: [PATCH 04/16] Version bump v7.0.6+beta.2 --- addon.xml | 2 +- changelog.txt | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/addon.xml b/addon.xml index 6718c251a..d0c45af60 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/changelog.txt b/changelog.txt index b88bf0be0..3b3b14cca 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,12 @@ +## v7.0.6+beta.2 +### Fixed +- Further fixes for multiple busy dialog crash workaround + - Possibly fix issues reported in comments of #704 +- Update error checks to avoid unnecessary retries of player request + +### Changed +- Make "Support alternative player" and "Use YouTube website urls with default player" mutually exclusive options + ## v7.0.6+beta.1 ### Fixed - Improve updating containers and (re)loading windows #681 @@ -7,8 +16,7 @@ ### Changed - Setup Wizard default settings now disable pre-downloading subtitles if MPEG-DASH is enabled -- Ask for quality, Play with subtitles, Play audio only context menu items only displayed if - associated setting is not already enabled +- Ask for quality, Play with subtitles, Play audio only context menu items only displayed if associated setting is not already enabled - Only make extra request for subtitles for Android client when all subtitle languages are enabled - Overhaul alternative player support due to Kodi changes and also improve external player support From 11aa5a6985d15d9667dd8434153dcf6eea20797c Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:05:04 +1000 Subject: [PATCH 05/16] Remove deprecated ISA manifest_type property in Kodi v21+ --- resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 aff4f4803..0d1f6ed8e 100644 --- a/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py +++ b/resources/lib/youtube_plugin/kodion/items/xbmc/xbmc_items.py @@ -366,7 +366,9 @@ def video_playback_item(context, video_item, show_fanart=None): if current_system_version.compatible(19, 0) else 'inputstreamaddon') props[inputstream_property] = 'inputstream.adaptive' - props['inputstream.adaptive.manifest_type'] = manifest_type + + if not current_system_version.compatible(21, 0): + props['inputstream.adaptive.manifest_type'] = manifest_type if headers: props['inputstream.adaptive.manifest_headers'] = headers From 9c9e448a8cbca7273b4836070877dd3a34e07520 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Thu, 18 Apr 2024 02:37:34 +1000 Subject: [PATCH 06/16] Re-enable Opus audio by default, but only use it with Kodi v21+ - Closes #537 - Underlying issue fixed by xbmc/xbmc#24748 --- .../kodion/context/xbmc/xbmc_context.py | 4 ++-- .../youtube/helper/yt_setup_wizard.py | 14 +++++++------- resources/settings.xml | 3 +-- 3 files changed, 10 insertions(+), 11 deletions(-) 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 ea3403aba..d22419e23 100644 --- a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py @@ -610,8 +610,8 @@ def use_inputstream_adaptive(self): 'ttml': loose_version('20.0.0'), # audio codecs 'vorbis': loose_version('2.3.14'), - # unknown when Opus audio support was implemented - 'opus': loose_version('19.0.0'), + # Opus audio enabled in Kodi v21+ which fixes stalls after seek + 'opus': loose_version('21.0.0'), 'mp4a': True, 'ac-3': loose_version('2.1.15'), 'ec-3': loose_version('2.1.15'), 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 a7fbe306e..77422f836 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py @@ -373,7 +373,7 @@ def process_performance_settings(_provider, context, step, steps): }, '1080p30_avc': { 'max_resolution': 4, # 1080p - 'stream_features': ('avc1', 'vorbis', 'mp4a', 'filter'), + 'stream_features': ('avc1', 'vorbis', 'opus', 'mp4a', 'filter'), 'num_items': 10, 'settings': ( (settings.client_selection, (2,)), @@ -381,32 +381,32 @@ def process_performance_settings(_provider, context, step, steps): }, '1080p30': { 'max_resolution': 4, # 1080p - 'stream_features': ('avc1', 'vp9', 'vorbis', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), + 'stream_features': ('avc1', 'vp9', 'vorbis', 'opus', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), 'num_items': 20, }, '1080p60': { 'max_resolution': 4, # 1080p - 'stream_features': ('avc1', 'vp9', 'hfr', 'vorbis', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), + 'stream_features': ('avc1', 'vp9', 'hfr', 'vorbis', 'opus', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), 'num_items': 30, }, '4k30': { 'max_resolution': 6, # 4k - 'stream_features': ('avc1', 'vp9', 'hdr', 'hfr', 'no_hfr_max', 'vorbis', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), + 'stream_features': ('avc1', 'vp9', 'hdr', 'hfr', 'no_hfr_max', 'vorbis', 'opus', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), 'num_items': 50, }, '4k60': { 'max_resolution': 6, # 4k - 'stream_features': ('avc1', 'vp9', 'hdr', 'hfr', 'vorbis', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), + 'stream_features': ('avc1', 'vp9', 'hdr', 'hfr', 'vorbis', 'opus', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), 'num_items': 50, }, '4k60_av1': { 'max_resolution': 6, # 4k - 'stream_features': ('avc1', 'vp9', 'av01', 'hdr', 'hfr', 'vorbis', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), + 'stream_features': ('avc1', 'vp9', 'av01', 'hdr', 'hfr', 'vorbis', 'opus', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), 'num_items': 50, }, 'max': { 'max_resolution': 7, # 8k - 'stream_features': ('avc1', 'vp9', 'av01', 'hdr', 'hfr', 'vorbis', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), + 'stream_features': ('avc1', 'vp9', 'av01', 'hdr', 'hfr', 'vorbis', 'opus', 'mp4a', 'ssa', 'ac-3', 'ec-3', 'dts', 'filter'), 'num_items': 50, }, } diff --git a/resources/settings.xml b/resources/settings.xml index c2a28f30d..8c250954a 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -115,12 +115,11 @@ 0 - avc1,vp9,av01,hdr,hfr,vorbis,mp4a,ssa,ac-3,ec-3,dts,filter + avc1,vp9,av01,hdr,hfr,vorbis,opus,mp4a,ssa,ac-3,ec-3,dts,filter From 71cc472680a6bb3c90f7aadd3718e0a2f21f6829 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:09:02 +1000 Subject: [PATCH 07/16] Prefer MPEG-DASH for live streams - Close #680 --- resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py | 2 +- resources/settings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 77422f836..7aa362f3e 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_setup_wizard.py @@ -322,7 +322,7 @@ def process_default_settings(_provider, context, step, steps): settings.use_mpd_videos(True) settings.stream_select(4 if settings.ask_for_video_quality() else 3) settings.set_subtitle_download(False) - settings.live_stream_type(2) + settings.live_stream_type(3) if not xbmcvfs.exists('special://profile/playercorefactory.xml'): settings.default_player_web_urls(False) if settings.cache_size() < 20: diff --git a/resources/settings.xml b/resources/settings.xml index 8c250954a..fad839fd0 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -665,7 +665,7 @@ 0 - 2 + 3 From afe236cc9591613a96c18ec97f1c3cacb50a7384 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:14:21 +1000 Subject: [PATCH 08/16] Bump kodion.setup_wizard.forced_runs min value required - Use forced Setup Wizard to update changes in default settings --- .../lib/youtube_plugin/kodion/settings/abstract_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py b/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py index 38d376e32..7c825657a 100644 --- a/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py +++ b/resources/lib/youtube_plugin/kodion/settings/abstract_settings.py @@ -96,7 +96,7 @@ def get_search_history_size(self): def is_setup_wizard_enabled(self): # Increment min_required on new release to enable oneshot on first run - min_required = 2 + min_required = 3 forced_runs = self.get_int(settings.SETUP_WIZARD_RUNS, min_required - 1) if forced_runs < min_required: self.set_int(settings.SETUP_WIZARD_RUNS, min_required) From 7796ddc8dc102fea710ecac254579585614a3f7d Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:57:30 +1000 Subject: [PATCH 09/16] Update conditions used enable http server after settings change - Align with initial conditions checked during ServiceMonitor.__init__ after 0d4afbe --- .../lib/youtube_plugin/kodion/monitors/service_monitor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py b/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py index d11efb1f6..a97df29eb 100644 --- a/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py +++ b/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py @@ -85,7 +85,9 @@ def onSettingsChanged(self): 'plugin://{0}/'.format(ADDON_ID))): xbmc.executebuiltin('Container.Refresh') - use_httpd = settings.use_isa() or settings.api_config_page() + use_httpd = (settings.use_isa() + or settings.api_config_page() + or settings.support_alternative_player()) address, port = get_connect_address() whitelist = settings.httpd_whitelist() From a8827d830342c5ad2a401d2c9557f3cd75f54e76 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Fri, 19 Apr 2024 06:57:26 +1000 Subject: [PATCH 10/16] Shutdown http server on idle - Attempt to shutdown before network interface stops #699 - Can't use onScreensaverActivated/onScreensaverDeactivated as screensaver may not be enababled --- .../kodion/context/abstract_context.py | 4 +++ .../kodion/context/xbmc/xbmc_context.py | 4 +++ .../kodion/monitors/service_monitor.py | 3 ++ .../youtube_plugin/kodion/service_runner.py | 28 +++++++++++++------ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/context/abstract_context.py b/resources/lib/youtube_plugin/kodion/context/abstract_context.py index c5eaecf87..e7012c7fe 100644 --- a/resources/lib/youtube_plugin/kodion/context/abstract_context.py +++ b/resources/lib/youtube_plugin/kodion/context/abstract_context.py @@ -386,6 +386,10 @@ def execute(command): def sleep(timeout=None): raise NotImplementedError() + @staticmethod + def get_infobool(name): + raise NotImplementedError() + @staticmethod def get_infolabel(name): raise NotImplementedError() 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 d22419e23..48a3c1cdf 100644 --- a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py @@ -660,6 +660,10 @@ def inputstream_adaptive_auto_stream_selection(): def abort_requested(self): return self.get_ui().get_property('abort_requested').lower() == 'true' + @staticmethod + def get_infobool(name): + return xbmc.getCondVisibility(name) + @staticmethod def get_infolabel(name): return xbmc.getInfoLabel(name) diff --git a/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py b/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py index a97df29eb..bd0817701 100644 --- a/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py +++ b/resources/lib/youtube_plugin/kodion/monitors/service_monitor.py @@ -168,3 +168,6 @@ def restart_httpd(self): @staticmethod def ping_httpd(): return httpd_status() + + def httpd_required(self): + return self._use_httpd diff --git a/resources/lib/youtube_plugin/kodion/service_runner.py b/resources/lib/youtube_plugin/kodion/service_runner.py index f48e6c77d..447be67a8 100644 --- a/resources/lib/youtube_plugin/kodion/service_runner.py +++ b/resources/lib/youtube_plugin/kodion/service_runner.py @@ -10,7 +10,7 @@ from __future__ import absolute_import, division, unicode_literals -from .constants import TEMP_PATH +from .constants import ADDON_ID, TEMP_PATH from .context import XbmcContext from .monitors import PlayerMonitor, ServiceMonitor from .utils import rm_dir @@ -36,20 +36,30 @@ def run(): wait_interval = 10 ping_period = waited = 60 restart_attempts = 0 + plugin_url = 'plugin://{0}/'.format(ADDON_ID) while not monitor.abortRequested(): - if waited >= ping_period: + if not monitor.httpd: + if (monitor.httpd_required() + and not context.get_infobool('System.IdleTime(10)')): + monitor.start_httpd() + elif context.get_infobool('System.IdleTime(30)'): + monitor.shutdown_httpd() + elif waited >= ping_period: waited = 0 - - if monitor.httpd and not monitor.ping_httpd(): + if monitor.ping_httpd(): + restart_attempts = 0 + elif restart_attempts < 5: + monitor.restart_httpd() restart_attempts += 1 - if restart_attempts > 5: - monitor.shutdown_httpd() - restart_attempts = 0 - else: - monitor.restart_httpd() else: + monitor.shutdown_httpd() restart_attempts = 0 + if context.get_infolabel('Container.FolderPath').startswith(plugin_url): + wait_interval = 1 + else: + wait_interval = 10 + if monitor.waitForAbort(wait_interval): break waited += wait_interval From d5b3935485ceec79b9198788b3fac8134314d790 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Fri, 19 Apr 2024 08:08:03 +1000 Subject: [PATCH 11/16] Misc tidy ups --- .../kodion/context/xbmc/xbmc_context.py | 10 +++++----- resources/lib/youtube_plugin/kodion/plugin_runner.py | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) 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 48a3c1cdf..15866b644 100644 --- a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py @@ -370,14 +370,14 @@ def format_time(time_obj, str_format=None): @staticmethod def get_language(): language = xbmc.getLanguage(format=xbmc.ISO_639_1, region=True) - lang_code, seperator, region = language.partition('-') + lang_code, separator, region = language.partition('-') if not lang_code: language = xbmc.getLanguage(format=xbmc.ISO_639_2, region=False) - lang_code, seperator, region = language.partition('-') + lang_code, separator, region = language.partition('-') if not lang_code: return 'en-US' if region: - return seperator.join((lang_code.lower(), region.upper())) + return separator.join((lang_code.lower(), region.upper())) return lang_code def get_language_name(self, lang_id=None): @@ -549,7 +549,7 @@ def execute(self, command, wait=False, wait_for=None): @staticmethod def sleep(timeout=None): - wait(timeout) + return wait(timeout) def addon_enabled(self, addon_id): response = jsonrpc(method='Addons.GetAddonDetails', @@ -590,7 +590,7 @@ def use_inputstream_adaptive(self): if self.addon_enabled('inputstream.adaptive'): success = True elif self.get_ui().on_yes_no_input( - self.get_name(), self.localize('isa.enable.confirm') + self.get_name(), self.localize('isa.enable.confirm') ): success = self.set_addon_enabled('inputstream.adaptive') else: diff --git a/resources/lib/youtube_plugin/kodion/plugin_runner.py b/resources/lib/youtube_plugin/kodion/plugin_runner.py index 45a659881..44509c2b5 100644 --- a/resources/lib/youtube_plugin/kodion/plugin_runner.py +++ b/resources/lib/youtube_plugin/kodion/plugin_runner.py @@ -15,9 +15,12 @@ def run(provider, context=None): - from .compatibility import xbmc + if not context: + from .context import XbmcContext + + context = XbmcContext() - profiler = xbmc.getCondVisibility('System.GetBool(debug.showloginfo)') + profiler = context.get_infobool('System.GetBool(debug.showloginfo)') if profiler: from .debug import Profiler @@ -29,10 +32,6 @@ def run(provider, context=None): from .plugin import XbmcPlugin plugin = XbmcPlugin() - if not context: - from .context import XbmcContext - - context = XbmcContext() context.log_debug('Starting Kodion framework by bromix...') From 9f32064693f0bf54beeaf842c0503d9db0fd41b2 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:10:41 +1000 Subject: [PATCH 12/16] Update changelog --- changelog.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 3b3b14cca..bc4ccf12c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,10 +2,14 @@ ### Fixed - Further fixes for multiple busy dialog crash workaround - Possibly fix issues reported in comments of #704 -- Update error checks to avoid unnecessary retries of player request +- Update error checks to avoid unnecessary retries of player requests ### Changed -- Make "Support alternative player" and "Use YouTube website urls with default player" mutually exclusive options +- Make "Support alternative player" and "Use YouTube website urls with default player" mutually exclusive options #692 +- Enable OPUS audio by default #537 +- Enable MPEG-DASH for live streams by default #680 +- Setup Wizard will prompt to run when plugin first opens after updating, in order to update default settings +- http server will shutdown on idle timeout #699 ## v7.0.6+beta.1 ### Fixed From ab76a2eea97de621c20a8525c9a2aacd2514257e Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:31:41 +1000 Subject: [PATCH 13/16] Only use first two letters of fallback ISO_639_2 language code/region - With exception for fil -> fi - Just in case xbmc.getLanguage ever gets fixed or even more broken - Also tidy up related variable names --- .../kodion/context/xbmc/xbmc_context.py | 3 +++ .../youtube_plugin/kodion/script_actions.py | 10 +++++----- .../youtube/helper/subtitles.py | 20 ++++++++++--------- .../lib/youtube_plugin/youtube/provider.py | 8 ++++---- 4 files changed, 23 insertions(+), 18 deletions(-) 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 15866b644..eb5b9c099 100644 --- a/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py @@ -374,6 +374,9 @@ def get_language(): if not lang_code: language = xbmc.getLanguage(format=xbmc.ISO_639_2, region=False) lang_code, separator, region = language.partition('-') + if lang_code != 'fil': + lang_code = lang_code[:2] + region = region[:2] if not lang_code: return 'en-US' if region: diff --git a/resources/lib/youtube_plugin/kodion/script_actions.py b/resources/lib/youtube_plugin/kodion/script_actions.py index 9a85dfc00..f6db310f2 100644 --- a/resources/lib/youtube_plugin/kodion/script_actions.py +++ b/resources/lib/youtube_plugin/kodion/script_actions.py @@ -41,16 +41,16 @@ def _config_actions(context, action, *_args): xbmc.executebuiltin('InstallAddon(script.module.inputstreamhelper)') elif action == 'subtitles': - sub_lang = context.get_subtitle_language() + kodi_sub_lang = context.get_subtitle_language() plugin_lang = settings.get_language() sub_selection = settings.get_subtitle_selection() - if not sub_lang: + if not kodi_sub_lang: preferred = (plugin_lang,) - elif sub_lang.partition('-')[0] != plugin_lang.partition('-')[0]: - preferred = (sub_lang, plugin_lang) + elif kodi_sub_lang.partition('-')[0] != plugin_lang.partition('-')[0]: + preferred = (kodi_sub_lang, plugin_lang) else: - preferred = (sub_lang,) + preferred = (kodi_sub_lang,) fallback = ('ASR' if preferred[0].startswith('en') else context.get_language_name('en')) diff --git a/resources/lib/youtube_plugin/youtube/helper/subtitles.py b/resources/lib/youtube_plugin/youtube/helper/subtitles.py index 2ee3f8c69..a669705af 100644 --- a/resources/lib/youtube_plugin/youtube/helper/subtitles.py +++ b/resources/lib/youtube_plugin/youtube/helper/subtitles.py @@ -66,16 +66,18 @@ def __init__(self, context, video_id): else: self.FORMATS['_default'] = 'vtt' - sub_lang = context.get_subtitle_language() - ui_lang = settings.get_language() - if not sub_lang and ui_lang: - self.preferred_lang = (ui_lang,) - elif sub_lang: - if (ui_lang and - sub_lang.partition('-')[0] != ui_lang.partition('-')[0]): - self.preferred_lang = (sub_lang, ui_lang) + kodi_sub_lang = context.get_subtitle_language() + plugin_lang = settings.get_language() + if not kodi_sub_lang and plugin_lang: + self.preferred_lang = (plugin_lang,) + elif kodi_sub_lang: + if not plugin_lang: + self.preferred_lang = (kodi_sub_lang,) + elif (plugin_lang.partition('-')[0] + != kodi_sub_lang.partition('-')[0]): + self.preferred_lang = (kodi_sub_lang, plugin_lang) else: - self.preferred_lang = (sub_lang,) + self.preferred_lang = (kodi_sub_lang,) else: self.preferred_lang = ('en',) diff --git a/resources/lib/youtube_plugin/youtube/provider.py b/resources/lib/youtube_plugin/youtube/provider.py index c6781d20e..c461e2e66 100644 --- a/resources/lib/youtube_plugin/youtube/provider.py +++ b/resources/lib/youtube_plugin/youtube/provider.py @@ -129,8 +129,8 @@ def get_client(self, context): items_per_page = settings.items_per_page() - language = settings.get_language() - region = settings.get_region() + plugin_lang = settings.get_language() + plugin_region = settings.get_region() api_last_origin = access_manager.get_last_origin() @@ -220,8 +220,8 @@ def get_client(self, context): context.log_debug('Access token count: |%d| Refresh token count: |%d|' % (len(access_tokens), len(refresh_tokens))) client = YouTube(context=context, - language=language, - region=region, + language=plugin_lang, + region=plugin_region, items_per_page=items_per_page, config=dev_keys if dev_keys else youtube_config) From ca4d3478bbff84aed4ef868327768a67c2458a89 Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Sun, 21 Apr 2024 02:22:09 +1000 Subject: [PATCH 14/16] Fix using empty str for date related infolabels if value not set --- .../kodion/compatibility/__init__.py | 8 ++---- .../youtube_plugin/kodion/items/base_item.py | 26 +++++++++--------- .../youtube_plugin/kodion/items/video_item.py | 27 +++++++++---------- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/compatibility/__init__.py b/resources/lib/youtube_plugin/kodion/compatibility/__init__.py index d8cf14ef9..b63f5ea75 100644 --- a/resources/lib/youtube_plugin/kodion/compatibility/__init__.py +++ b/resources/lib/youtube_plugin/kodion/compatibility/__init__.py @@ -132,12 +132,8 @@ def to_str(value): # Kodi v20+ if hasattr(xbmcgui.ListItem, 'setDateTime'): def datetime_infolabel(datetime_obj): - if datetime_obj: - return datetime_obj.replace(microsecond=0, tzinfo=None).isoformat() - return '' + return datetime_obj.replace(microsecond=0, tzinfo=None).isoformat() # Compatibility shims for Kodi v18 and v19 else: def datetime_infolabel(datetime_obj): - if datetime_obj: - return datetime_obj.strftime('%d.%m.%Y') - return '' + return datetime_obj.strftime('%d.%m.%Y') diff --git a/resources/lib/youtube_plugin/kodion/items/base_item.py b/resources/lib/youtube_plugin/kodion/items/base_item.py index 32e862c3d..c40f77239 100644 --- a/resources/lib/youtube_plugin/kodion/items/base_item.py +++ b/resources/lib/youtube_plugin/kodion/items/base_item.py @@ -143,14 +143,13 @@ def set_date_from_datetime(self, date_time): self._date = date_time def get_date(self, as_text=False, short=False, as_info_label=False): - if not self._date: - return '' - if short: - return self._date.date().strftime('%x') - if as_text: - return self._date.strftime('%x %X') - if as_info_label: - return datetime_infolabel(self._date) + if self._date: + if as_info_label: + return datetime_infolabel(self._date) + if short: + return self._date.date().strftime('%x') + if as_text: + return self._date.strftime('%x %X') return self._date def set_dateadded(self, year, month, day, hour=0, minute=0, second=0): @@ -165,12 +164,11 @@ def set_dateadded_from_datetime(self, date_time): self._dateadded = date_time def get_dateadded(self, as_text=False, as_info_label=False): - if not self._dateadded: - return '' - if as_text: - return self._dateadded.strftime('%x %X') - if as_info_label: - return datetime_infolabel(self._date) + if self._dateadded: + if as_info_label: + return datetime_infolabel(self._dateadded) + if as_text: + return self._dateadded.strftime('%x %X') return self._dateadded def set_added_utc(self, date_time): diff --git a/resources/lib/youtube_plugin/kodion/items/video_item.py b/resources/lib/youtube_plugin/kodion/items/video_item.py index acfabd368..931b4a3f8 100644 --- a/resources/lib/youtube_plugin/kodion/items/video_item.py +++ b/resources/lib/youtube_plugin/kodion/items/video_item.py @@ -123,12 +123,11 @@ def set_premiered_from_datetime(self, date_time): self._premiered = date_time.date() def get_premiered(self, as_text=True, as_info_label=False): - if not self._premiered: - return '' - if as_info_label: - return self._premiered.isoformat() - if as_text: - return self._premiered.strftime('%x') + if self._premiered: + if as_info_label: + return self._premiered.isoformat() + if as_text: + return self._premiered.strftime('%x') return self._premiered def set_plot(self, plot): @@ -228,12 +227,11 @@ def set_aired_from_datetime(self, date_time): self._aired = date_time.date() def get_aired(self, as_text=True, as_info_label=False): - if not self._aired: - return '' - if as_info_label: - return self._aired.isoformat() - if as_text: - return self._aired.strftime('%x') + if self._aired: + if as_info_label: + return self._aired.isoformat() + if as_text: + return self._aired.strftime('%x') return self._aired def set_scheduled_start_utc(self, date_time): @@ -319,8 +317,9 @@ def set_last_played(self, last_played): self._last_played = last_played def get_last_played(self, as_info_label=False): - if as_info_label: - return datetime_infolabel(self._last_played) + if self._last_played: + if as_info_label: + return datetime_infolabel(self._last_played) return self._last_played def set_start_percent(self, start_percent): From 2b58a0136da9fcc45a508dd86b1e7a18c4a773cf Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Sun, 21 Apr 2024 02:25:53 +1000 Subject: [PATCH 15/16] Fix Python 2 JSONEncoder internally trying to join unicode values with str values --- resources/lib/youtube_plugin/kodion/items/base_item.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/lib/youtube_plugin/kodion/items/base_item.py b/resources/lib/youtube_plugin/kodion/items/base_item.py index c40f77239..32af94419 100644 --- a/resources/lib/youtube_plugin/kodion/items/base_item.py +++ b/resources/lib/youtube_plugin/kodion/items/base_item.py @@ -239,4 +239,7 @@ def encode(self, obj, nested=False): } else: output = obj - return output if nested else super(_Encoder, self).encode(output) + + if nested: + return to_str(output) + return super(_Encoder, self).encode(output) From 3df4000ad5362e4d1bd94d2fa3225069dc80cefe Mon Sep 17 00:00:00 2001 From: MoojMidge <56883549+MoojMidge@users.noreply.github.com> Date: Sun, 21 Apr 2024 15:44:58 +1000 Subject: [PATCH 16/16] Close stream that profiler stats are written to in finally statement - In case some other error causes the profiler to fail --- resources/lib/youtube_plugin/kodion/debug.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/lib/youtube_plugin/kodion/debug.py b/resources/lib/youtube_plugin/kodion/debug.py index 81002d9c8..72712c0df 100644 --- a/resources/lib/youtube_plugin/kodion/debug.py +++ b/resources/lib/youtube_plugin/kodion/debug.py @@ -206,11 +206,12 @@ def get_stats(self, flush=True, reuse=False): self._profiler, stream=output_stream ).strip_dirs().sort_stats('cumulative', 'time').print_stats(50) + output = output_stream.getvalue() # Occurs when no stats were able to be generated from profiler except TypeError: - pass - output = output_stream.getvalue() - output_stream.close() + output = 'Profiler: unable to generate stats' + finally: + output_stream.close() if reuse: # If stats are accumulating then enable existing/new profiler