Skip to content

Commit

Permalink
Merge pull request #969 from MoojMidge/master
Browse files Browse the repository at this point in the history
v7.1.1+beta.6
  • Loading branch information
MoojMidge authored Nov 12, 2024
2 parents 8276797 + 5dcbddb commit 4cee6a1
Show file tree
Hide file tree
Showing 31 changed files with 790 additions and 445 deletions.
2 changes: 1 addition & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.youtube" name="YouTube" version="7.1.1+beta.5" provider-name="anxdpanic, bromix, MoojMidge">
<addon id="plugin.video.youtube" name="YouTube" version="7.1.1+beta.6" provider-name="anxdpanic, bromix, MoojMidge">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.requests" version="2.27.1"/>
Expand Down
19 changes: 19 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## v7.1.1+beta.6
### Fixed
- Ensure http server is started prior to creating MPD for playback #961
- Fix shuffle play of playlists not working
- Fix plugin category label not being applied if content type not set
- Fix Kodi navigating to root path of Video window if plugin listing fails

### Changed
- Use redirect in multiple busy dialog crash workaround #938
- Disable unusable alternate players #966
- Standardise plugin URIs for routing
- path parameters used for folders and sub-folders
- query parameters used for changing display modes, filtering, sorting and inputs

### New
- Add Quick Search and search management context menu items to Search folders
- Add context menu items to Clear and Play All/Shuffle in Bookmarks/Watch Later/Watch History folders
- Add progress dialog to My Subscription loading

## v7.1.1+beta.5
### Fixed
- Fix Python2/Android incompatibilty when checking CPU count #958
Expand Down
52 changes: 35 additions & 17 deletions resources/lib/youtube_plugin/kodion/abstract_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
class AbstractProvider(object):
RESULT_CACHE_TO_DISC = 'cache_to_disc' # (bool)
RESULT_FORCE_RESOLVE = 'force_resolve' # (bool)
RESULT_TRY_FALLBACK = 'try_fallback' # (bool)
RESULT_UPDATE_LISTING = 'update_listing' # (bool)

# map for regular expression (path) to method (names)
Expand Down Expand Up @@ -68,13 +69,13 @@ def __init__(self):
self.register_path(r''.join((
'^',
PATHS.WATCH_LATER,
'/(?P<command>add|clear|list|remove)/?$'
'/(?P<command>add|clear|list|play|remove)/?$'
)), self.on_watch_later)

self.register_path(r''.join((
'^',
PATHS.BOOKMARKS,
'/(?P<command>add|clear|list|remove)/?$'
'/(?P<command>add|clear|list|play|remove)/?$'
)), self.on_bookmarks)

self.register_path(r''.join((
Expand All @@ -86,34 +87,34 @@ def __init__(self):
self.register_path(r''.join((
'^',
PATHS.HISTORY,
'/?$'
'/(?P<command>clear|list|mark_unwatched|mark_watched|play|remove|reset_resume)/?$'
)), self.on_playback_history)

self.register_path(r'(?P<path>.*\/)extrafanart\/([\?#].+)?$',
self.on_extra_fanart)

@classmethod
def register_path(cls, re_path, method=None):
def register_path(cls, re_path, command=None):
"""
Registers a new method for the given regular expression
:param re_path: regular expression of the path
:param method: method or function to be registered
:param command: command or function to be registered
:return:
"""

def wrapper(method):
if callable(method):
func = method
def wrapper(command):
if callable(command):
func = command
else:
func = getattr(method, '__func__', None)
func = getattr(command, '__func__', None)
if not callable(func):
return None

cls._dict_path[re.compile(re_path, re.UNICODE)] = func
return method
return command

if method:
return wrapper(method)
if command:
return wrapper(command)
return wrapper

def run_wizard(self, context):
Expand Down Expand Up @@ -257,22 +258,37 @@ def reroute(self, context, path=None, params=None, uri=None):
path, params = uri

if not path:
context.log_error('Rerouting - No route path')
return False

window_fallback = params.pop('window_fallback', False)
window_replace = params.pop('window_replace', False)
window_return = params.pop('window_return', True)

if window_fallback:
container_uri = context.get_infolabel('Container.FolderPath')
if context.is_plugin_path(container_uri):
context.log_debug('Rerouting - Fallback route not required')
return (
False,
{
self.RESULT_TRY_FALLBACK: False,
},
)

if 'refresh' in params:
container = context.get_infolabel('System.CurrentControlId')
position = context.get_infolabel('Container.CurrentItem')
params['refresh'] += 1
elif path == current_path and params == current_params:
context.log_error('Rerouting - Unable to reroute to current path')
return False
else:
container = None
position = None

result = None
function_cache = context.get_function_cache()
window_replace = params.pop('window_replace', False)
window_return = params.pop('window_return', True)
try:
result, options = function_cache.run(
self.navigate,
Expand All @@ -287,10 +303,12 @@ def reroute(self, context, path=None, params=None, uri=None):
uri = context.create_uri(path, params)
if result:
context.log_debug('Rerouting - Success'
'\n\tURI: {uri}'
'\n\tReplace: |{window_replace}|'
'\n\tReturn: |{window_return}|'
'\n\tURI: {uri}'
'\n\tFallback: |{window_fallback}|'
'\n\tReplace: |{window_replace}|'
'\n\tReturn: |{window_return}|'
.format(uri=uri,
window_fallback=window_fallback,
window_replace=window_replace,
window_return=window_return))
else:
Expand Down
56 changes: 40 additions & 16 deletions resources/lib/youtube_plugin/kodion/context/abstract_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
import os

from ..logger import Logger
from ..compatibility import parse_qsl, quote, to_str, urlencode, urlsplit
from ..compatibility import (
parse_qsl,
quote,
to_str,
unquote,
urlencode,
urlsplit,
)
from ..constants import (
PATHS,
PLAY_FORCE_AUDIO,
Expand Down Expand Up @@ -62,6 +69,7 @@ class AbstractContext(Logger):
'logged_in',
'resume',
'screensaver',
'window_fallback',
'window_replace',
'window_return',
}
Expand Down Expand Up @@ -139,9 +147,13 @@ def __init__(self, path='/', params=None, plugin_id=''):
self._plugin_icon = None
self._version = 'UNKNOWN'

self._path = self.create_path(path)
self._path = path
self._path_parts = []
self.set_path(path, force=True)

self._params = params or {}
self.parse_params(self._params)

self._uri = self.create_uri(self._path, self._params)

@staticmethod
Expand Down Expand Up @@ -265,40 +277,52 @@ def create_uri(self, path=None, params=None, run=False):
uri = self._plugin_id.join(('plugin://', uri))

if params:
uri = '?'.join((uri, urlencode(params)))
if isinstance(params, (dict, list, tuple)):
params = urlencode(params)
uri = '?'.join((uri, params))

return ''.join((
'RunPlugin(',
uri,
')'
)) if run else uri

def get_parent_uri(self, **kwargs):
return self.create_uri(self._path_parts[:-1], **kwargs)

@staticmethod
def create_path(*args, **kwargs):
path = '/'.join([
part
for part in [
include_parts = kwargs.get('parts')
parts = [
part for part in [
str(arg).strip('/').replace('\\', '/').replace('//', '/')
for arg in args
] if part
])
if path:
path = path.join(('/', '/'))
]
if parts:
path = '/'.join(parts).join(('/', '/'))
if path.startswith(PATHS.ROUTE):
parts = parts[2:]
elif path.startswith(PATHS.COMMAND):
parts = []
elif path.startswith(PATHS.GOTO_PAGE):
parts = parts[2:]
if parts and parts[0].isnumeric():
parts = parts[1:]
else:
return '/'
return ('/', parts) if include_parts else '/'

if kwargs.get('is_uri'):
return quote(path)
return path
path = quote(path)
return (path, parts) if include_parts else path

def get_path(self):
return self._path

def set_path(self, *path, **kwargs):
if kwargs.get('force'):
self._path = path[0]
else:
self._path = self.create_path(*path)
path = unquote(path[0]).split('/')
self._path, self._path_parts = self.create_path(*path, parts=True)

def get_params(self):
return self._params
Expand All @@ -308,7 +332,7 @@ def get_param(self, name, default=None):

def parse_uri(self, uri):
uri = urlsplit(uri)
path = uri.path
path = uri.path.rstrip('/')
params = self.parse_params(
dict(parse_qsl(uri.query, keep_blank_values=True)),
update=False,
Expand Down
61 changes: 42 additions & 19 deletions resources/lib/youtube_plugin/kodion/context/xbmc/xbmc_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from ..abstract_context import AbstractContext
from ...compatibility import (
parse_qsl,
unquote,
urlsplit,
xbmc,
xbmcaddon,
Expand Down Expand Up @@ -142,6 +141,7 @@ class XbmcContext(AbstractContext):
'my_channel': 30507,
'my_location': 30654,
'my_subscriptions': 30510,
'my_subscriptions.loading': 575,
'my_subscriptions.filter.add': 30587,
'my_subscriptions.filter.added': 30589,
'my_subscriptions.filter.remove': 30588,
Expand Down Expand Up @@ -392,8 +392,7 @@ def init(self):
return

# first the path of the uri
parsed_url = urlsplit(uri)
self._path = unquote(parsed_url.path)
self.set_path(urlsplit(uri).path, force=True)

# after that try to get the params
self._params = {}
Expand Down Expand Up @@ -547,20 +546,26 @@ def set_content(self, content_type, sub_type=None, category_label=None):
))

def apply_content(self):
# ui local variable used for ui.get_view_manager() in unofficial version
ui = self.get_ui()

content_type = ui.pop_property(CONTENT_TYPE)
if not content_type:
return
if content_type:
content_type, sub_type, category_label = json.loads(content_type)
self.log_debug('Applying content-type: |{type}| for |{path}|'.format(
type=(sub_type or content_type), path=self.get_path()
))
xbmcplugin.setContent(self._plugin_handle, content_type)
else:
content_type = None
sub_type = None
category_label = None

content_type, sub_type, category_label = json.loads(content_type)
self.log_debug('Applying content-type: |{type}| for |{path}|'.format(
type=(sub_type or content_type), path=self.get_path()
))
xbmcplugin.setContent(self._plugin_handle, content_type)
if category_label is None:
category_label = self.get_param('category_label')
if category_label:
xbmcplugin.setPluginCategory(self._plugin_handle, category_label)

detailed_labels = self.get_settings().show_detailed_labels()
if sub_type == 'history':
self.add_sort_method(
Expand All @@ -582,6 +587,7 @@ def apply_content(self):
(SORT.UNSORTED,),
(SORT.LABEL,),
)

if content_type == CONTENT.VIDEO_CONTENT:
self.add_sort_method(
(SORT.CHANNEL, '[%A - ]%T \u2022 %P', '%D | %J'),
Expand Down Expand Up @@ -825,7 +831,7 @@ def wakeup(self, target, timeout=None, payload=None):
data.update(payload)
self.send_notification(WAKEUP, data)
if not timeout:
return
return None

pop_property = self.get_ui().pop_property
no_timeout = timeout < 0
Expand All @@ -834,17 +840,34 @@ def wakeup(self, target, timeout=None, payload=None):
wait_period = wait_period_ms / 1000

while no_timeout or remaining > 0:
awake = pop_property(WAKEUP)
if awake:
if awake == target:
self.log_debug('Wakeup |{0}| in {1}ms'
.format(awake, timeout - remaining))
else:
self.log_error('Wakeup |{0}| in {1}ms - expected |{2}|'
.format(awake, timeout - remaining, target))
data = pop_property(WAKEUP)
if data:
data = json.loads(data)

if data:
response = data.get('response')
response_target = data.get('target') or 'Unknown'

if target == response_target:
if response:
self.log_debug('Wakeup |{0}| in {1}ms'
.format(response_target,
timeout - remaining))
else:
self.log_error('Wakeup |{0}| in {1}ms - failed'
.format(response_target,
timeout - remaining))
return response

self.log_error('Wakeup |{0}| in {1}ms - expected |{2}|'
.format(response_target,
timeout - remaining,
target))
break

wait(wait_period)
remaining -= wait_period_ms
else:
self.log_error('Wakeup |{0}| timed out in {1}ms'
.format(target, timeout))
return False
Loading

0 comments on commit 4cee6a1

Please sign in to comment.