Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion script.module.youtube.dl/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="script.module.youtube.dl" name="youtube-dl Control" version="25.05.18+matrix.1" provider-name="ytdl-org,ruuk,sy6sy2,wwark">
<addon id="script.module.youtube.dl" name="youtube-dl Control" version="25.11.30+matrix.1" provider-name="ytdl-org,ruuk,sy6sy2,wwark">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.addon.signals" version="0.0.5+matrix.1"/>
Expand Down
2 changes: 1 addition & 1 deletion script.module.youtube.dl/lib/YDStreamExtractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def _getYoutubeDLVideo(url, quality=None, resolve_redirects=False):
try:
url = resolve_http_redirect(url)
except Exception:
util.ERROR('_getYoutubeDLVideo(): Failed to resolve URL')
util.ERROR('_getYoutubeDLVideo(): Failed to resolve URL'+' '+url)
return None
ytdl = YoutubeDLWrapper._getYTDL()
ytdl.clearDownloadParams()
Expand Down
95 changes: 43 additions & 52 deletions script.module.youtube.dl/lib/youtube_dl/YoutubeDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class YoutubeDL(object):

_NUMERIC_FIELDS = set((
'width', 'height', 'tbr', 'abr', 'asr', 'vbr', 'fps', 'filesize', 'filesize_approx',
'timestamp', 'upload_year', 'upload_month', 'upload_day',
'timestamp', 'upload_year', 'upload_month', 'upload_day', 'available_at',
'duration', 'view_count', 'like_count', 'dislike_count', 'repost_count',
'average_rating', 'comment_count', 'age_limit',
'start_time', 'end_time',
Expand Down Expand Up @@ -681,8 +681,7 @@ def write_debug(self, message, only_once=False):
message = '[debug] {0}'.format(message)
if self.params.get('logger'):
self.params['logger'].debug(message)
else:
self.to_stderr(message, only_once)


def report_unscoped_cookies(self, *args, **kwargs):
# message=None, tb=False, is_error=False
Expand Down Expand Up @@ -2404,60 +2403,52 @@ def format_resolution(format, default='unknown'):
return res

def _format_note(self, fdict):
res = ''
if fdict.get('ext') in ['f4f', 'f4m']:
res += '(unsupported) '
if fdict.get('language'):
if res:
res += ' '
res += '[%s] ' % fdict['language']
if fdict.get('format_note') is not None:
res += fdict['format_note'] + ' '
if fdict.get('tbr') is not None:
res += '%4dk ' % fdict['tbr']

def simplified_codec(f, field):
assert field in ('acodec', 'vcodec')
codec = f.get(field)
return (
'unknown' if not codec
else '.'.join(codec.split('.')[:4]) if codec != 'none'
else 'images' if field == 'vcodec' and f.get('acodec') == 'none'
else None if field == 'acodec' and f.get('vcodec') == 'none'
else 'audio only' if field == 'vcodec'
else 'video only')

res = join_nonempty(
fdict.get('ext') in ('f4f', 'f4m') and '(unsupported)',
fdict.get('language') and ('[%s]' % (fdict['language'],)),
fdict.get('format_note') is not None and fdict['format_note'],
fdict.get('tbr') is not None and ('%4dk' % fdict['tbr']),
delim=' ')
res = [res] if res else []
if fdict.get('container') is not None:
if res:
res += ', '
res += '%s container' % fdict['container']
if (fdict.get('vcodec') is not None
and fdict.get('vcodec') != 'none'):
if res:
res += ', '
res += fdict['vcodec']
if fdict.get('vbr') is not None:
res += '@'
res.append('%s container' % (fdict['container'],))
if fdict.get('vcodec') not in (None, 'none'):
codec = simplified_codec(fdict, 'vcodec')
if codec and fdict.get('vbr') is not None:
codec += '@'
elif fdict.get('vbr') is not None and fdict.get('abr') is not None:
res += 'video@'
if fdict.get('vbr') is not None:
res += '%4dk' % fdict['vbr']
codec = 'video@'
else:
codec = None
codec = join_nonempty(codec, fdict.get('vbr') is not None and ('%4dk' % fdict['vbr']))
if codec:
res.append(codec)
if fdict.get('fps') is not None:
if res:
res += ', '
res += '%sfps' % fdict['fps']
if fdict.get('acodec') is not None:
if res:
res += ', '
if fdict['acodec'] == 'none':
res += 'video only'
else:
res += '%-5s' % fdict['acodec']
elif fdict.get('abr') is not None:
if res:
res += ', '
res += 'audio'
if fdict.get('abr') is not None:
res += '@%3dk' % fdict['abr']
if fdict.get('asr') is not None:
res += ' (%5dHz)' % fdict['asr']
res.append('%sfps' % (fdict['fps'],))
codec = (
simplified_codec(fdict, 'acodec') if fdict.get('acodec') is not None
else 'audio' if fdict.get('abr') is not None else None)
if codec:
res.append(join_nonempty(
'%-4s' % (codec + (('@%3dk' % fdict['abr']) if fdict.get('abr') else ''),),
fdict.get('asr') and '(%5dHz)' % fdict['asr'], delim=' '))
if fdict.get('filesize') is not None:
if res:
res += ', '
res += format_bytes(fdict['filesize'])
res.append(format_bytes(fdict['filesize']))
elif fdict.get('filesize_approx') is not None:
if res:
res += ', '
res += '~' + format_bytes(fdict['filesize_approx'])
return res
res.append('~' + format_bytes(fdict['filesize_approx']))
return ', '.join(res)

def list_formats(self, info_dict):
formats = info_dict.get('formats', [info_dict])
Expand Down
2 changes: 2 additions & 0 deletions script.module.youtube.dl/lib/youtube_dl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ def parse_retries(retries):
'include_ads': opts.include_ads,
'default_search': opts.default_search,
'youtube_include_dash_manifest': opts.youtube_include_dash_manifest,
'youtube_player_js_version': opts.youtube_player_js_version,
'youtube_player_js_variant': opts.youtube_player_js_variant,
'encoding': opts.encoding,
'extract_flat': opts.extract_flat,
'mark_watched': opts.mark_watched,
Expand Down
170 changes: 169 additions & 1 deletion script.module.youtube.dl/lib/youtube_dl/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
try:
import collections.abc as compat_collections_abc
except ImportError:
import collections as compat_collections_abc
compat_collections_abc = collections


# compat_urllib_request
Expand Down Expand Up @@ -3452,13 +3452,18 @@ def unpack(self, string):
except ImportError:
compat_map = map


# compat_filter, compat_filter_fns
try:
from future_builtins import filter as compat_filter
except ImportError:
try:
from itertools import ifilter as compat_filter
except ImportError:
compat_filter = filter
# "Is this function one or maybe the other filter()?"
compat_filter_fns = tuple(set((filter, compat_filter)))


# compat_zip
try:
Expand All @@ -3478,6 +3483,40 @@ def unpack(self, string):
from itertools import izip_longest as compat_itertools_zip_longest


# compat_abc_ABC
try:
from abc import ABC as compat_abc_ABC
except ImportError:
# Py < 3.4
from abc import ABCMeta as _ABCMeta
compat_abc_ABC = _ABCMeta(str('ABC'), (object,), {})


# dict mixin used here
# like UserDict.DictMixin, without methods created by MutableMapping
class _DictMixin(compat_abc_ABC):
def has_key(self, key):
return key in self

# get(), clear(), setdefault() in MM

def iterkeys(self):
return (k for k in self)

def itervalues(self):
return (self[k] for k in self)

def iteritems(self):
return ((k, self[k]) for k in self)

# pop(), popitem() in MM

def copy(self):
return type(self)(self)

# update() in MM


# compat_collections_chain_map
# collections.ChainMap: new class
try:
Expand Down Expand Up @@ -3632,6 +3671,129 @@ def compat_datetime_timedelta_total_seconds(td):
compat_zstandard = None


# compat_thread
try:
import _thread as compat_thread
except ImportError:
try:
import thread as compat_thread
except ImportError:
import dummy_thread as compat_thread


# compat_dict
# compat_builtins_dict
# compat_dict_items
if sys.version_info >= (3, 6):
compat_dict = compat_builtins_dict = dict
compat_dict_items = dict.items
else:
_get_ident = compat_thread.get_ident

class compat_dict(compat_collections_abc.MutableMapping, _DictMixin, dict):
"""`dict` that preserves insertion order with interface like Py3.7+"""

_order = [] # default that should never be used

def __init__(self, *mappings_or_iterables, **kwargs):
# order an unordered dict using a list of keys: actual Py 2.7+
# OrderedDict uses a doubly linked list for better performance
self._order = []
for arg in mappings_or_iterables:
self.__update(arg)
if kwargs:
self.__update(kwargs)

def __getitem__(self, key):
return dict.__getitem__(self, key)

def __setitem__(self, key, value):
try:
if key not in self._order:
self._order.append(key)
dict.__setitem__(self, key, value)
except Exception:
if key in self._order[-1:] and key not in self:
del self._order[-1]
raise

def __len__(self):
return dict.__len__(self)

def __delitem__(self, key):
dict.__delitem__(self, key)
try:
# expected case, O(len(self)), but who dels anyway?
self._order.remove(key)
except ValueError:
pass

def __iter__(self):
for from_ in self._order:
if from_ in self:
yield from_

def __del__(self):
for attr in ('_order',):
try:
delattr(self, attr)
except Exception:
pass

def __repr__(self, _repr_running={}):
# skip recursive items ...
call_key = id(self), _get_ident()
if _repr_running.get(call_key):
return '...'
_repr_running[call_key] = True
try:
return '%s({%s})' % (
type(self).__name__,
','.join('%r: %r' % k_v for k_v in self.items()))
finally:
del _repr_running[call_key]

# merge/update (PEP 584)

def __or__(self, other):
if not isinstance(other, compat_collections_abc.Mapping):
return NotImplemented
new = type(self)(self)
new.update(other)
return new

def __ror__(self, other):
if not isinstance(other, compat_collections_abc.Mapping):
return NotImplemented
new = type(other)(other)
new.update(self)
return new

def __ior__(self, other):
self.update(other)
return self

# optimisations

def __reversed__(self):
for from_ in reversed(self._order):
if from_ in self:
yield from_

def __contains__(self, item):
return dict.__contains__(self, item)

# allow overriding update without breaking __init__
def __update(self, *args, **kwargs):
super(compat_dict, self).update(*args, **kwargs)

compat_builtins_dict = dict
# Using the object's method, not dict's:
# an ordered dict's items can be returned unstably by unordered
# dict.items as if the method was not ((k, self[k]) for k in self)
compat_dict_items = lambda d: d.items()


legacy = [
'compat_HTMLParseError',
'compat_HTMLParser',
Expand Down Expand Up @@ -3662,19 +3824,24 @@ def compat_datetime_timedelta_total_seconds(td):

__all__ = [
'compat_Struct',
'compat_abc_ABC',
'compat_base64_b64decode',
'compat_basestring',
'compat_brotli',
'compat_builtins_dict',
'compat_casefold',
'compat_chr',
'compat_collections_abc',
'compat_collections_chain_map',
'compat_contextlib_suppress',
'compat_ctypes_WINFUNCTYPE',
'compat_datetime_timedelta_total_seconds',
'compat_dict',
'compat_dict_items',
'compat_etree_fromstring',
'compat_etree_iterfind',
'compat_filter',
'compat_filter_fns',
'compat_get_terminal_size',
'compat_getenv',
'compat_getpass_getpass',
Expand Down Expand Up @@ -3716,6 +3883,7 @@ def compat_datetime_timedelta_total_seconds(td):
'compat_struct_unpack',
'compat_subprocess_get_DEVNULL',
'compat_subprocess_Popen',
'compat_thread',
'compat_tokenize_tokenize',
'compat_urllib_error',
'compat_urllib_parse',
Expand Down
Loading