diff --git a/README.md b/README.md index c8936027f..916fe0665 100644 --- a/README.md +++ b/README.md @@ -534,14 +534,9 @@ Then simply run `make`. You can also run `make yt-dlp` instead to compile only t formats are found (default) --skip-download Do not download the video but write all related files (Alias: --no-download) - -g, --get-url Simulate, quiet but print URL - -e, --get-title Simulate, quiet but print title - --get-id Simulate, quiet but print id - --get-thumbnail Simulate, quiet but print thumbnail URL - --get-description Simulate, quiet but print video description - --get-duration Simulate, quiet but print video length - --get-filename Simulate, quiet but print output filename - --get-format Simulate, quiet but print output format + -O, --print TEMPLATE Simulate, quiet but print the given fields. + Either a field name or similar formatting + as the output template can be used -j, --dump-json Simulate, quiet but print JSON information. See "OUTPUT TEMPLATE" for a description of available keys @@ -912,7 +907,7 @@ The available fields are: - `channel_id` (string): Id of the channel - `location` (string): Physical location where the video was filmed - `duration` (numeric): Length of the video in seconds - - `duration_string` (string): Length of the video (HH-mm-ss) + - `duration_string` (string): Length of the video (HH:mm:ss) - `view_count` (numeric): How many users have watched the video on the platform - `like_count` (numeric): Number of positive ratings of the video - `dislike_count` (numeric): Number of negative ratings of the video @@ -990,6 +985,11 @@ Available for `chapter:` prefix when using `--split-chapters` for videos with in - `section_start` (numeric): Start time of the chapter in seconds - `section_end` (numeric): End time of the chapter in seconds +Available only when used in `--print`: + + - `urls` (string): The URLs of all requested formats, one in each line + - `filename` (string): Name of the video file. Note that the actual filename may be different due to post-processing. Use `--exec echo` to get the name after all postprocessing is complete + Each aforementioned sequence when referenced in an output template will be replaced by the actual value corresponding to the sequence name. Note that some of the sequences are not guaranteed to be present since they depend on the metadata obtained by a particular extractor. Such sequences will be replaced with placeholder value provided with `--output-na-placeholder` (`NA` by default). For example for `-o %(title)s-%(id)s.%(ext)s` and an mp4 video with title `yt-dlp test video` and id `BaW_jenozKcj`, this will result in a `yt-dlp test video-BaW_jenozKcj.mp4` file created in the current directory. @@ -1313,6 +1313,14 @@ These are all the deprecated options and the current alternative to achieve the #### Not recommended While these options still work, their use is not recommended since there are other alternatives to achieve the same + --get-description --print description + --get-duration --print duration_string + --get-filename --print filename + --get-format --print format + --get-id --print id + --get-thumbnail --print thumbnail + -e, --get-title --print title + -g, --get-url --print urls --all-formats -f all --all-subs --sub-langs all --write-subs --autonumber-size NUMBER Use string formatting. Eg: %(autonumber)03d diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index e7ba6248f..9fe591f75 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -177,13 +177,14 @@ class YoutubeDL(object): verbose: Print additional info to stdout. quiet: Do not print messages to stdout. no_warnings: Do not print out anything for warnings. - forceurl: Force printing final URL. - forcetitle: Force printing title. - forceid: Force printing ID. - forcethumbnail: Force printing thumbnail URL. - forcedescription: Force printing description. - forcefilename: Force printing final filename. - forceduration: Force printing duration. + forceprint: A list of templates to force print + forceurl: Force printing final URL. (Deprecated) + forcetitle: Force printing title. (Deprecated) + forceid: Force printing ID. (Deprecated) + forcethumbnail: Force printing thumbnail URL. (Deprecated) + forcedescription: Force printing description. (Deprecated) + forcefilename: Force printing final filename. (Deprecated) + forceduration: Force printing duration. (Deprecated) forcejson: Force printing info_dict as JSON. dump_single_json: Force printing the info_dict of the whole playlist (or video) as a single JSON line. @@ -820,7 +821,7 @@ class YoutubeDL(object): # duration_string template_dict['duration_string'] = ( # %(duration>%H-%M-%S)s is wrong if duration > 24hrs - formatSeconds(info_dict['duration'], '-') + formatSeconds(info_dict['duration'], '-' if sanitize else ':') if info_dict.get('duration', None) is not None else None) @@ -2206,32 +2207,43 @@ class YoutubeDL(object): return subs def __forced_printings(self, info_dict, filename, incomplete): - def print_mandatory(field): + def print_mandatory(field, actual_field=None): + if actual_field is None: + actual_field = field if (self.params.get('force%s' % field, False) - and (not incomplete or info_dict.get(field) is not None)): - self.to_stdout(info_dict[field]) + and (not incomplete or info_dict.get(actual_field) is not None)): + self.to_stdout(info_dict[actual_field]) def print_optional(field): if (self.params.get('force%s' % field, False) and info_dict.get(field) is not None): self.to_stdout(info_dict[field]) + info_dict = info_dict.copy() + if filename is not None: + info_dict['filename'] = filename + if info_dict.get('requested_formats') is not None: + # For RTMP URLs, also include the playpath + info_dict['urls'] = '\n'.join(f['url'] + f.get('play_path', '') for f in info_dict['requested_formats']) + elif 'url' in info_dict: + info_dict['urls'] = info_dict['url'] + info_dict.get('play_path', '') + + for tmpl in self.params.get('forceprint', []): + if re.match(r'\w+$', tmpl): + tmpl = '%({})s'.format(tmpl) + tmpl, info_copy = self.prepare_outtmpl(tmpl, info_dict) + self.to_stdout(tmpl % info_copy) + print_mandatory('title') print_mandatory('id') - if self.params.get('forceurl', False) and not incomplete: - if info_dict.get('requested_formats') is not None: - for f in info_dict['requested_formats']: - self.to_stdout(f['url'] + f.get('play_path', '')) - else: - # For RTMP URLs, also include the playpath - self.to_stdout(info_dict['url'] + info_dict.get('play_path', '')) + print_mandatory('url', 'urls') print_optional('thumbnail') print_optional('description') - if self.params.get('forcefilename', False) and filename is not None: - self.to_stdout(filename) + print_optional('filename') if self.params.get('forceduration', False) and info_dict.get('duration') is not None: self.to_stdout(formatSeconds(info_dict['duration'])) print_mandatory('format') + if self.params.get('forcejson', False): self.post_extract(info_dict) self.to_stdout(json.dumps(info_dict, default=repr)) diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index f1bdc1b76..16b1e9a2e 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -321,7 +321,7 @@ def _real_main(argv=None): if re.match(MetadataFromFieldPP.regex, f) is None: parser.error('invalid format string "%s" specified for --parse-metadata' % f) - any_getting = opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.getduration or opts.dumpjson or opts.dump_single_json + any_getting = opts.print or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.getduration or opts.dumpjson or opts.dump_single_json any_printing = opts.print_json download_archive_fn = expand_path(opts.download_archive) if opts.download_archive is not None else opts.download_archive @@ -508,6 +508,7 @@ def _real_main(argv=None): 'forceduration': opts.getduration, 'forcefilename': opts.getfilename, 'forceformat': opts.getformat, + 'forceprint': opts.print, 'forcejson': opts.dumpjson or opts.print_json, 'dump_single_json': opts.dump_single_json, 'force_write_download_archive': opts.force_write_download_archive, diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 0f2b77287..26e54f1f6 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -788,38 +788,45 @@ def parseOpts(overrideArguments=None): '--skip-download', '--no-download', action='store_true', dest='skip_download', default=False, help='Do not download the video but write all related files (Alias: --no-download)') + verbosity.add_option( + '-O', '--print', metavar='TEMPLATE', + action='callback', dest='print', type='str', default=[], + callback=_list_from_options_callback, callback_kwargs={'delim': None}, + help=( + 'Simulate, quiet but print the given fields. Either a field name ' + 'or similar formatting as the output template can be used')) verbosity.add_option( '-g', '--get-url', action='store_true', dest='geturl', default=False, - help='Simulate, quiet but print URL') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '-e', '--get-title', action='store_true', dest='gettitle', default=False, - help='Simulate, quiet but print title') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--get-id', action='store_true', dest='getid', default=False, - help='Simulate, quiet but print id') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--get-thumbnail', action='store_true', dest='getthumbnail', default=False, - help='Simulate, quiet but print thumbnail URL') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--get-description', action='store_true', dest='getdescription', default=False, - help='Simulate, quiet but print video description') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--get-duration', action='store_true', dest='getduration', default=False, - help='Simulate, quiet but print video length') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--get-filename', action='store_true', dest='getfilename', default=False, - help='Simulate, quiet but print output filename') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--get-format', action='store_true', dest='getformat', default=False, - help='Simulate, quiet but print output format') + help=optparse.SUPPRESS_HELP) verbosity.add_option( '-j', '--dump-json', action='store_true', dest='dumpjson', default=False,