mirror of
https://github.com/anxdpanic/plugin.video.youtube.git
synced 2025-12-05 18:20:41 -08:00
Compare commits
3 commits
6ec507d4b4
...
ae9699c0cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae9699c0cc | ||
|
|
cdbcf5b517 | ||
|
|
14d446a8e4 |
16 changed files with 647 additions and 7 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.youtube" name="YouTube" version="7.3.0+beta.10" provider-name="anxdpanic, bromix, MoojMidge">
|
||||
<addon id="plugin.video.youtube" name="YouTube" version="7.3.0" provider-name="anxdpanic, bromix, MoojMidge">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="3.0.0"/>
|
||||
<import addon="script.module.requests" version="2.27.1"/>
|
||||
|
|
|
|||
185
changelog.txt
185
changelog.txt
|
|
@ -1,3 +1,188 @@
|
|||
## v7.3.0
|
||||
### Fixed
|
||||
- Revert change to busy polling interval #1339
|
||||
- Prune invalid entries from DB when closing connection #1331
|
||||
- Fix regressions with SQLite db operations #1331
|
||||
- Disable label masks being used in Kodi 18 #1327
|
||||
- Python 2 compatibility workaround for lack of timeout when trying to acquire an RLock #1327
|
||||
- More expansive handling of inconsistent urllib3 exception re-raising
|
||||
- Fix regression in handling audio only setting after d154325c5b672dccc6a17413063cfdeb32256ffd
|
||||
- Fix comments not using correct sort methods
|
||||
- Fix incorrectly using playlist cache entries that have been invalidated by playlist modification
|
||||
- Fix some context menu actions failing for video item bookmarks
|
||||
- Ensure listings and items added by the addon have correct sort order
|
||||
- Fix resetting client region when playing media with subtitles enabled
|
||||
- Only add playable items to playlist when adding related items
|
||||
- Fix using invalid default end limit with Playlist.GetItems JSONRPC method
|
||||
- Fix conversion of SRT subtitles to WebVTT #1256
|
||||
- Workaround playback failure of progressive streams
|
||||
- Fix re-sorting live search lists
|
||||
- Disable use of custom thumbnail urls #1245
|
||||
- Workaround addon service not starting prior to plugin invocation #1298
|
||||
- Fix unofficial version using localised sort order #1309
|
||||
- Fix parsing of logged_in query parameter
|
||||
- Fix typo in YouTubePlayerClient error hook
|
||||
- Fix not resolving single playable items when using the uri2addon plugin endpoint #1300
|
||||
- Correctly check whether access tokens are available to be used for player requests
|
||||
- Fix not correctly resetting client instance
|
||||
- Dont restore container position on forced refresh when playback ends
|
||||
- Better handle urllib3 re-raising low level errors but sometimes not
|
||||
- Ignore unused parameters in item constructors #1282
|
||||
- Various misc fixes for focus and position loss on refresh
|
||||
- Fix possible unnecessary listing refresh after playlist action
|
||||
- Don't check items added to non music or video playlists
|
||||
- Re-enable setting for displaying saved playlists #1023
|
||||
- Fix exceptions with using non-existent request response as context manager #1279
|
||||
- Use different default player client request which provides more captions in response #1250
|
||||
- Exclude retrying player clients that do not support authentication if authentication is required #1273
|
||||
- Only request authenticated player request once, if not otherwise required #1273
|
||||
- Fix not updating breadcrumb after certain context menu actions
|
||||
- Fix setting focus on items in listing when parent item is not shown #1012
|
||||
- Reduce CPU usage of service runner loop when idle
|
||||
- Simplify window history fallback for search inputs #1070 #1266
|
||||
- Fix MPD quality selection #1268
|
||||
- Fix stream feature for disabling HFR at max resolution #539
|
||||
- Don't re-raise BrokenPipeError in RequestHandler.handle_one_request #1259
|
||||
- Fix including details in label2 mask when video details in listings is disabled #1265
|
||||
- Fix incorrect modification of custom thumbnails #1245
|
||||
- Refresh stale cached entries if new player data is available #1259
|
||||
- Disable use of captions from clients that are sometimes aggressively rate limited #1250
|
||||
- Switch browse client for recommended videos #1254
|
||||
- Ignore failing player requests that require signing in but won't accept OAuth2 authentication #1254
|
||||
- Improve querying of GUI info to work with widgets and custom windows #1243
|
||||
- Fix using locale specific abbreviations for weekday and month in If-Modified-Since header #1246
|
||||
- Workaround various Kodi 18 and Python 2 issues #1246
|
||||
- Fix inconsistencies between item IDs used as params that could result in exceptions
|
||||
- Fix generated page token not working for first page in listing
|
||||
- Don't replace non-standard JPEG thumbnails with WebP thumbnails #1245
|
||||
- Fix not parsing infolabels used in plugin url path #1239 #1243
|
||||
- Fix not parsing infolabels used as plugin url query params #1239
|
||||
- Fix unnecessarily processing window properties #1238
|
||||
- Workaround for distributions that patch Kodi to disable System.InternetState #1224
|
||||
- Workaround issue with search not returning items if no search query is used
|
||||
- Misc updates to try and prevent some GVS requests from failing #1222
|
||||
- Fix not correctly updating channel details of subscriptions #1226
|
||||
- Redact IP address in stream urls when not using InputStream.Adaptive
|
||||
- Fix implementation of logging module debugging property
|
||||
- Python 2 compatibility fix to automatically handle timezones when determining timestamps
|
||||
- Fix not skipping invalid items in plugin list response
|
||||
- Fix not showing Create bookmark item if bookmarks list is empty
|
||||
- Python 2 compatibility fixes for pretty print logging and lack of datetime.datetime.timestamp
|
||||
- Fix typo resulting in spatial audio streams not being correctly enabled/disabled
|
||||
- Ensure DELETE API requests are properly authorised #1226
|
||||
- Update XbmcContext.localize to handle string interpolation #1225
|
||||
- Attempt to mitigate possible memory leaks associated with use of Python Requests
|
||||
- Ensure plugin folders in channel listings are not processed as YouTube items
|
||||
- Fix not refreshing cached data when not logged in #1224
|
||||
- Fix potentially misidentifying vp9.2 video streams #1222
|
||||
- Fix not fully redacting logged stream data
|
||||
- Fix clip start and end time parsing
|
||||
- Fix possible exception when fetching subtitles after http server fails to wakeup
|
||||
- Improve concurrent JSON file IO operations #1218
|
||||
- Change pruning of sqlite databases to avoid potential deadlocks #1161
|
||||
- Avoid potential deadlock in v3._process_list_response #1161
|
||||
- Improve listing pre and post fill methods #1161
|
||||
- Fix repeating previous V1 browse/next requests when no continuation is available #1161
|
||||
- Fix typo in allowable search parameters
|
||||
- Ensure best available quality is used for thumbnails as fanart #1212
|
||||
- Fix double forward slash in channel url in description
|
||||
- Fix toggling watched status on non-forced refresh
|
||||
- Ensure logged in status is properly (re)set when client is reset #1210
|
||||
- Don't rely on dict insertion order for client groups #1185
|
||||
- Ensure authenticated requests are used only when necessary #1210 #1196
|
||||
- Workaround playlistNotFound error in Related Videos #1210 #1196
|
||||
- Fix stream quality checks incorrectly identifying 480p streams as 360p
|
||||
- Add workarounds for various Kodi/ISA subtitle incompatibilities and formatting issues #489 #1147
|
||||
- Identify API requests requiring authentication
|
||||
- Fix incorrect page number used when post filling listings
|
||||
- Prevent unnecessary API requests when refreshing listing that uses channel filters
|
||||
- Improve loading of malformed/partial access_manager.json #1173
|
||||
|
||||
### Changed
|
||||
- Improve robustness of fetching recommended and related videos
|
||||
- Improve workarounds for SQLite concurrency issues
|
||||
- Remove possibly invalid access token if an authentication error occurs
|
||||
- Better organise and use standard labels for http server address and port settings
|
||||
- Try to make http server IP address selection even more obvious when running Setup Wizard #1320
|
||||
- Improve logging of errors caused by localised strings that have been incorrectly translated
|
||||
- Improve offline access to cached data
|
||||
- Updates to SQLite database lock handling
|
||||
- Ignore player request failures that may incorrectly indicate a need to sign-in #1312
|
||||
- Include playlist_id listitem property for items from virtual playlists
|
||||
- Don't list users own playlists in listing of saved playlists
|
||||
- Allow sign-in when partially logged in without needing to sign-out
|
||||
- Identify if user is only partially logged in
|
||||
- Use persistent visitor data where possible except when incognito
|
||||
- Allow additional query parameters to be inherited from parent listing #1282
|
||||
- Improve process for initial player request if remote history not enabled #1273
|
||||
- Disable unusable player clients #1273
|
||||
- Disable multiple busy dialog crash workarounds in Kodi 22
|
||||
- Include visitorData in subtitle request headers along with referer #1250
|
||||
- Revert use of WEBP thumbnails #1245
|
||||
- Improve notification of player request errors #1254 #1262
|
||||
- Add notification if reCaptcha check is required
|
||||
- Improve workarounds for failing authorised player requests #1254
|
||||
- Allow plugin url query parameters to hide folders in search and channel playlist listing #1251
|
||||
- Use WebP thumbnails instead of JPEG thumbnails
|
||||
- Update Setup Wizard for the various new Stream Features that have been added
|
||||
- Don't cache playback associated requests
|
||||
- Bypass requests cache when a listing is refreshed
|
||||
- Update order and wording of prompts used when adding custom bookmark
|
||||
- Allow bookmarking channel of videos within Bookmarks list
|
||||
- Return empty listing on failure to parse YouTube url
|
||||
- Update known itag details
|
||||
- Do not show notification when automatically removing played video from Watch Later list
|
||||
- Add visitor ID to stream headers #1222
|
||||
- Improve logging of adaptive streams from player requests #1222
|
||||
- Identify and de-prioritise DRC audio streams
|
||||
- Specifically use elapsed time for plugin profiling if available
|
||||
- Use new BookmarkItem class for custom bookmarks to allow updating details like normal bookmarks
|
||||
- Default to list as path command if not given when navigating to various internal listings
|
||||
- Re-enable http server idle shutdown for all platforms
|
||||
- Update default cache size to 50 MiB
|
||||
- Enable caching of YouTube virtual lists #1220
|
||||
- Improve fetching of cached playlist items #1220
|
||||
- Allow for use of last known good value if JSON file read IPC timeout occurs #1220
|
||||
- Limit JSON-RPC usage when interacting with Kodi playlist #1220
|
||||
- Improve syncing local history with Kodi play count
|
||||
- api_keys.json not backwards compatible with older addon versions
|
||||
- Set default live stream type to adaptive HLS
|
||||
- Allow saving of play count and last played date for live streams
|
||||
- Disable script.trakt scrobbling when playing YouTube videos
|
||||
- Allow failure of unauthorised player requests to be ignored #1211
|
||||
- Try to prevent misuse of strm files #1208
|
||||
- Update thumbnails size settings and selection logic #1204
|
||||
- Only show folders in channel playlists listing on first page and if not hidden
|
||||
- Improve cache performance and reliability
|
||||
- Improve management of user data stored as JSON files
|
||||
|
||||
### New
|
||||
- Add refresh to context menu of playlists
|
||||
- Allow watch urls from music.youtube.com to be directly handled by the addon
|
||||
- Allow urls from www.youtubekids.com to be directly handled by the addon
|
||||
- Add support for listing members only content of channels
|
||||
- Add selections to hide various folders from listings #1282
|
||||
- Add support for additional OAuth2 client to allow playback of age restricted videos #1273
|
||||
- Add dubbed audio preferences to stream features #1036 #1228 #1232
|
||||
- Add support for directly adding YouTube URL as custom bookmark
|
||||
- Add support for 3D/VR video and spatial audio in stream selections
|
||||
- Add Setup Wizard steps to change custom Watch History and Watch Later playlists to internal YouTube lists #1210
|
||||
- Add support for viewing YouTube history list (HL)
|
||||
- Add support for podcasts in related videos #1161
|
||||
- Add notification on successfully adding item to local Watch Later list #1210
|
||||
- Add support for saving/removing playlists to/from YouTube library (Saved Playlists) #1023
|
||||
- Add support for removing liked videos from YouTube Liked videos list
|
||||
- Add support for auto removing videos from YouTube Watch Later list after playback #1210
|
||||
- Add support for removing videos from YouTube Watch Later list #1210
|
||||
- Add support for adding videos to YouTube Watch Later list #1210
|
||||
- Initial support for viewing Saved Playlists #1023
|
||||
- Initial support for viewing YouTube Watch Later list (WL) #1210
|
||||
- Implement request cache
|
||||
- Add ability to create custom bookmarks #1208
|
||||
- Add subtitle type selection to stream features
|
||||
- Add plugin execution timeout to forcibly terminate execution after set time limit #1161
|
||||
- Improve addon logging using customised Python logging module
|
||||
|
||||
## v7.3.0+beta.10
|
||||
### Fixed
|
||||
- Prune invalid entries from DB when closing connection #1331
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ class AbstractProvider(object):
|
|||
if last_run and last_run > 1:
|
||||
self.pre_run_wizard_step(provider=self, context=context)
|
||||
wizard_steps = self.get_wizard_steps()
|
||||
wizard_steps.extend(ui.get_view_manager().get_wizard_steps())
|
||||
|
||||
step = 0
|
||||
steps = len(wizard_steps)
|
||||
|
|
|
|||
|
|
@ -186,6 +186,8 @@ PAGE = 'page'
|
|||
PLAYLIST_IDS = 'playlist_ids'
|
||||
SCREENSAVER = 'screensaver'
|
||||
SEEK = 'seek'
|
||||
SORT_DIR = 'sort_dir'
|
||||
SORT_METHOD = 'sort_method'
|
||||
START = 'start'
|
||||
VIDEO_IDS = 'video_ids'
|
||||
|
||||
|
|
@ -346,6 +348,8 @@ __all__ = (
|
|||
'PLAYLIST_IDS',
|
||||
'SCREENSAVER',
|
||||
'SEEK',
|
||||
'SORT_DIR',
|
||||
'SORT_METHOD',
|
||||
'START',
|
||||
'VIDEO_IDS',
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
||||
VIDEO_CONTENT = 'videos'
|
||||
LIST_CONTENT = 'files'
|
||||
VIDEO_CONTENT = 'episodes'
|
||||
LIST_CONTENT = 'default'
|
||||
|
||||
COMMENTS = 'comments'
|
||||
HISTORY = 'history'
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import sys
|
|||
|
||||
from . import const_content_types as CONTENT
|
||||
from ..compatibility import (
|
||||
xbmc,
|
||||
xbmcplugin,
|
||||
)
|
||||
|
||||
|
|
@ -77,12 +78,31 @@ methods = [
|
|||
('VIDEO_ORIGINAL_TITLE', 20376, 57),
|
||||
('VIDEO_ORIGINAL_TITLE_IGNORE_THE', 20376, None),
|
||||
]
|
||||
SORT_ID_MAPPING = {}
|
||||
|
||||
SORT = sys.modules[__name__]
|
||||
name = label_id = sort_by = sort_method = None
|
||||
for name, label_id, sort_by in methods:
|
||||
sort_method = getattr(xbmcplugin, 'SORT_METHOD_' + name, 0)
|
||||
setattr(SORT, name, sort_method)
|
||||
if sort_by is not None:
|
||||
SORT_ID_MAPPING.update((
|
||||
(name, sort_by),
|
||||
(xbmc.getLocalizedString(label_id), sort_by),
|
||||
(sort_method, sort_by if sort_method else 0),
|
||||
))
|
||||
|
||||
SORT_ID_MAPPING.update((
|
||||
(CONTENT.VIDEO_CONTENT.join(('__', '__')), SORT.UNSORTED),
|
||||
(CONTENT.LIST_CONTENT.join(('__', '__')), SORT.LABEL),
|
||||
(CONTENT.COMMENTS.join(('__', '__')), SORT.CHANNEL),
|
||||
(CONTENT.HISTORY.join(('__', '__')), SORT.LASTPLAYED),
|
||||
))
|
||||
|
||||
SORT_DIR = {
|
||||
xbmc.getLocalizedString(584): 'ascending',
|
||||
xbmc.getLocalizedString(585): 'descending',
|
||||
}
|
||||
|
||||
# Label mask token details:
|
||||
# https://github.com/xbmc/xbmc/blob/master/xbmc/utils/LabelFormatter.cpp#L33-L105
|
||||
|
|
@ -179,6 +199,7 @@ COMMENTS_CONTENT_SIMPLE = (
|
|||
del (
|
||||
sys,
|
||||
CONTENT,
|
||||
xbmc,
|
||||
xbmcplugin,
|
||||
methods,
|
||||
SORT,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ from ..constants import (
|
|||
PLAY_USING,
|
||||
SCREENSAVER,
|
||||
SEEK,
|
||||
SORT_DIR,
|
||||
SORT_METHOD,
|
||||
START,
|
||||
SUBSCRIPTION_ID,
|
||||
VIDEO_ID,
|
||||
|
|
@ -169,6 +171,8 @@ class AbstractContext(object):
|
|||
'q',
|
||||
'rating',
|
||||
'reload_path',
|
||||
SORT_DIR,
|
||||
SORT_METHOD,
|
||||
'search_type',
|
||||
SUBSCRIPTION_ID,
|
||||
'uri',
|
||||
|
|
|
|||
|
|
@ -705,6 +705,7 @@ class XbmcContext(AbstractContext):
|
|||
path=self.get_path())
|
||||
if content_type != 'default':
|
||||
xbmcplugin.setContent(self._plugin_handle, content_type)
|
||||
ui.get_view_manager().set_view_mode(content_type)
|
||||
|
||||
if category_label is None:
|
||||
category_label = self.get_param('category_label')
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ from ...constants import (
|
|||
REFRESH_CONTAINER,
|
||||
RELOAD_ACCESS_MANAGER,
|
||||
REROUTE_PATH,
|
||||
SORT_DIR,
|
||||
SORT_METHOD,
|
||||
SYNC_LISTITEM,
|
||||
TRAKT_PAUSE_FLAG,
|
||||
VIDEO_ID,
|
||||
|
|
@ -417,7 +419,31 @@ class XbmcPlugin(AbstractPlugin):
|
|||
container = ui.get_property(CONTAINER_ID)
|
||||
position = ui.get_property(CONTAINER_POSITION)
|
||||
|
||||
# set alternative view mode
|
||||
view_manager = ui.get_view_manager()
|
||||
if view_manager.is_override_view_enabled():
|
||||
post_run_actions.append((
|
||||
view_manager.apply_view_mode,
|
||||
{
|
||||
'context': context,
|
||||
},
|
||||
))
|
||||
|
||||
if is_same_path:
|
||||
sort_method = kwargs.get(SORT_METHOD)
|
||||
sort_dir = kwargs.get(SORT_DIR)
|
||||
if sort_method and sort_dir:
|
||||
post_run_actions.append((
|
||||
view_manager.apply_sort_method,
|
||||
{
|
||||
'context': context,
|
||||
SORT_METHOD: sort_method,
|
||||
SORT_DIR: sort_dir,
|
||||
CONTAINER_POSITION: position if forced else None,
|
||||
},
|
||||
))
|
||||
position = None
|
||||
|
||||
if (container and position
|
||||
and (forced or position == 'current')
|
||||
and (not played_video_id or route)):
|
||||
|
|
@ -439,7 +465,7 @@ class XbmcPlugin(AbstractPlugin):
|
|||
@staticmethod
|
||||
def post_run(context, ui, *actions, **kwargs):
|
||||
timeout = kwargs.get('timeout', 30)
|
||||
interval = kwargs.get('interval', 0.01)
|
||||
interval = kwargs.get('interval', 0.1)
|
||||
for action in actions:
|
||||
while not ui.get_container(container_type=None, check_ready=True):
|
||||
timeout -= interval
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ from .constants import (
|
|||
CHECK_SETTINGS,
|
||||
FOLDER_URI,
|
||||
PATHS,
|
||||
SORT_DIR,
|
||||
SORT_METHOD,
|
||||
)
|
||||
from .context import XbmcContext
|
||||
from .debug import Profiler
|
||||
|
|
@ -103,6 +105,20 @@ def run(context=_context,
|
|||
refresh = context.refresh_requested(force=True, off=True, params=params)
|
||||
new_params['refresh'] = refresh if refresh else 0
|
||||
|
||||
sort_method = (
|
||||
params.get(SORT_METHOD)
|
||||
or ui.get_infolabel('Container.SortMethod')
|
||||
)
|
||||
if sort_method:
|
||||
new_kwargs[SORT_METHOD] = sort_method
|
||||
|
||||
sort_dir = (
|
||||
params.get(SORT_DIR)
|
||||
or ui.get_infolabel('Container.SortOrder')
|
||||
)
|
||||
if sort_dir:
|
||||
new_kwargs[SORT_DIR] = sort_dir
|
||||
|
||||
if new_params:
|
||||
context.set_params(**new_params)
|
||||
|
||||
|
|
@ -112,7 +128,7 @@ def run(context=_context,
|
|||
log_params[key] = '<redacted>'
|
||||
|
||||
system_version = context.get_system_version()
|
||||
log.info(('Running v{version}',
|
||||
log.info(('Running v{version} (unofficial)',
|
||||
'Kodi: v{kodi}',
|
||||
'Python: v{python}',
|
||||
'Handle: {handle}',
|
||||
|
|
|
|||
|
|
@ -493,7 +493,7 @@ def run(argv):
|
|||
log.verbose_logging = False
|
||||
|
||||
system_version = context.get_system_version()
|
||||
log.info(('Running v{version}',
|
||||
log.info(('Running v{version} (unofficial)',
|
||||
'Kodi: v{kodi}',
|
||||
'Python: v{python}',
|
||||
'Category: {category!r}',
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ def run():
|
|||
monitor=monitor)
|
||||
|
||||
system_version = context.get_system_version()
|
||||
logging.info(('Starting v{version}',
|
||||
logging.info(('Starting v{version} (unofficial)',
|
||||
'Kodi: v{kodi}',
|
||||
'Python: v{python}'),
|
||||
version=context.get_version(),
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ class AbstractContextUI(object):
|
|||
message_template=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_view_manager(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def on_keyboard_input(title, default='', hidden=False):
|
||||
raise NotImplementedError()
|
||||
|
|
|
|||
342
resources/lib/youtube_plugin/kodion/ui/xbmc/view_manager.py
Normal file
342
resources/lib/youtube_plugin/kodion/ui/xbmc/view_manager.py
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
|
||||
Copyright (C) 2014-2016 bromix (plugin.video.youtube)
|
||||
Copyright (C) 2016-2025 plugin.video.youtube
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only
|
||||
See LICENSES/GPL-2.0-only for more information.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from ... import logging
|
||||
from ...compatibility import xbmc
|
||||
from ...constants import (
|
||||
CONTAINER_POSITION,
|
||||
CONTENT,
|
||||
SORT,
|
||||
SORT_DIR,
|
||||
SORT_METHOD,
|
||||
)
|
||||
|
||||
|
||||
class ViewManager(object):
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
SETTINGS = {
|
||||
'override': 'kodion.view.override', # (bool)
|
||||
'view_default': 'kodion.view.default', # (int)
|
||||
'view_type': 'kodion.view.{0}', # (int)
|
||||
}
|
||||
|
||||
SUPPORTED_TYPES_MAP = {
|
||||
CONTENT.LIST_CONTENT: 'default',
|
||||
CONTENT.VIDEO_CONTENT: 'episodes',
|
||||
}
|
||||
|
||||
STRING_MAP = {
|
||||
'prompt': 30777,
|
||||
'unsupported_skin': 10109,
|
||||
'supported_skin': 14240,
|
||||
'albums': 30035,
|
||||
'artists': 30034,
|
||||
'default': 30027,
|
||||
'episodes': 30028,
|
||||
'movies': 30029,
|
||||
'songs': 30033,
|
||||
'tvshows': 30032,
|
||||
}
|
||||
|
||||
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': 'IconWall', 'id': 52},
|
||||
{'name': 'WideList', 'id': 55},
|
||||
),
|
||||
'videos': (
|
||||
{'name': 'Shift', 'id': 53},
|
||||
{'name': 'InfoWall', 'id': 54},
|
||||
{'name': 'WideList', 'id': 55},
|
||||
{'name': 'Wall', 'id': 500},
|
||||
),
|
||||
'episodes': (
|
||||
{'name': 'InfoWall', 'id': 54},
|
||||
{'name': 'Wall', 'id': 500},
|
||||
{'name': 'WideList', 'id': 55},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, context):
|
||||
self._context = context
|
||||
self._view_mode = None
|
||||
|
||||
def is_override_view_enabled(self):
|
||||
return self._context.get_settings().get_bool(self.SETTINGS['override'])
|
||||
|
||||
def get_wizard_steps(self):
|
||||
return (self.run,)
|
||||
|
||||
def run(self, context, step, steps, **_kwargs):
|
||||
localize = context.localize
|
||||
|
||||
skin_id = xbmc.getSkinDir()
|
||||
if skin_id in self.SKIN_DATA:
|
||||
status = localize(self.STRING_MAP['supported_skin'])
|
||||
else:
|
||||
status = localize(self.STRING_MAP['unsupported_skin'])
|
||||
prompt_text = localize(self.STRING_MAP['prompt'], (skin_id, status))
|
||||
|
||||
step += 1
|
||||
if context.get_ui().on_yes_no_input(
|
||||
'{youtube} - {setup_wizard} ({step}/{steps})'.format(
|
||||
youtube=localize('youtube'),
|
||||
setup_wizard=localize('setup_wizard'),
|
||||
step=step,
|
||||
steps=steps,
|
||||
),
|
||||
localize('setup_wizard.prompt.x', prompt_text)
|
||||
):
|
||||
for view_type in self.SUPPORTED_TYPES_MAP:
|
||||
self.update_view_mode(skin_id, view_type)
|
||||
return step
|
||||
|
||||
def get_view_mode(self):
|
||||
if self._view_mode is None:
|
||||
self.set_view_mode()
|
||||
return self._view_mode
|
||||
|
||||
def set_view_mode(self, view_type='default'):
|
||||
settings = self._context.get_settings()
|
||||
default = settings.get_int(self.SETTINGS['view_default'], 50)
|
||||
if view_type == 'default':
|
||||
view_mode = default
|
||||
else:
|
||||
view_type = self.SUPPORTED_TYPES_MAP.get(view_type, 'default')
|
||||
view_mode = settings.get_int(
|
||||
self.SETTINGS['view_type'].format(view_type), default
|
||||
)
|
||||
self._view_mode = view_mode
|
||||
|
||||
def update_view_mode(self, skin_id, view_type='default'):
|
||||
view_id = -1
|
||||
settings = self._context.get_settings()
|
||||
ui = self._context.get_ui()
|
||||
|
||||
content_type = self.SUPPORTED_TYPES_MAP[view_type]
|
||||
|
||||
if content_type not in self.STRING_MAP:
|
||||
self.log.warning('Unsupported content type: %r', content_type)
|
||||
return False
|
||||
title = self._context.localize(self.STRING_MAP[content_type])
|
||||
|
||||
view_setting = self.SETTINGS['view_type'].format(content_type)
|
||||
current_value = settings.get_int(view_setting)
|
||||
if current_value == -1:
|
||||
self.log.warning('No setting for content type: %r', content_type)
|
||||
return False
|
||||
|
||||
skin_data = self.SKIN_DATA.get(skin_id, {})
|
||||
view_type_data = skin_data.get(view_type) or skin_data.get(content_type)
|
||||
if view_type_data:
|
||||
items = []
|
||||
preselect = -1
|
||||
for view_data in view_type_data:
|
||||
view_id = view_data['id']
|
||||
items.append((view_data['name'], view_id))
|
||||
if view_id == current_value:
|
||||
preselect = len(items) - 1
|
||||
view_id = ui.on_select(title, items, preselect=preselect)
|
||||
else:
|
||||
self.log.warning('Unsupported view: %r', view_type)
|
||||
|
||||
if view_id == -1:
|
||||
result, view_id = ui.on_numeric_input(title, current_value)
|
||||
if not result:
|
||||
return False
|
||||
|
||||
if view_id > -1:
|
||||
settings.set_int(view_setting, view_id)
|
||||
settings.set_bool(self.SETTINGS['override'], True)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def apply_view_mode(self, context):
|
||||
view_mode = self.get_view_mode()
|
||||
if view_mode is None:
|
||||
return
|
||||
|
||||
self.log.debug('Applying view mode: %r', view_mode)
|
||||
context.execute('Container.SetViewMode(%s)' % view_mode)
|
||||
|
||||
@classmethod
|
||||
def apply_sort_method(cls, context, **kwargs):
|
||||
execute = context.execute
|
||||
get_infobool = xbmc.getCondVisibility
|
||||
|
||||
sort_method = (
|
||||
kwargs.get(SORT_METHOD)
|
||||
or CONTENT.VIDEO_CONTENT.join(('__', '__'))
|
||||
)
|
||||
sort_id = SORT.SORT_ID_MAPPING.get(sort_method)
|
||||
if sort_id is None:
|
||||
cls.log.warning('Unknown sort method: %r', sort_method)
|
||||
return
|
||||
|
||||
sort_dir = kwargs.get(SORT_DIR)
|
||||
_sort_dir = SORT.SORT_DIR.get(sort_dir)
|
||||
if _sort_dir is None:
|
||||
cls.log.warning('Invalid sort direction: %r', sort_dir)
|
||||
return
|
||||
|
||||
position = kwargs.get(CONTAINER_POSITION)
|
||||
if position is not None:
|
||||
context.get_ui().focus_container(position=position)
|
||||
|
||||
# Workaround for Container.SetSortMethod failing for some sort methods
|
||||
num_attempts = 0
|
||||
while num_attempts < 4:
|
||||
# Workaround for Container.SetSortMethod(0) being a noop
|
||||
# https://github.com/xbmc/xbmc/blob/7e1a55cb861342cd9062745161d88aca08dcead1/xbmc/windows/GUIMediaWindow.cpp#L502
|
||||
if sort_id == 0:
|
||||
# Sort by track number to reset sort order to default order
|
||||
if not num_attempts % 2:
|
||||
_sort_method = 'TRACKNUM'
|
||||
_sort_id = SORT.SORT_ID_MAPPING.get(_sort_method)
|
||||
sort_action = 'Container.SetSortMethod(%s)' % _sort_id
|
||||
# Then switch to previous sort method which is default/unsorted
|
||||
# as per the order set in XbmcContext.apply_content
|
||||
else:
|
||||
_sort_method = 'UNSORTED'
|
||||
_sort_id = SORT.SORT_ID_MAPPING.get(_sort_method)
|
||||
sort_action = 'Container.PreviousSortMethod'
|
||||
else:
|
||||
_sort_method = sort_method
|
||||
_sort_id = sort_id
|
||||
sort_action = 'Container.SetSortMethod(%s)' % _sort_id
|
||||
|
||||
cls.log.debug('Applying sort method: {method!r} ({id})',
|
||||
method=_sort_method,
|
||||
id=_sort_id)
|
||||
execute(sort_action)
|
||||
context.sleep(0.1)
|
||||
|
||||
if not get_infobool('Container.SortDirection(%s)' % _sort_dir):
|
||||
cls.log.debug('Applying sort direction: %r', sort_dir)
|
||||
# This builtin should be Container.SortDirection but has been
|
||||
# broken since Kodi v16
|
||||
# https://github.com/xbmc/xbmc/commit/ac870b64b16dfd0fc2bd0496c14529cf6d563f41
|
||||
execute('Container.SetSortDirection')
|
||||
context.sleep(0.1)
|
||||
|
||||
num_attempts += 1
|
||||
|
||||
if get_infobool('Container.SortMethod(%s)' % sort_id):
|
||||
break
|
||||
else:
|
||||
cls.log.warning('Unable to apply sorting:'
|
||||
' {sort_method!r} ({sort_id}) {sort_dir!r}',
|
||||
sort_method=sort_method,
|
||||
sort_id=sort_id,
|
||||
sort_dir=sort_dir)
|
||||
|
|
@ -12,6 +12,7 @@ from __future__ import absolute_import, division, unicode_literals
|
|||
|
||||
from weakref import proxy
|
||||
|
||||
from .view_manager import ViewManager
|
||||
from ..abstract_context_ui import AbstractContextUI
|
||||
from ... import logging
|
||||
from ...compatibility import string_type, xbmc, xbmcgui
|
||||
|
|
@ -47,6 +48,7 @@ class XbmcContextUI(AbstractContextUI):
|
|||
def __init__(self, context):
|
||||
super(XbmcContextUI, self).__init__()
|
||||
self._context = context
|
||||
self._view_manager = None
|
||||
|
||||
def create_progress_dialog(self,
|
||||
heading,
|
||||
|
|
@ -77,6 +79,12 @@ class XbmcContextUI(AbstractContextUI):
|
|||
),
|
||||
)
|
||||
|
||||
def get_view_manager(self):
|
||||
if self._view_manager is None:
|
||||
self._view_manager = ViewManager(self._context)
|
||||
|
||||
return self._view_manager
|
||||
|
||||
@staticmethod
|
||||
def on_keyboard_input(title, default='', hidden=False):
|
||||
# Starting with Gotham (13.X > ...)
|
||||
|
|
|
|||
|
|
@ -900,6 +900,35 @@
|
|||
</constraints>
|
||||
<control format="string" type="spinner"/>
|
||||
</setting>
|
||||
<setting id="kodion.view.override" type="boolean" label="30026" help="">
|
||||
<level>0</level>
|
||||
<default>false</default>
|
||||
<control type="toggle"/>
|
||||
</setting>
|
||||
<setting id="kodion.view.default" type="integer" parent="kodion.view.override" label="30027" help="">
|
||||
<level>0</level>
|
||||
<default>55</default>
|
||||
<dependencies>
|
||||
<dependency type="enable">
|
||||
<condition setting="kodion.view.override" operator="is">true</condition>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<control format="integer" type="edit">
|
||||
<heading>30027</heading>
|
||||
</control>
|
||||
</setting>
|
||||
<setting id="kodion.view.episodes" type="integer" parent="kodion.view.override" label="30028" help="">
|
||||
<level>0</level>
|
||||
<default>55</default>
|
||||
<dependencies>
|
||||
<dependency type="enable">
|
||||
<condition setting="kodion.view.override" operator="is">true</condition>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<control format="integer" type="edit">
|
||||
<heading>30028</heading>
|
||||
</control>
|
||||
</setting>
|
||||
</group>
|
||||
<group id="regional" label="14222">
|
||||
<setting id="youtube.language_region.configure" type="action" label="30527" help="">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue