Skip to content

Commit 302f7a7

Browse files
authored
Merge pull request #1429 from MoojMidge/v7.4
v7.4.3+beta.1
2 parents fc0b621 + 3edccda commit 302f7a7

25 files changed

Lines changed: 627 additions & 386 deletions

File tree

addon.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<addon id="plugin.video.youtube" name="YouTube" version="7.4.2" provider-name="anxdpanic, bromix, MoojMidge">
2+
<addon id="plugin.video.youtube" name="YouTube" version="7.4.3+beta.1" provider-name="anxdpanic, bromix, MoojMidge">
33
<requires>
44
<import addon="xbmc.python" version="3.0.0"/>
55
<import addon="script.module.requests" version="2.27.1"/>

changelog.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
## v7.4.3+beta.1
2+
### Fixed
3+
- Updates to resolve inconsistent Python2 string encoding/decoding
4+
- Ensure parameters to Kodi builtin functions are parsed as string literals
5+
- Allow trailing slashes in url of html pages served by internal http server
6+
7+
### Changed
8+
- Open confirmation dialog, with a link for further information, when sign in process fails
9+
- Update API config page html
10+
- Improve handling of API key settings changes
11+
- Detect and notify when API requests cannot be completed
12+
- Use consistent language for signing in/out
13+
14+
### New
15+
- Add button in API settings to view address of API config page
16+
- Fallback to playing channel uploads if no live stream is available
17+
- Add fallback method to load playlists when v3 API request cannot be made
18+
119
## v7.4.2
220
### Fixed
321
- Ensure updated context is used in client instance when rerouting to existing provider handler methods #1418

resources/language/resource.language.en_gb/strings.po

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -490,15 +490,15 @@ msgid "No videos found."
490490
msgstr ""
491491

492492
msgctxt "#30546"
493-
msgid "Please complete all login prompts"
493+
msgid "Please sign in and complete all access authorisation prompts"
494494
msgstr ""
495495

496496
msgctxt "#30547"
497-
msgid "You may be prompted to login and enable access to multiple applications so that this addon can function properly."
497+
msgid "You may be prompted to sign in and enable access to multiple applications so that this addon can function properly."
498498
msgstr ""
499499

500500
msgctxt "#30548"
501-
msgid ""
501+
msgid "Authorisation process failed. Check the log for more information about this message.[CR][CR]Refer to the wiki for detailed setup instructions: [B]https://ytaddon.page.link/keys[/B]"
502502
msgstr ""
503503

504504
msgctxt "#30549"
@@ -826,7 +826,7 @@ msgid "IP whitelist (comma delimited)"
826826
msgstr ""
827827

828828
msgctxt "#30630"
829-
msgid ""
829+
msgid "Check API configuration page address"
830830
msgstr ""
831831

832832
msgctxt "#30631"
@@ -1166,7 +1166,7 @@ msgid "Prefer automatically translated dubbed audio over original audio"
11661166
msgstr ""
11671167

11681168
msgctxt "#30715"
1169-
msgid "Use YouTube internal list for Watch History?[CR][CR]Requires signing-in via the addon and activating history tracking on YouTube."
1169+
msgid "Use YouTube internal list for Watch History?[CR][CR]Requires signing in via the addon and activating history tracking on YouTube."
11701170
msgstr ""
11711171

11721172
msgctxt "#30716"
@@ -1178,7 +1178,7 @@ msgid "Disliked video"
11781178
msgstr ""
11791179

11801180
msgctxt "#30718"
1181-
msgid "Use YouTube internal list for Watch Later?[CR][CR]Requires signing-in via the addon."
1181+
msgid "Use YouTube internal list for Watch Later?[CR][CR]Requires signing in via the addon."
11821182
msgstr ""
11831183

11841184
msgctxt "#30719"

resources/lib/youtube_plugin/kodion/abstract_provider.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
SearchHistoryItem,
3939
UriItem,
4040
)
41-
from .utils.convert_format import to_unicode
4241

4342

4443
class AbstractProvider(object):
@@ -366,30 +365,32 @@ def reroute(self, context, path=None, params=None, uri=None):
366365
if window_cache:
367366
ui.set_property(REROUTE_PATH, path)
368367

369-
action = ''.join((
370-
'ReplaceWindow' if window_replace else 'ActivateWindow',
371-
'(Videos,',
372-
uri,
373-
',return)' if window_return else ')',
374-
))
375-
376368
timeout = 30
377369
while ui.busy_dialog_active():
378370
timeout -= 1
379371
if timeout < 0:
380372
self.log.warning('Multiple busy dialogs active'
381373
' - Rerouting workaround')
382-
return UriItem('command://{0}'.format(action))
374+
defer = True
375+
break
383376
context.sleep(0.1)
384377
else:
385-
context.execute(
386-
action,
387-
# wait=True,
388-
# wait_for=(REROUTE_PATH if window_cache else None),
389-
# wait_for_set=False,
390-
# block_ui=True,
391-
)
392-
return True
378+
defer = False
379+
380+
action = context.create_uri(
381+
uri,
382+
window={
383+
'name': 'Videos',
384+
'replace': window_replace,
385+
'return': window_return,
386+
},
387+
command=defer,
388+
)
389+
390+
if defer:
391+
return UriItem(action)
392+
context.execute(action)
393+
return True
393394

394395
@staticmethod
395396
def on_bookmarks(provider, context, re_match):
@@ -412,7 +413,7 @@ def on_search(provider, context, re_match):
412413
search_history = context.get_search_history()
413414

414415
if not command or command == 'query':
415-
query = to_unicode(params.get('q', ''))
416+
query = params.get('q', '')
416417
if query:
417418
result, options = provider.on_search_run(context, query=query)
418419
if not options:
@@ -428,7 +429,7 @@ def on_search(provider, context, re_match):
428429
context.set_path(PATHS.SEARCH, command)
429430

430431
if command == 'remove':
431-
query = to_unicode(params.get('q', ''))
432+
query = params.get('q', '')
432433
if not ui.on_yes_no_input(
433434
localize('content.remove'),
434435
localize('content.remove.check.x', query),
@@ -442,7 +443,7 @@ def on_search(provider, context, re_match):
442443
return True, {provider.FORCE_REFRESH: True}
443444

444445
if command == 'rename':
445-
query = to_unicode(params.get('q', ''))
446+
query = params.get('q', '')
446447
result, new_query = ui.on_keyboard_input(
447448
localize('search.rename'), query
448449
)

resources/lib/youtube_plugin/kodion/compatibility/__init__.py

Lines changed: 116 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
SPDX-License-Identifier: GPL-2.0-only
77
See LICENSES/GPL-2.0-only for more information.
88
"""
9+
from __future__ import absolute_import, division, unicode_literals
10+
911

1012
__all__ = (
1113
'BaseHTTPRequestHandler',
1214
'StringIO',
1315
'TCPServer',
1416
'ThreadingMixIn',
1517
'available_cpu_count',
16-
'byte_string_type',
1718
'datetime_infolabel',
1819
'entity_escape',
1920
'generate_hash',
@@ -25,6 +26,7 @@
2526
'range_type',
2627
'string_type',
2728
'to_str',
29+
'to_unicode',
2830
'unescape',
2931
'unquote',
3032
'unquote_plus',
@@ -72,11 +74,16 @@
7274

7375
range_type = (range, list)
7476

75-
byte_string_type = bytes
7677
string_type = str
7778
to_str = str
7879

7980

81+
def to_unicode(text):
82+
if isinstance(text, bytes):
83+
text = text.decode('utf-8', errors='ignore')
84+
return text
85+
86+
8087
def entity_escape(text,
8188
entities=str.maketrans({
8289
'&': '&amp;',
@@ -133,8 +140,8 @@ def generate_hash(*args, **kwargs):
133140
urlencode as _urlencode,
134141
)
135142
from urlparse import (
136-
parse_qs,
137-
parse_qsl,
143+
parse_qs as _parse_qs,
144+
parse_qsl as _parse_qsl,
138145
urljoin,
139146
urlsplit,
140147
urlunsplit,
@@ -150,38 +157,103 @@ def generate_hash(*args, **kwargs):
150157
)
151158

152159

153-
def quote(data, *args, **kwargs):
154-
return _quote(to_str(data), *args, **kwargs)
155-
156-
157-
def quote_plus(data, *args, **kwargs):
158-
return _quote_plus(to_str(data), *args, **kwargs)
159-
160-
161-
def unquote(data):
162-
return _unquote(to_str(data))
163-
164-
165-
def unquote_plus(data):
166-
return _unquote_plus(to_str(data))
167-
168-
169-
def urlencode(data, *args, **kwargs):
170-
if isinstance(data, dict):
171-
data = data.items()
172-
kwargs = {
173-
key: value
174-
for key, value in kwargs.viewitems()
175-
if key in {'query', 'doseq'}
176-
}
177-
return _urlencode({
178-
to_str(key): (
179-
[to_str(part) for part in value]
160+
def parse_qs(*args, **kwargs):
161+
num_args = len(args)
162+
query_dict = _parse_qs(
163+
to_str(args[0] if args else kwargs.get('qs')),
164+
keep_blank_values=(
165+
args[1]
166+
if num_args >= 2 else
167+
kwargs.get('keep_blank_values', 0)
168+
),
169+
strict_parsing=(
170+
args[2]
171+
if num_args >= 3 else
172+
kwargs.get('strict_parsing', 0)
173+
),
174+
# max_num_fields=(
175+
# args[3]
176+
# if num_args >= 4 else
177+
# kwargs.get('max_num_fields', 0)
178+
# ),
179+
)
180+
return {
181+
to_unicode(key): (
182+
[to_unicode(part) for part in value]
180183
if isinstance(value, (list, tuple)) else
181-
to_str(value)
184+
to_unicode(value)
182185
)
183-
for key, value in data
184-
}, *args, **kwargs)
186+
for key, value in query_dict.viewitems()
187+
}
188+
189+
190+
def parse_qsl(*args, **kwargs):
191+
num_args = len(args)
192+
query_list = _parse_qsl(
193+
to_str(args[0] if args else kwargs.get('qs')),
194+
keep_blank_values=(
195+
args[1]
196+
if num_args >= 2 else
197+
kwargs.get('keep_blank_values', 0)
198+
),
199+
strict_parsing=(
200+
args[2]
201+
if num_args >= 3 else
202+
kwargs.get('strict_parsing', 0)
203+
),
204+
# max_num_fields=(
205+
# args[3]
206+
# if num_args >= 4 else
207+
# kwargs.get('max_num_fields', 0)
208+
# ),
209+
)
210+
return [
211+
(to_unicode(key), to_unicode(value))
212+
for key, value in query_list
213+
]
214+
215+
216+
def quote(*args, **kwargs):
217+
return _quote(
218+
to_str(args[0] if args else kwargs.get('string')),
219+
safe=args[1] if len(args) >= 2 else kwargs.get('safe', '/'),
220+
)
221+
222+
223+
def quote_plus(*args, **kwargs):
224+
return _quote_plus(
225+
to_str(args[0] if args else kwargs.get('string')),
226+
safe=args[1] if len(args) >= 2 else kwargs.get('safe', '/'),
227+
)
228+
229+
230+
def unquote(*args, **kwargs):
231+
return _unquote(
232+
to_str(args[0] if args else kwargs.get('string'))
233+
)
234+
235+
236+
def unquote_plus(*args, **kwargs):
237+
return _unquote_plus(
238+
to_str(args[0] if args else kwargs.get('string'))
239+
)
240+
241+
242+
def urlencode(*args, **kwargs):
243+
query = args[0] if args else kwargs.get('query')
244+
if isinstance(query, dict):
245+
query = query.viewitems()
246+
return _urlencode(
247+
query={
248+
to_str(key): (
249+
[to_str(part) for part in value]
250+
if isinstance(value, (list, tuple)) else
251+
to_str(value)
252+
)
253+
for key, value in query
254+
},
255+
doseq=args[1] if len(args) >= 2 else kwargs.get('doseq', 0),
256+
)
185257

186258

187259
class StringIO(_StringIO):
@@ -205,18 +277,23 @@ def __exit__(self, exc_type, exc_val, exc_tb):
205277

206278
range_type = (xrange, list)
207279

208-
byte_string_type = (bytes, str)
209280
string_type = basestring
210281

211282

212-
def to_str(value, _format='{0!s}'.format):
283+
def to_str(value, _format=b'{0!s}'.format):
213284
if not isinstance(value, basestring):
214285
value = _format(value)
215-
if isinstance(value, unicode):
216-
value = value.encode('utf-8')
286+
elif isinstance(value, unicode):
287+
value = value.encode('utf-8', errors='ignore')
217288
return value
218289

219290

291+
def to_unicode(text):
292+
if isinstance(text, (bytes, str)):
293+
text = text.decode('utf-8', errors='ignore')
294+
return text
295+
296+
220297
def entity_escape(text,
221298
entities={
222299
'&': '&amp;',
@@ -231,7 +308,7 @@ def entity_escape(text,
231308

232309

233310
def generate_hash(*args, **kwargs):
234-
return md5(''.join(
311+
return md5(b''.join(
235312
map(to_str, args or kwargs.get('iter'))
236313
)).hexdigest()
237314

resources/lib/youtube_plugin/kodion/constants/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
REFRESH_CONTAINER = 'refresh_container'
116116
RELOAD_ACCESS_MANAGER = 'reload_access_manager'
117117
SERVICE_IPC = 'service_ipc'
118+
SYNC_API_KEYS = 'sync_api_keys'
118119
SYNC_LISTITEM = 'sync_listitem'
119120

120121
# Sleep/wakeup states
@@ -282,6 +283,7 @@
282283
'REFRESH_CONTAINER',
283284
'RELOAD_ACCESS_MANAGER',
284285
'SERVICE_IPC',
286+
'SYNC_API_KEYS',
285287
'SYNC_LISTITEM',
286288

287289
# Sleep/wakeup states

0 commit comments

Comments
 (0)