1
0
Fork 0
mirror of https://github.com/ytdl-org/youtube-dl.git synced 2024-05-19 03:29:32 +00:00
This commit is contained in:
Maximilian Teegen 2024-04-17 01:35:52 +00:00 committed by GitHub
commit dd3b08ec43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 27 deletions

View file

@ -160,27 +160,33 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
than COUNT views than COUNT views
--max-views COUNT Do not download any videos with more --max-views COUNT Do not download any videos with more
than COUNT views than COUNT views
--match-filter FILTER Generic video filter. Specify any key --match-filter FILTER Generic video filter. Specify any key (see
(see the "OUTPUT TEMPLATE" for a list the "OUTPUT TEMPLATE" for a list of
of available keys) to match if the key available keys) to match if the key is
is present, !key to check if the key is present, !key to check if the key is not
not present, key > NUMBER (like present, key > NUMBER (like "comment_count
"comment_count > 12", also works with > 12", also works with >=, <, <=, !=, =) to
>=, <, <=, !=, =) to compare against a compare against a number, key = 'LITERAL'
number, key = 'LITERAL' (like "uploader (like "uploader = 'Mike Smith'", also works
= 'Mike Smith'", also works with !=) to with !=) to match against a string literal
match against a string literal and & to and & to require multiple matches. Values
require multiple matches. Values which which are not known are excluded unless you
are not known are excluded unless you put a question mark (?) after the operator.
put a question mark (?) after the For example, to only match videos that have
operator. For example, to only match been liked more than 100 times and disliked
videos that have been liked more than less than 50 times (or the dislike
100 times and disliked less than 50 functionality is not available at the given
times (or the dislike functionality is service), but who also have a description,
not available at the given service), use --match-filter "like_count > 100 &
but who also have a description, use
--match-filter "like_count > 100 &
dislike_count <? 50 & description" . dislike_count <? 50 & description" .
For matching strings, the oparators ~= and
!~= check for string containment and
exclusion. The operators *= and !*= search
for a regular expression.
For example, to only match videos which
have neither 'sponsored' nor 'Sponsored' in
the title, use --match-filter "title !*=
'[Ss]ponsored'".
--no-playlist Download only the video, if the URL --no-playlist Download only the video, if the URL
refers to a video and a playlist. refers to a video and a playlist.
--yes-playlist Download the playlist, if the URL --yes-playlist Download the playlist, if the URL

View file

@ -1334,7 +1334,6 @@ ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4')
'9999 51') '9999 51')
def test_match_str(self): def test_match_str(self):
self.assertRaises(ValueError, match_str, 'xy>foobar', {})
self.assertFalse(match_str('xy', {'x': 1200})) self.assertFalse(match_str('xy', {'x': 1200}))
self.assertTrue(match_str('!xy', {'x': 1200})) self.assertTrue(match_str('!xy', {'x': 1200}))
self.assertTrue(match_str('x', {'x': 1200})) self.assertTrue(match_str('x', {'x': 1200}))
@ -1351,6 +1350,17 @@ ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4')
self.assertTrue(match_str('y=foobar42', {'y': 'foobar42'})) self.assertTrue(match_str('y=foobar42', {'y': 'foobar42'}))
self.assertFalse(match_str('y!=foobar42', {'y': 'foobar42'})) self.assertFalse(match_str('y!=foobar42', {'y': 'foobar42'}))
self.assertTrue(match_str('y!=foobar2', {'y': 'foobar42'})) self.assertTrue(match_str('y!=foobar2', {'y': 'foobar42'}))
self.assertTrue(match_str('y^=foo', {'y': 'foobar42'}))
self.assertFalse(match_str('y!^=foo', {'y': 'foobar42'}))
self.assertFalse(match_str('y^=bar', {'y': 'foobar42'}))
self.assertTrue(match_str('y!^=bar', {'y': 'foobar42'}))
self.assertRaises(ValueError, match_str, 'x^=42', {'x': 42})
self.assertTrue(match_str('y*=bar', {'y': 'foobar42'}))
self.assertFalse(match_str('y!*=bar', {'y': 'foobar42'}))
self.assertFalse(match_str('y*=baz', {'y': 'foobar42'}))
self.assertTrue(match_str('y!*=baz', {'y': 'foobar42'}))
self.assertTrue(match_str('y$=42', {'y': 'foobar42'}))
self.assertFalse(match_str('y$=43', {'y': 'foobar42'}))
self.assertFalse(match_str( self.assertFalse(match_str(
'like_count > 100 & dislike_count <? 50 & description', 'like_count > 100 & dislike_count <? 50 & description',
{'like_count': 90, 'description': 'foo'})) {'like_count': 90, 'description': 'foo'}))

View file

@ -4814,11 +4814,13 @@ def _match_one(filter_part, dct):
'>': operator.gt, '>': operator.gt,
'>=': operator.ge, '>=': operator.ge,
'=': operator.eq, '=': operator.eq,
'!=': operator.ne, '*=': operator.contains,
'^=': lambda attr, value: attr.startswith(value),
'$=': lambda attr, value: attr.endswith(value),
} }
operator_rex = re.compile(r'''(?x)\s* operator_rex = re.compile(r'''(?x)\s*
(?P<key>[a-z_]+) (?P<key>[a-z_]+)
\s*(?P<op>%s)(?P<none_inclusive>\s*\?)?\s* \s*(?P<negation>!\s*)?(?P<op>%s)(?P<none_inclusive>\s*\?)?\s*
(?: (?:
(?P<intval>[0-9.]+(?:[kKmMgGtTpPeEzZyY]i?[Bb]?)?)| (?P<intval>[0-9.]+(?:[kKmMgGtTpPeEzZyY]i?[Bb]?)?)|
(?P<quote>["\'])(?P<quotedstrval>(?:\\.|(?!(?P=quote)|\\).)+?)(?P=quote)| (?P<quote>["\'])(?P<quotedstrval>(?:\\.|(?!(?P=quote)|\\).)+?)(?P=quote)|
@ -4828,7 +4830,11 @@ def _match_one(filter_part, dct):
''' % '|'.join(map(re.escape, COMPARISON_OPERATORS.keys()))) ''' % '|'.join(map(re.escape, COMPARISON_OPERATORS.keys())))
m = operator_rex.search(filter_part) m = operator_rex.search(filter_part)
if m: if m:
op = COMPARISON_OPERATORS[m.group('op')] unnegated_op = COMPARISON_OPERATORS[m.group('op')]
if m.group('negation'):
op = lambda attr, value: not unnegated_op(attr, value)
else:
op = unnegated_op
actual_value = dct.get(m.group('key')) actual_value = dct.get(m.group('key'))
if (m.group('quotedstrval') is not None if (m.group('quotedstrval') is not None
or m.group('strval') is not None or m.group('strval') is not None
@ -4838,14 +4844,14 @@ def _match_one(filter_part, dct):
# https://github.com/ytdl-org/youtube-dl/issues/11082). # https://github.com/ytdl-org/youtube-dl/issues/11082).
or actual_value is not None and m.group('intval') is not None or actual_value is not None and m.group('intval') is not None
and isinstance(actual_value, compat_str)): and isinstance(actual_value, compat_str)):
if m.group('op') not in ('=', '!='):
raise ValueError(
'Operator %s does not support string values!' % m.group('op'))
comparison_value = m.group('quotedstrval') or m.group('strval') or m.group('intval') comparison_value = m.group('quotedstrval') or m.group('strval') or m.group('intval')
quote = m.group('quote') quote = m.group('quote')
if quote is not None: if quote is not None:
comparison_value = comparison_value.replace(r'\%s' % quote, quote) comparison_value = comparison_value.replace(r'\%s' % quote, quote)
else: else:
if m.group('op') in ('*=', '^=', '$='):
raise ValueError(
'Operator %s only supports string values!' % m.group('op'))
try: try:
comparison_value = int(m.group('intval')) comparison_value = int(m.group('intval'))
except ValueError: except ValueError: