diff --git a/addon.xml b/addon.xml index 61c0caac..c9876322 100644 --- a/addon.xml +++ b/addon.xml @@ -99,3 +99,4 @@ 此附加元件未由Google支持 + diff --git a/resources/lib/youtube_plugin/kodion/abstract_provider.py b/resources/lib/youtube_plugin/kodion/abstract_provider.py index 7069da60..f49e2e27 100644 --- a/resources/lib/youtube_plugin/kodion/abstract_provider.py +++ b/resources/lib/youtube_plugin/kodion/abstract_provider.py @@ -73,10 +73,29 @@ class AbstractProvider(object): self._dict_path[re_path] = method_name def _process_wizard(self, context): + def _setup_views(_context, _view): + view_manager = utils.ViewManager(_context) + if not view_manager.update_view_mode(_context.localize(self._local_map['kodion.wizard.view.%s' % _view]), + _view): + return + + _context.get_settings().set_bool(constants.setting.VIEW_OVERRIDE, True) + # start the setup wizard wizard_steps = [] if context.get_settings().is_setup_wizard_enabled(): context.get_settings().set_bool(constants.setting.SETUP_WIZARD, False) + if utils.ViewManager(context).has_supported_views(): + views = self.get_wizard_supported_views() + for view in views: + if view in utils.ViewManager.SUPPORTED_VIEWS: + wizard_steps.append((_setup_views, [context, view])) + else: + context.log_warning('[Setup-Wizard] Unsupported view "%s"' % view) + else: + skin_id = context.get_ui().get_skin_id() + context.log("ViewManager: Unknown skin id '%s'" % skin_id) + wizard_steps.extend(self.get_wizard_steps(context)) if wizard_steps and context.get_ui().on_yes_no_input(context.get_name(), @@ -279,7 +298,7 @@ class AbstractProvider(object): query = query.decode('utf-8') return self.on_search(query, context, re_match) else: - context.set_content_type(constants.content_type.FILES) + context.set_content_type(constants.content_type.VIDEOS) result = [] location = str(context.get_param('location', False)).lower() == 'true' diff --git a/resources/lib/youtube_plugin/kodion/constants/const_settings.py b/resources/lib/youtube_plugin/kodion/constants/const_settings.py index ead2715e..129ffb5b 100644 --- a/resources/lib/youtube_plugin/kodion/constants/const_settings.py +++ b/resources/lib/youtube_plugin/kodion/constants/const_settings.py @@ -35,6 +35,10 @@ HIDE_SHORT_VIDEOS = 'youtube.hide_shorts' # (bool) SUPPORT_ALTERNATIVE_PLAYER = 'kodion.support.alternative_player' # (bool) ALTERNATIVE_PLAYER_WEB_URLS = 'kodion.alternative_player.web.urls' # (bool) +VIEW_OVERRIDE = 'kodion.view.override' # (bool) +VIEW_DEFAULT = 'kodion.view.default' # (int) +VIEW_X = 'kodion.view.%s' # (int) + ALLOW_DEV_KEYS = 'youtube.allow.dev.keys' # (bool) DASH_VIDEOS = 'kodion.mpd.videos' # (bool) diff --git a/resources/lib/youtube_plugin/kodion/impl/abstract_context_ui.py b/resources/lib/youtube_plugin/kodion/impl/abstract_context_ui.py index 7ed3feff..46eabf26 100644 --- a/resources/lib/youtube_plugin/kodion/impl/abstract_context_ui.py +++ b/resources/lib/youtube_plugin/kodion/impl/abstract_context_ui.py @@ -16,6 +16,12 @@ class AbstractContextUI(object): def create_progress_dialog(self, heading, text=None, background=False): raise NotImplementedError() + def set_view_mode(self, view_mode): + raise NotImplementedError() + + def get_view_mode(self): + raise NotImplementedError() + def get_skin_id(self): raise NotImplementedError() diff --git a/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py b/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py index 6a2e2887..ff5764c1 100644 --- a/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py +++ b/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py @@ -90,6 +90,9 @@ class AbstractSettings(object): def is_setup_wizard_enabled(self): return self.get_bool(constants.setting.SETUP_WIZARD, False) + def is_override_view_enabled(self): + return self.get_bool(constants.setting.VIEW_OVERRIDE, False) + def is_support_alternative_player_enabled(self): return self.get_bool(constants.setting.SUPPORT_ALTERNATIVE_PLAYER, False) diff --git a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context.py b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context.py index dd000aa9..a7c74b9a 100644 --- a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context.py +++ b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context.py @@ -208,6 +208,7 @@ class XbmcContext(AbstractContext): def set_content_type(self, content_type): self.log_debug('Setting content-type: "%s" for "%s"' % (content_type, self.get_path())) xbmcplugin.setContent(self._plugin_handle, content_type) + self.get_ui().set_view_mode(content_type) def add_sort_method(self, *sort_methods): for sort_method in sort_methods: diff --git a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context_ui.py b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context_ui.py index 49c95074..6aece7b9 100644 --- a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context_ui.py +++ b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_context_ui.py @@ -9,6 +9,7 @@ """ from six import PY3 +from six import string_types import xbmc import xbmcgui @@ -35,6 +36,18 @@ class XbmcContextUI(AbstractContextUI): return XbmcProgressDialog(heading, text) + def set_view_mode(self, view_mode): + if isinstance(view_mode, string_types): + view_mode = self._context.get_settings().get_int(constants.setting.VIEW_X % view_mode, self._context.get_settings().get_int(constants.setting.VIEW_DEFAULT, 50)) + + self._view_mode = view_mode + + def get_view_mode(self): + if self._view_mode is not None: + return self._view_mode + + return self._context.get_settings().get_int(constants.setting.VIEW_DEFAULT, 50) + def get_skin_id(self): return xbmc.getSkinDir() diff --git a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py index 9f69f4c1..7f42560a 100644 --- a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py +++ b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py @@ -8,6 +8,7 @@ See LICENSES/GPL-2.0-only for more information. """ +import xbmc import xbmcgui import xbmcplugin @@ -66,6 +67,13 @@ class XbmcRunner(AbstractProviderRunner): self.handle, succeeded=True, updateListing=options.get(AbstractProvider.RESULT_UPDATE_LISTING, False), cacheToDisc=options.get(AbstractProvider.RESULT_CACHE_TO_DISC, True)) + + # set alternative view mode + if context.get_settings().is_override_view_enabled(): + view_mode = context.get_ui().get_view_mode() + if view_mode is not None: + context.log_debug('Override view mode to "%d"' % view_mode) + xbmc.executebuiltin('Container.SetViewMode(%d)' % view_mode) else: # handle exception pass diff --git a/resources/lib/youtube_plugin/kodion/utils/__init__.py b/resources/lib/youtube_plugin/kodion/utils/__init__.py index 20e9597e..4c279f9b 100644 --- a/resources/lib/youtube_plugin/kodion/utils/__init__.py +++ b/resources/lib/youtube_plugin/kodion/utils/__init__.py @@ -16,6 +16,7 @@ from .favorite_list import FavoriteList from .watch_later_list import WatchLaterList from .function_cache import FunctionCache from .access_manager import AccessManager +from .view_manager import ViewManager from .http_server import get_http_server, is_httpd_live, get_client_ip_address from .monitor import YouTubeMonitor from .player import YouTubePlayer @@ -25,7 +26,7 @@ from .system_version import SystemVersion from . import ip_api -__all__ = ['SearchHistory', 'FavoriteList', 'WatchLaterList', 'FunctionCache', 'AccessManager', +__all__ = ['SearchHistory', 'FavoriteList', 'WatchLaterList', 'FunctionCache', 'AccessManager', 'ViewManager', 'strip_html_from_text', 'create_path', 'create_uri_path', 'find_best_fit', 'to_unicode', 'to_utf8', 'datetime_parser', 'select_stream', 'get_http_server', 'is_httpd_live', 'YouTubeMonitor', 'make_dirs', 'loose_version', 'ip_api', 'PlaybackHistory', 'DataCache', 'get_client_ip_address', diff --git a/resources/lib/youtube_plugin/kodion/utils/view_manager.py b/resources/lib/youtube_plugin/kodion/utils/view_manager.py new file mode 100644 index 00000000..9a5fe5ae --- /dev/null +++ b/resources/lib/youtube_plugin/kodion/utils/view_manager.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +""" + + Copyright (C) 2014-2016 bromix (plugin.video.youtube) + Copyright (C) 2016-2018 plugin.video.youtube + + SPDX-License-Identifier: GPL-2.0-only + See LICENSES/GPL-2.0-only for more information. +""" + +from .. import constants + + +class ViewManager(object): + SUPPORTED_VIEWS = ['default', 'movies', 'tvshows', 'episodes', 'musicvideos', 'songs', 'albums', 'artists'] + SKIN_DATA = { + 'skin.confluence': { + 'default': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500} + ], + 'movies': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Media info', 'id': 504}, + {'name': 'Media info 2', 'id': 503} + ], + 'episodes': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Media info', 'id': 504}, + {'name': 'Media info 2', 'id': 503} + ], + 'tvshows': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Poster', 'id': 500}, + {'name': 'Wide', 'id': 505}, + {'name': 'Media info', 'id': 504}, + {'name': 'Media info 2', 'id': 503}, + {'name': 'Fanart', 'id': 508} + ], + 'musicvideos': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Media info', 'id': 504}, + {'name': 'Media info 2', 'id': 503} + ], + 'songs': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Media info', 'id': 506} + ], + 'albums': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Media info', 'id': 506} + ], + 'artists': [ + {'name': 'List', 'id': 50}, + {'name': 'Big List', 'id': 51}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Media info', 'id': 506} + ] + }, + 'skin.aeon.nox.5': { + 'default': [ + {'name': 'List', 'id': 50}, + {'name': 'Episodes', 'id': 502}, + {'name': 'LowList', 'id': 501}, + {'name': 'BannerWall', 'id': 58}, + {'name': 'Shift', 'id': 57}, + {'name': 'Posters', 'id': 56}, + {'name': 'ShowCase', 'id': 53}, + {'name': 'Landscape', 'id': 52}, + {'name': 'InfoWall', 'id': 51} + ] + }, + 'skin.xperience1080+': { + 'default': [ + {'name': 'List', 'id': 50}, + {'name': 'Thumbnail', 'id': 500}, + ], + 'episodes': [ + {'name': 'List', 'id': 50}, + {'name': 'Info list', 'id': 52}, + {'name': 'Fanart', 'id': 502}, + {'name': 'Landscape', 'id': 54}, + {'name': 'Poster', 'id': 55}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Banner', 'id': 60} + ], + }, + 'skin.xperience1080': { + 'default': [ + {'name': 'List', 'id': 50}, + {'name': 'Thumbnail', 'id': 500}, + ], + 'episodes': [ + {'name': 'List', 'id': 50}, + {'name': 'Info list', 'id': 52}, + {'name': 'Fanart', 'id': 502}, + {'name': 'Landscape', 'id': 54}, + {'name': 'Poster', 'id': 55}, + {'name': 'Thumbnail', 'id': 500}, + {'name': 'Banner', 'id': 60} + ], + }, + 'skin.estuary': { + 'default': [ + {'name': 'Shift', 'id': 53}, + {'name': 'InfoWall', 'id': 54}, + {'name': 'Wall', 'id': 500}, + {'name': 'WideList', 'id': 55}, + ], + 'episodes': [ + {'name': 'InfoWall', 'id': 54}, + {'name': 'Wall', 'id': 500}, + {'name': 'WideList', 'id': 55}, + ] + } + } + + def __init__(self, context): + self._context = context + + def has_supported_views(self): + """ + Returns True if the View of the current skin are supported + :return: True if the View of the current skin are supported + """ + return self._context.get_ui().get_skin_id() in self.SKIN_DATA + + def update_view_mode(self, title, view='default'): + view_id = -1 + settings = self._context.get_settings() + + skin_id = self._context.get_ui().get_skin_id() + skin_data = self.SKIN_DATA.get(skin_id, {}).get(view, []) + if skin_data: + items = [] + for view_data in skin_data: + items.append((view_data['name'], view_data['id'])) + view_id = self._context.get_ui().on_select(title, items) + else: + self._context.log_notice("ViewManager: Unknown skin id '%s'" % skin_id) + + if view_id == -1: + old_value = settings.get_string(constants.setting.VIEW_X % view, '') + if old_value: + result, view_id = self._context.get_ui().on_numeric_input(title, old_value) + if not result: + view_id = -1 + + if view_id > -1: + settings.set_int(constants.setting.VIEW_X % view, view_id) + return True + + return False diff --git a/resources/lib/youtube_plugin/youtube/helper/utils.py b/resources/lib/youtube_plugin/youtube/helper/utils.py index 88a7fb7f..5fc6e374 100644 --- a/resources/lib/youtube_plugin/youtube/helper/utils.py +++ b/resources/lib/youtube_plugin/youtube/helper/utils.py @@ -258,7 +258,7 @@ def update_video_infos(provider, context, video_id_dict, playlist_item_id_dict=N video_item = video_id_dict[video_id] # set mediatype - video_item.set_mediatype('video') # using video + video_item.set_mediatype('episode') # using video if not yt_item: continue diff --git a/resources/lib/youtube_plugin/youtube/helper/yt_specials.py b/resources/lib/youtube_plugin/youtube/helper/yt_specials.py index 89ad2b11..c5c8fec3 100644 --- a/resources/lib/youtube_plugin/youtube/helper/yt_specials.py +++ b/resources/lib/youtube_plugin/youtube/helper/yt_specials.py @@ -15,7 +15,7 @@ from . import utils def _process_related_videos(provider, context): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] page_token = context.get_param('page_token', '') @@ -60,7 +60,7 @@ def _process_child_comments(provider, context): def _process_recommendations(provider, context): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] page_token = context.get_param('page_token', '') @@ -72,7 +72,7 @@ def _process_recommendations(provider, context): def _process_popular_right_now(provider, context): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] page_token = context.get_param('page_token', '') @@ -85,7 +85,7 @@ def _process_popular_right_now(provider, context): def _process_browse_channels(provider, context): - provider.set_content_type(context, kodion.constants.content_type.FILES) + provider.set_content_type(context, kodion.constants.content_type.VIDEOS) result = [] # page_token = context.get_param('page_token', '') @@ -107,7 +107,7 @@ def _process_browse_channels(provider, context): def _process_disliked_videos(provider, context): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] page_token = context.get_param('page_token', '') @@ -122,7 +122,7 @@ def _process_live_events(provider, context, event_type='live'): def _sort(x): return x.get_aired() - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] # TODO: cache result @@ -142,7 +142,7 @@ def _process_description_links(provider, context): addon_id = context.get_param('addon_id', '') def _extract_urls(_video_id): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) url_resolver = UrlResolver(context) result = [] @@ -304,7 +304,7 @@ def _process_purchases_tv(provider, context): def _process_new_uploaded_videos_tv(provider, context): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] next_page_token = context.get_param('next_page_token', '') @@ -316,7 +316,7 @@ def _process_new_uploaded_videos_tv(provider, context): def _process_new_uploaded_videos_tv_filtered(provider, context): - provider.set_content_type(context, kodion.constants.content_type.VIDEOS) + provider.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] next_page_token = context.get_param('next_page_token', '') diff --git a/resources/lib/youtube_plugin/youtube/provider.py b/resources/lib/youtube_plugin/youtube/provider.py index fea29579..75c67802 100644 --- a/resources/lib/youtube_plugin/youtube/provider.py +++ b/resources/lib/youtube_plugin/youtube/provider.py @@ -437,7 +437,7 @@ class Provider(kodion.AbstractProvider): @kodion.RegisterProviderPath('^/playlist/(?P[^/]+)/$') def _on_playlist(self, context, re_match): - self.set_content_type(context, kodion.constants.content_type.VIDEOS) + self.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] @@ -461,7 +461,7 @@ class Provider(kodion.AbstractProvider): @kodion.RegisterProviderPath('^/channel/(?P[^/]+)/playlist/(?P[^/]+)/$') def _on_channel_playlist(self, context, re_match): - self.set_content_type(context, kodion.constants.content_type.VIDEOS) + self.set_content_type(context, kodion.constants.content_type.EPISODES) client = self.get_client(context) result = [] @@ -484,7 +484,7 @@ class Provider(kodion.AbstractProvider): @kodion.RegisterProviderPath('^/channel/(?P[^/]+)/playlists/$') def _on_channel_playlists(self, context, re_match): - self.set_content_type(context, kodion.constants.content_type.FILES) + self.set_content_type(context, kodion.constants.content_type.VIDEOS) result = [] channel_id = re_match.group('channel_id') @@ -525,7 +525,7 @@ class Provider(kodion.AbstractProvider): @kodion.RegisterProviderPath('^/channel/(?P[^/]+)/live/$') def _on_channel_live(self, context, re_match): - self.set_content_type(context, kodion.constants.content_type.VIDEOS) + self.set_content_type(context, kodion.constants.content_type.EPISODES) result = [] channel_id = re_match.group('channel_id') @@ -560,7 +560,7 @@ class Provider(kodion.AbstractProvider): if method == 'channel' and not channel_id: return False - self.set_content_type(context, kodion.constants.content_type.VIDEOS) + self.set_content_type(context, kodion.constants.content_type.EPISODES) resource_manager = self.get_resource_manager(context) @@ -648,7 +648,7 @@ class Provider(kodion.AbstractProvider): # noinspection PyUnusedLocal @kodion.RegisterProviderPath('^/location/mine/$') def _on_my_location(self, context, re_match): - self.set_content_type(context, kodion.constants.content_type.FILES) + self.set_content_type(context, kodion.constants.content_type.VIDEOS) settings = context.get_settings() result = list() @@ -793,7 +793,7 @@ class Provider(kodion.AbstractProvider): subscriptions = yt_subscriptions.process(method, self, context) if method == 'list': - self.set_content_type(context, kodion.constants.content_type.FILES) + self.set_content_type(context, kodion.constants.content_type.VIDEOS) channel_ids = [] for subscription in subscriptions: channel_ids.append(subscription.get_channel_id()) @@ -1025,9 +1025,9 @@ class Provider(kodion.AbstractProvider): context.set_param('q', search_text) if search_type == 'video': - self.set_content_type(context, kodion.constants.content_type.VIDEOS) + self.set_content_type(context, kodion.constants.content_type.EPISODES) else: - self.set_content_type(context, kodion.constants.content_type.FILES) + self.set_content_type(context, kodion.constants.content_type.VIDEOS) if page == 1 and search_type == 'video' and not event_type and not hide_folders: if not channel_id and not location: @@ -1361,7 +1361,7 @@ class Provider(kodion.AbstractProvider): settings = context.get_settings() _ = self.get_client(context) # required for self.is_logged_in() - self.set_content_type(context, kodion.constants.content_type.FILES) + self.set_content_type(context, kodion.constants.content_type.VIDEOS) result = [] @@ -1596,7 +1596,7 @@ class Provider(kodion.AbstractProvider): @staticmethod def set_content_type(context, content_type): context.set_content_type(content_type) - if content_type == kodion.constants.content_type.VIDEOS: + if content_type == kodion.constants.content_type.EPISODES: context.add_sort_method(kodion.constants.sort_method.UNSORTED, kodion.constants.sort_method.VIDEO_RUNTIME, kodion.constants.sort_method.DATE_ADDED, diff --git a/resources/settings.xml b/resources/settings.xml index 14bfc452..29661e89 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -610,6 +610,37 @@ + + 0 + false + + + + 0 + 50 + + + true + + + + 30027 + + + + 0 + 50 + + + true + + + + 30028 + + + + 0 10 @@ -628,7 +659,7 @@ - + 0 en-US