aboutsummaryrefslogtreecommitdiffstats
path: root/yt_dlp
diff options
context:
space:
mode:
Diffstat (limited to 'yt_dlp')
-rw-r--r--yt_dlp/YoutubeDL.py4
-rw-r--r--yt_dlp/downloader/common.py2
-rw-r--r--yt_dlp/extractor/rumble.py7
-rw-r--r--yt_dlp/jsinterp.py8
-rw-r--r--yt_dlp/utils.py33
5 files changed, 29 insertions, 25 deletions
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index bf62f2820..b8c250d73 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -576,7 +576,7 @@ class YoutubeDL:
)
self._allow_colors = Namespace(**{
type_: not self.params.get('no_color') and supports_terminal_sequences(stream)
- for type_, stream in self._out_files if type_ != 'console'
+ for type_, stream in self._out_files.items_ if type_ != 'console'
})
if sys.version_info < (3, 6):
@@ -3671,7 +3671,7 @@ class YoutubeDL:
sys.getfilesystemencoding(),
self.get_encoding(),
', '.join(
- f'{key} {get_encoding(stream)}' for key, stream in self._out_files
+ f'{key} {get_encoding(stream)}' for key, stream in self._out_files.items_
if stream is not None and key != 'console')
)
diff --git a/yt_dlp/downloader/common.py b/yt_dlp/downloader/common.py
index cd30d1eff..b559e7cae 100644
--- a/yt_dlp/downloader/common.py
+++ b/yt_dlp/downloader/common.py
@@ -302,7 +302,7 @@ class FileDownloader:
)
def _report_progress_status(self, s, default_template):
- for name, style in self.ProgressStyles:
+ for name, style in self.ProgressStyles.items_:
name = f'_{name}_str'
if name not in s:
continue
diff --git a/yt_dlp/extractor/rumble.py b/yt_dlp/extractor/rumble.py
index 83b688532..8c0d0f37d 100644
--- a/yt_dlp/extractor/rumble.py
+++ b/yt_dlp/extractor/rumble.py
@@ -24,6 +24,11 @@ class RumbleEmbedIE(InfoExtractor):
'title': 'WMAR 2 News Latest Headlines | October 20, 6pm',
'timestamp': 1571611968,
'upload_date': '20191020',
+ 'channel_url': 'https://rumble.com/c/WMAR',
+ 'channel': 'WMAR',
+ 'thumbnail': 'https://sp.rmbl.ws/s8/1/5/M/z/1/5Mz1a.OvCc-small-WMAR-2-News-Latest-Headline.jpg',
+ 'duration': 234,
+ 'uploader': 'WMAR',
}
}, {
'url': 'https://rumble.com/embed/vslb7v',
@@ -38,6 +43,7 @@ class RumbleEmbedIE(InfoExtractor):
'channel': 'CTNews',
'thumbnail': 'https://sp.rmbl.ws/s8/6/7/i/9/h/7i9hd.OvCc.jpg',
'duration': 901,
+ 'uploader': 'CTNews',
}
}, {
'url': 'https://rumble.com/embed/ufe9n.v5pv5f',
@@ -96,6 +102,7 @@ class RumbleEmbedIE(InfoExtractor):
'channel': author.get('name'),
'channel_url': author.get('url'),
'duration': int_or_none(video.get('duration')),
+ 'uploader': author.get('name'),
}
diff --git a/yt_dlp/jsinterp.py b/yt_dlp/jsinterp.py
index 70857b798..56229cd99 100644
--- a/yt_dlp/jsinterp.py
+++ b/yt_dlp/jsinterp.py
@@ -24,6 +24,7 @@ _ASSIGN_OPERATORS.append(('=', (lambda cur, right: right)))
_NAME_RE = r'[a-zA-Z_$][a-zA-Z_$0-9]*'
_MATCHING_PARENS = dict(zip('({[', ')}]'))
+_QUOTES = '\'"'
class JS_Break(ExtractorError):
@@ -69,12 +70,17 @@ class JSInterpreter:
return
counters = {k: 0 for k in _MATCHING_PARENS.values()}
start, splits, pos, delim_len = 0, 0, 0, len(delim) - 1
+ in_quote, escaping = None, False
for idx, char in enumerate(expr):
if char in _MATCHING_PARENS:
counters[_MATCHING_PARENS[char]] += 1
elif char in counters:
counters[char] -= 1
- if char != delim[pos] or any(counters.values()):
+ elif not escaping and char in _QUOTES and in_quote in (char, None):
+ in_quote = None if in_quote else char
+ escaping = not escaping and in_quote and char == '\\'
+
+ if char != delim[pos] or any(counters.values()) or in_quote:
pos = 0
continue
elif pos != delim_len:
diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py
index 137d29d0a..e6e6d2759 100644
--- a/yt_dlp/utils.py
+++ b/yt_dlp/utils.py
@@ -34,6 +34,7 @@ import sys
import tempfile
import time
import traceback
+import types
import urllib.parse
import xml.etree.ElementTree
import zlib
@@ -397,14 +398,14 @@ def get_element_html_by_attribute(attribute, value, html, **kargs):
def get_elements_by_class(class_name, html, **kargs):
"""Return the content of all tags with the specified class in the passed HTML document as a list"""
return get_elements_by_attribute(
- 'class', r'[^\'"]*\b%s\b[^\'"]*' % re.escape(class_name),
+ 'class', r'[^\'"]*(?<=[\'"\s])%s(?=[\'"\s])[^\'"]*' % re.escape(class_name),
html, escape_value=False)
def get_elements_html_by_class(class_name, html):
"""Return the html of all tags with the specified class in the passed HTML document as a list"""
return get_elements_html_by_attribute(
- 'class', r'[^\'"]*\b%s\b[^\'"]*' % re.escape(class_name),
+ 'class', r'[^\'"]*(?<=[\'"\s])%s(?=[\'"\s])[^\'"]*' % re.escape(class_name),
html, escape_value=False)
@@ -3404,16 +3405,15 @@ def _match_one(filter_part, dct, incomplete):
else:
is_incomplete = lambda k: k in incomplete
- operator_rex = re.compile(r'''(?x)\s*
+ operator_rex = re.compile(r'''(?x)
(?P<key>[a-z_]+)
\s*(?P<negation>!\s*)?(?P<op>%s)(?P<none_inclusive>\s*\?)?\s*
(?:
(?P<quote>["\'])(?P<quotedstrval>.+?)(?P=quote)|
(?P<strval>.+?)
)
- \s*$
''' % '|'.join(map(re.escape, COMPARISON_OPERATORS.keys())))
- m = operator_rex.search(filter_part)
+ m = operator_rex.fullmatch(filter_part.strip())
if m:
m = m.groupdict()
unnegated_op = COMPARISON_OPERATORS[m['op']]
@@ -3449,11 +3449,10 @@ def _match_one(filter_part, dct, incomplete):
'': lambda v: (v is True) if isinstance(v, bool) else (v is not None),
'!': lambda v: (v is False) if isinstance(v, bool) else (v is None),
}
- operator_rex = re.compile(r'''(?x)\s*
+ operator_rex = re.compile(r'''(?x)
(?P<op>%s)\s*(?P<key>[a-z_]+)
- \s*$
''' % '|'.join(map(re.escape, UNARY_OPERATORS.keys())))
- m = operator_rex.search(filter_part)
+ m = operator_rex.fullmatch(filter_part.strip())
if m:
op = UNARY_OPERATORS[m.group('op')]
actual_value = dct.get(m.group('key'))
@@ -5395,23 +5394,15 @@ class classproperty:
return self.func(cls)
-class Namespace:
+class Namespace(types.SimpleNamespace):
"""Immutable namespace"""
- def __init__(self, **kwargs):
- self._dict = kwargs
-
- def __getattr__(self, attr):
- return self._dict[attr]
-
- def __contains__(self, item):
- return item in self._dict.values()
-
def __iter__(self):
- return iter(self._dict.items())
+ return iter(self.__dict__.values())
- def __repr__(self):
- return f'{type(self).__name__}({", ".join(f"{k}={v}" for k, v in self)})'
+ @property
+ def items_(self):
+ return self.__dict__.items()
# Deprecated