aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--yt_dlp/YoutubeDL.py15
-rw-r--r--yt_dlp/minicurses.py1
-rw-r--r--yt_dlp/options.py3
-rw-r--r--yt_dlp/utils.py12
5 files changed, 28 insertions, 7 deletions
diff --git a/README.md b/README.md
index d401acb21..ca931aba3 100644
--- a/README.md
+++ b/README.md
@@ -451,7 +451,9 @@ You can also fork the project on github and run your fork's [build workflow](.gi
those that have a like count more than 100
(or the like field is not available) and
also has a description that contains the
- phrase "cats & dogs" (ignoring case)
+ phrase "cats & dogs" (ignoring case). Use
+ "--match-filter -" to interactively ask
+ whether to download each video
--no-match-filter Do not use generic video filter (default)
--no-playlist Download only the video, if the URL refers
to a video and a playlist
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index 4351699b6..78345f87a 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -413,6 +413,8 @@ class YoutubeDL:
every video.
If it returns a message, the video is ignored.
If it returns None, the video is downloaded.
+ If it returns utils.NO_DEFAULT, the user is interactively
+ asked whether to download the video.
match_filter_func in utils.py is one example for this.
no_color: Do not emit color codes in output.
geo_bypass: Bypass geographic restriction via faking X-Forwarded-For
@@ -878,6 +880,7 @@ class YoutubeDL:
Styles = Namespace(
HEADERS='yellow',
EMPHASIS='light blue',
+ FILENAME='green',
ID='green',
DELIM='blue',
ERROR='red',
@@ -1303,7 +1306,17 @@ class YoutubeDL:
except TypeError:
# For backward compatibility
ret = None if incomplete else match_filter(info_dict)
- if ret is not None:
+ if ret is NO_DEFAULT:
+ while True:
+ filename = self._format_screen(self.prepare_filename(info_dict), self.Styles.FILENAME)
+ reply = input(self._format_screen(
+ f'Download "{filename}"? (Y/n): ', self.Styles.EMPHASIS)).lower().strip()
+ if reply in {'y', ''}:
+ return None
+ elif reply == 'n':
+ return f'Skipping {video_title}'
+ return True
+ elif ret is not None:
return ret
return None
diff --git a/yt_dlp/minicurses.py b/yt_dlp/minicurses.py
index 9fd679a48..a867fd289 100644
--- a/yt_dlp/minicurses.py
+++ b/yt_dlp/minicurses.py
@@ -69,6 +69,7 @@ def format_text(text, f):
raise SyntaxError(f'Invalid format {" ".join(tokens)!r} in {f!r}')
if fg_color or bg_color:
+ text = text.replace(CONTROL_SEQUENCES['RESET'], f'{fg_color}{bg_color}')
return f'{fg_color}{bg_color}{text}{CONTROL_SEQUENCES["RESET"]}'
else:
return text
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index 73bc88b89..725ab89db 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -471,7 +471,8 @@ def create_parser():
'!is_live --match-filter "like_count>?100 & description~=\'(?i)\\bcats \\& dogs\\b\'" '
'matches only videos that are not live OR those that have a like count more than 100 '
'(or the like field is not available) and also has a description '
- 'that contains the phrase "cats & dogs" (ignoring case)'))
+ 'that contains the phrase "cats & dogs" (ignoring case). '
+ 'Use "--match-filter -" to interactively ask whether to download each video'))
selection.add_option(
'--no-match-filter',
metavar='FILTER', dest='match_filter', action='store_const', const=None,
diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py
index 7faee62ac..0612139e0 100644
--- a/yt_dlp/utils.py
+++ b/yt_dlp/utils.py
@@ -3407,11 +3407,15 @@ def match_str(filter_str, dct, incomplete=False):
def match_filter_func(filters):
if not filters:
return None
- filters = variadic(filters)
+ filters = set(variadic(filters))
- def _match_func(info_dict, *args, **kwargs):
- if any(match_str(f, info_dict, *args, **kwargs) for f in filters):
- return None
+ interactive = '-' in filters
+ if interactive:
+ filters.remove('-')
+
+ def _match_func(info_dict, incomplete=False):
+ if not filters or any(match_str(f, info_dict, incomplete) for f in filters):
+ return NO_DEFAULT if interactive and not incomplete else None
else:
video_title = info_dict.get('title') or info_dict.get('id') or 'video'
filter_str = ') | ('.join(map(str.strip, filters))