From 80983be77a4ad0eafc96b51a618d6cf254dd4518 Mon Sep 17 00:00:00 2001 From: cousteau Date: Tue, 11 Aug 2015 03:53:11 +0200 Subject: [PATCH 01/12] New output format string version --- youtube_dl/YoutubeDL.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index cad6b026e..ef4677dd1 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -566,9 +566,11 @@ class YoutubeDL(object): if v is not None) template_dict = collections.defaultdict(lambda: 'NA', template_dict) - outtmpl = sanitize_path(self.params.get('outtmpl', DEFAULT_OUTTMPL)) + outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL) tmpl = compat_expanduser(outtmpl) - filename = tmpl % template_dict + # Backwards compatibility fix (deprecated): %(foo)s -> {foo}, %% -> % + tmpl = re.sub(r'(? Date: Tue, 11 Aug 2015 03:54:37 +0200 Subject: [PATCH 02/12] New output format string version --- youtube_dl/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index e265c7574..f2b88a825 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -1657,7 +1657,7 @@ def qualities(quality_ids): return q -DEFAULT_OUTTMPL = '%(title)s-%(id)s.%(ext)s' +DEFAULT_OUTTMPL = '{title}-{id}.{ext}' def limit_length(s, length): From e45665c912d0a200f1fc27acf482367840b69622 Mon Sep 17 00:00:00 2001 From: cousteau Date: Tue, 11 Aug 2015 03:55:28 +0200 Subject: [PATCH 03/12] New output format string version --- youtube_dl/options.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 9016e3498..9004e352f 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -575,22 +575,24 @@ def parseOpts(overrideArguments=None): filesystem.add_option( '-o', '--output', dest='outtmpl', metavar='TEMPLATE', - help=('Output filename template. Use %(title)s to get the title, ' - '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, ' - '%(autonumber)s to get an automatically incremented number, ' - '%(ext)s for the filename extension, ' - '%(format)s for the format description (like "22 - 1280x720" or "HD"), ' - '%(format_id)s for the unique id of the format (like YouTube\'s itags: "137"), ' - '%(upload_date)s for the upload date (YYYYMMDD), ' - '%(extractor)s for the provider (youtube, metacafe, etc), ' - '%(id)s for the video id, ' - '%(playlist_title)s, %(playlist_id)s, or %(playlist)s (=title if present, ID otherwise) for the playlist the video is in, ' - '%(playlist_index)s for the position in the playlist. ' - '%(height)s and %(width)s for the width and height of the video format. ' - '%(resolution)s for a textual description of the resolution of the video format. ' - '%% for a literal percent. ' + help=('Output filename template. Use {title} to get the title, ' + '{uploader} for the uploader name, {uploader_id} for the uploader nickname if different, ' + '{autonumber} to get an automatically incremented number, ' + '{ext} for the filename extension, ' + '{format} for the format description (like "22 - 1280x720" or "HD"), ' + '{format_id} for the unique id of the format (like YouTube\'s itags: "137"), ' + '{upload_date} for the upload date (YYYYMMDD), ' + '{extractor} for the provider (youtube, metacafe, etc), ' + '{id} for the video id, ' + '{playlist_title}, {playlist_id}, or {playlist} (=title if present, ID otherwise) for the playlist the video is in, ' + '{playlist_index} for the position in the playlist, ' + '{height} and {width} for the width and height of the video format, ' + '{resolution} for a textual description of the resolution of the video format, ' + '{{ }} %% for literal braces and percent. ' + 'The legacy form %(title)s is also supported with the same meaning as {title}, ' + 'but this may change in a future. ' 'Use - to output to stdout. Can also be used to download to a different directory, ' - 'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .')) + 'for example with -o \'/my/downloads/{uploader}/{title}-{id}.{ext}\' .')) filesystem.add_option( '--autonumber-size', dest='autonumber_size', metavar='NUMBER', From 08db5463cd72d5e812d3266bc7f5c6fb0ce2fdb3 Mon Sep 17 00:00:00 2001 From: cousteau Date: Fri, 14 Aug 2015 20:34:02 +0200 Subject: [PATCH 04/12] Fix behavior with unavailable formatters Turns out defaultdict and str.format(**d) don't work well together; using string.Formatter().vformat() instead. My bad. --- youtube_dl/YoutubeDL.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index ef4677dd1..35931d6bc 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -17,6 +17,7 @@ import os import platform import re import shutil +import string import subprocess import socket import sys @@ -570,7 +571,7 @@ class YoutubeDL(object): tmpl = compat_expanduser(outtmpl) # Backwards compatibility fix (deprecated): %(foo)s -> {foo}, %% -> % tmpl = re.sub(r'(? Date: Sun, 18 Oct 2015 15:16:32 +0200 Subject: [PATCH 05/12] Update README.md: new --output format Updated README.md with new format and latest options for --output --- README.md | 65 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 15baf75ce..03a5c091c 100644 --- a/README.md +++ b/README.md @@ -114,17 +114,26 @@ which means you can modify it, redistribute it or use it however you like. ## Filesystem Options: -a, --batch-file FILE File containing URLs to download ('-' for stdin) --id Use only video ID in file name - -o, --output TEMPLATE Output filename template. Use %(title)s to get the title, %(uploader)s for the uploader name, %(uploader_id)s for the uploader - nickname if different, %(autonumber)s to get an automatically incremented number, %(ext)s for the filename extension, %(format)s for - the format description (like "22 - 1280x720" or "HD"), %(format_id)s for the unique id of the format (like YouTube's itags: "137"), - %(upload_date)s for the upload date (YYYYMMDD), %(extractor)s for the provider (youtube, metacafe, etc), %(id)s for the video id, - %(playlist_title)s, %(playlist_id)s, or %(playlist)s (=title if present, ID otherwise) for the playlist the video is in, - %(playlist_index)s for the position in the playlist. %(height)s and %(width)s for the width and height of the video format. - %(resolution)s for a textual description of the resolution of the video format. %% for a literal percent. Use - to output to stdout. - Can also be used to download to a different directory, for example with -o '/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s' . - --autonumber-size NUMBER Specify the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given + -o, --output TEMPLATE Output filename template. Use + {title} to get the title, + {uploader} for the uploader name, {uploader_id} for the uploader nickname if different, + {autonumber} to get an automatically incremented number, + {ext} for the filename extension, + {format} for the format description (like "22 - 1280x720" or "HD"), + {format_id} for the unique id of the format (like YouTube's itags: "137"), + {upload_date} for the upload date (YYYYMMDD), + {extractor} for the provider (youtube, metacafe, etc), + {id} for the video id, + {playlist_title}, {playlist_id}, or {playlist} (=title if present, ID otherwise) for the playlist the video is in, + {playlist_index} for the position in the playlist, + {height} and {width} for the width and height of the video format, + {resolution} for a textual description of the resolution of the video format, + {{ }} %% for literal braces and percent. + Use - to output to stdout. + Can also be used to download to a different directory, for example with -o '/my/downloads/{uploader}/{title}-{id}.{ext}' . + --autonumber-size NUMBER Specify the number of digits in {autonumber} when it is present in output filename template or --auto-number option is given --restrict-filenames Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames - -A, --auto-number [deprecated; use -o "%(autonumber)s-%(title)s.%(ext)s" ] Number downloaded files starting from 00000 + -A, --auto-number [deprecated; use -o '{autonumber}-{title}.{ext}' ] Number downloaded files starting from 00000 -t, --title [deprecated] Use title in file name (default) -l, --literal [deprecated] Alias of --title -w, --no-overwrites Do not overwrite files @@ -221,7 +230,7 @@ which means you can modify it, redistribute it or use it however you like. --embed-subs Embed subtitles in the video (only for mkv and mp4 videos) --embed-thumbnail Embed thumbnail in the audio as cover art --add-metadata Write metadata to the video file - --metadata-from-title FORMAT Parse additional metadata like song title / artist from the video title. The format syntax is the same as --output, the parsed + --metadata-from-title FORMAT Parse additional metadata like song title / artist from the video title. The format syntax is the same as --output but using `%(NAME)s` instead of `{NAME}`, the parsed parameters replace existing values. Additional templates: %(album)s, %(artist)s. Example: --metadata-from-title "%(artist)s - %(title)s" matches a title like "Coldplay - Paradise" --xattrs Write metadata to the video file's xattrs (using dublin core and xdg standards) @@ -260,27 +269,31 @@ On Windows you may also need to setup `%HOME%` environment variable manually. # OUTPUT TEMPLATE -The `-o` option allows users to indicate a template for the output file names. The basic usage is not to set any template arguments when downloading a single file, like in `youtube-dl -o funny_video.flv "http://some/video"`. However, it may contain special sequences that will be replaced when downloading each video. The special sequences have the format `%(NAME)s`. To clarify, that is a percent symbol followed by a name in parenthesis, followed by a lowercase S. Allowed names are: +The `-o` option allows users to indicate a template for the output file names. The basic usage is not to set any template arguments when downloading a single file, like in `youtube-dl -o funny_video.flv "http://some/video"`. However, it may contain special sequences that will be replaced when downloading each video. The special sequences have the format `{NAME}`. To clarify, that is a name enclosed in braces. Allowed names are: - - `id`: The sequence will be replaced by the video identifier. - - `url`: The sequence will be replaced by the video URL. - - `uploader`: The sequence will be replaced by the nickname of the person who uploaded the video. - - `upload_date`: The sequence will be replaced by the upload date in YYYYMMDD format. - - `title`: The sequence will be replaced by the video title. - - `ext`: The sequence will be replaced by the appropriate extension (like flv or mp4). - - `epoch`: The sequence will be replaced by the Unix epoch when creating the file. - - `autonumber`: The sequence will be replaced by a five-digit number that will be increased with each download, starting at zero. - - `playlist`: The name or the id of the playlist that contains the video. - - `playlist_index`: The index of the video in the playlist, a five-digit number. + - `{title}`: the video title. + - `{uploader}`, `{uploader_id}`: the uploader name / nickname. + - `{autonumber}`: an automatically incremented number. + - `{ext}`: the filename extension. + - `{format}`: the format description (like "22 - 1280x720" or "HD"). + - `{format_id}`: the unique id of the format (like YouTube's itags: "137"). + - `{upload_date}`: the upload date, in YYYYMMDD format. + - `{extractor}`: the provider (youtube, metacafe, etc). + - `{id}`: the video identifier. + - `{playlist_title}`, `{playlist_id}`, `{playlist}` (=title if present, ID otherwise): the playlist the video is in. + - `{playlist_index}`: the position in the playlist, padded with zeros. + - `{height}`, `{width}`: the width and height of the video format. + - `{resolution}`: a textual description of the resolution of the video format. + - `{{`, `}}`, `%%`: literal braces and percent. -The current default template is `%(title)s-%(id)s.%(ext)s`. +The current default template is `{title}-{id}.{ext}`. In some cases, you don't want special characters such as 中, spaces, or &, such as when transferring the downloaded filename to a Windows system or the filename through an 8bit-unsafe channel. In these cases, add the `--restrict-filenames` flag to get a shorter title: ```bash -$ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc +$ youtube-dl --get-filename -o "{title}.{ext}" 'http://youtube.com/watch?v=BaW_jenozKc' youtube-dl test video ''_ä↭𝕐.mp4 # All kinds of weird characters -$ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc --restrict-filenames +$ youtube-dl --get-filename -o "{title}.{ext}" 'http://youtube.com/watch?v=BaW_jenozKc' --restrict-filenames youtube-dl_test_video_.mp4 # A simple file name ``` @@ -430,7 +443,7 @@ From then on, after restarting your shell, you will be able to access both youtu ### How do I put downloads into a specific folder? -Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/%(title)s-%(id)s.%(ext)s"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration). +Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/{title}-{id}.{ext}"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration). ### How do I download a video starting with a `-` ? From 588f7f8d6d2ee3dc4f7c6157a8f85fecbe6953b0 Mon Sep 17 00:00:00 2001 From: cousteau Date: Sun, 18 Oct 2015 18:51:56 +0200 Subject: [PATCH 06/12] Update options.py Fix help strings in options.py to reflect the new --output {format} --- youtube_dl/options.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 9004e352f..983911817 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -596,7 +596,7 @@ def parseOpts(overrideArguments=None): filesystem.add_option( '--autonumber-size', dest='autonumber_size', metavar='NUMBER', - help='Specify the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given') + help='Specify the number of digits in {autonumber} when it is present in output filename template or --auto-number option is given') filesystem.add_option( '--restrict-filenames', action='store_true', dest='restrictfilenames', default=False, @@ -604,7 +604,7 @@ def parseOpts(overrideArguments=None): filesystem.add_option( '-A', '--auto-number', action='store_true', dest='autonumber', default=False, - help='[deprecated; use -o "%(autonumber)s-%(title)s.%(ext)s" ] Number downloaded files starting from 00000') + help='[deprecated; use -o "{autonumber}-{title}.{ext}" ] Number downloaded files starting from 00000') filesystem.add_option( '-t', '--title', action='store_true', dest='usetitle', default=False, @@ -722,7 +722,8 @@ def parseOpts(overrideArguments=None): '--metadata-from-title', metavar='FORMAT', dest='metafromtitle', help='Parse additional metadata like song title / artist from the video title. ' - 'The format syntax is the same as --output, ' + 'The format syntax is the same as --output ' + 'but using "%(NAME)s" instead of "{NAME}"; ' 'the parsed parameters replace existing values. ' 'Additional templates: %(album)s, %(artist)s. ' 'Example: --metadata-from-title "%(artist)s - %(title)s" matches a title like ' From 95d9e485470f5637835326ef0667fece663b8097 Mon Sep 17 00:00:00 2001 From: cousteau Date: Sun, 18 Oct 2015 20:05:37 +0200 Subject: [PATCH 07/12] __init__.py -> new --output format + warn if old - Edited __init__.py so that it uses the new {NAME} rather than the old %(NAME)s --output format - Moved the backwards compatibility code from YoutubeDL.py to __init__.py - Display a warning if the old format is used (using sys.stderr.write(); this is probably not the way to go...) --- youtube_dl/__init__.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 55b22c889..54c0058a4 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -9,6 +9,7 @@ import codecs import io import os import random +import re import shlex import sys @@ -189,18 +190,25 @@ def _real_main(argv=None): if opts.allsubtitles and not opts.writeautomaticsub: opts.writesubtitles = True - outtmpl = ((opts.outtmpl is not None and opts.outtmpl) or - (opts.format == '-1' and opts.usetitle and '%(title)s-%(id)s-%(format)s.%(ext)s') or - (opts.format == '-1' and '%(id)s-%(format)s.%(ext)s') or - (opts.usetitle and opts.autonumber and '%(autonumber)s-%(title)s-%(id)s.%(ext)s') or - (opts.usetitle and '%(title)s-%(id)s.%(ext)s') or - (opts.useid and '%(id)s.%(ext)s') or - (opts.autonumber and '%(autonumber)s-%(id)s.%(ext)s') or - DEFAULT_OUTTMPL) - if not os.path.splitext(outtmpl)[1] and opts.extractaudio: + outtmpl = (opts.outtmpl if opts.outtmpl is not None + else '{title}-{id}-{format}.{ext}' if opts.format == '-1' and opts.usetitle + else '{id}-{format}.{ext}' if opts.format == '-1' + else '{autonumber}-{title}-{id}.{ext}' if opts.usetitle and opts.autonumber + else '{autonumber}-{title}-{id}.{ext}' if opts.usetitle + else '{id}.{ext}' if opts.useid + else '{autonumber}-{id}.{ext}' if opts.autonumber + else DEFAULT_OUTTMPL) + # Backwards compatibility fix (deprecated): %(foo)s -> {foo}, %% -> % + outtmpl2 = re.sub(r'(? Date: Sun, 18 Oct 2015 20:06:54 +0200 Subject: [PATCH 08/12] __init__.py -> new --output format + warn if old - Edited __init__.py so that it uses the new {NAME} rather than the old %(NAME)s --output format - Moved the backwards compatibility code from YoutubeDL.py to __init__.py - Display a warning if the old format is used --- youtube_dl/YoutubeDL.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 35931d6bc..ff0148d2e 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -546,8 +546,7 @@ class YoutubeDL(object): autonumber_size = self.params.get('autonumber_size') if autonumber_size is None: autonumber_size = 5 - autonumber_templ = '%0' + str(autonumber_size) + 'd' - template_dict['autonumber'] = autonumber_templ % self._num_downloads + template_dict['autonumber'] = '%0*d' % (autonumber_size, self._num_downloads) if template_dict.get('playlist_index') is not None: template_dict['playlist_index'] = '%0*d' % (len(str(template_dict['n_entries'])), template_dict['playlist_index']) if template_dict.get('resolution') is None: @@ -568,10 +567,7 @@ class YoutubeDL(object): template_dict = collections.defaultdict(lambda: 'NA', template_dict) outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL) - tmpl = compat_expanduser(outtmpl) - # Backwards compatibility fix (deprecated): %(foo)s -> {foo}, %% -> % - tmpl = re.sub(r'(? Date: Mon, 19 Oct 2015 01:50:25 +0200 Subject: [PATCH 09/12] Format specs for {autonumber} and {upload_date} {autonumber} and {playlist_index} are now integers rather than strings, so {autonumber:02} works and you don't need --autonumber-size. (Actually, they're "special integers" that get printed to a predefined fixed width if you leave the :02 out; see the _Int_digits class.) {upload_date} is now a datetime.date, so {upload_date:%b %Y} will format as "Oct 2015" (default is "2015-10-19"). sanitize() gets applied only to string values, not to numeric ones (or dates or lists or...) --- youtube_dl/YoutubeDL.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index ff0148d2e..848ca1a35 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -29,6 +29,7 @@ if os.name == 'nt': import ctypes from .compat import ( + compat_basestring, compat_cookiejar, compat_expanduser, compat_get_terminal_size, @@ -90,6 +91,19 @@ from .postprocessor import ( from .version import __version__ +class _Int_digits(int): + """int-like type that gets formatted with a default fixed width + (to be used within YoutubeDL.prepare_filename() so that {autonumber:03} + prints 001 but {autonumber} defaults to 00001 rather than 1) + """ + def __new__(cls, value, digits=0): + obj = int.__new__(cls, value) + obj.digits = digits + return obj + def __str__(self): + return "{0:0{1}}".format(self, self.digits) + + class YoutubeDL(object): """YoutubeDL class. @@ -546,9 +560,9 @@ class YoutubeDL(object): autonumber_size = self.params.get('autonumber_size') if autonumber_size is None: autonumber_size = 5 - template_dict['autonumber'] = '%0*d' % (autonumber_size, self._num_downloads) + template_dict['autonumber'] = _Int_digits(self._num_downloads, autonumber_size) if template_dict.get('playlist_index') is not None: - template_dict['playlist_index'] = '%0*d' % (len(str(template_dict['n_entries'])), template_dict['playlist_index']) + template_dict['playlist_index'] = _Int_digits(template_dict['playlist_index'], len(str(template_dict['n_entries']))) if template_dict.get('resolution') is None: if template_dict.get('width') and template_dict.get('height'): template_dict['resolution'] = '%dx%d' % (template_dict['width'], template_dict['height']) @@ -556,11 +570,16 @@ class YoutubeDL(object): template_dict['resolution'] = '%sp' % template_dict['height'] elif template_dict.get('width'): template_dict['resolution'] = '?x%d' % template_dict['width'] + if template_dict.get('upload_date') is not None: + template_dict['upload_date'] = datetime.datetime.strptime(template_dict['upload_date'],"%Y%m%d").date() - sanitize = lambda k, v: sanitize_filename( - compat_str(v), - restricted=self.params.get('restrictfilenames'), - is_id=(k == 'id')) + def sanitize(k, v): + if isinstance(v, compat_basestring): + return sanitize_filename(compat_str(v), + restricted = self.params.get('restrictfilenames'), + is_id = (k == 'id') ) + else: + return v # leave non-strings untouched template_dict = dict((k, sanitize(k, v)) for k, v in template_dict.items() if v is not None) From 946745f068a5c78ddc3f7c11e325a22eb29283b0 Mon Sep 17 00:00:00 2001 From: cousteau Date: Mon, 19 Oct 2015 01:53:07 +0200 Subject: [PATCH 10/12] Format specs for {autonumber} and {upload_date} {autonumber} and {playlist_index} are now integers rather than strings, so {autonumber:02} works and you don't need --autonumber-size. (Actually, they're "special integers" that get printed to a predefined fixed width if you leave the :02 out; see the _Int_digits class.) {upload_date} is now a datetime.date, so {upload_date:%b %Y} will format as "Oct 2015" (default is "2015-10-19"). sanitize() gets applied only to string values, not to numeric ones (or dates or lists or...) --- youtube_dl/options.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 983911817..f48dc3763 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -276,7 +276,7 @@ def parseOpts(overrideArguments=None): 'For example, to only match videos that have been liked more than ' '100 times and disliked less than 50 times (or the dislike ' 'functionality is not available at the given service), but who ' - 'also have a description, use --match-filter ' + 'also have a description, use --match-filter ' '"like_count > 100 & dislike_count Date: Sat, 24 Oct 2015 18:46:41 +0200 Subject: [PATCH 11/12] Fix {playlist_index:03} breaking if unavailable Using the 'NA' string when an item is unavailable would break with format specifiers such as {playlist_index:03} or {upload_date:%Y} since strings don't allow those formatters. Instead of using 'NA', use a class that always returns 'NA' on str() or format(). (The str() part can probably be removed; it's just in case %s is used somewhere else in the code, now or in a future.) Also, renamed class _Int_digits to a more meaningful and consistent _Int_formatter. --- youtube_dl/YoutubeDL.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 848ca1a35..d69cd32f8 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -91,7 +91,9 @@ from .postprocessor import ( from .version import __version__ -class _Int_digits(int): +# Special formatting classes to be used in output file name formatting + +class _Int_formatter(int): """int-like type that gets formatted with a default fixed width (to be used within YoutubeDL.prepare_filename() so that {autonumber:03} prints 001 but {autonumber} defaults to 00001 rather than 1) @@ -103,6 +105,15 @@ class _Int_digits(int): def __str__(self): return "{0:0{1}}".format(self, self.digits) +class _NA_formatter(object): + """Class that always yields 'NA' when formatted as %s or {} + to prevent stuff such as {upload_date:%Y} from yielding errors""" + def __format__(self, fmt): + return 'NA' + def __str__(self, fmt): + return 'NA' # just in case; can probably be removed + + class YoutubeDL(object): """YoutubeDL class. @@ -560,9 +571,9 @@ class YoutubeDL(object): autonumber_size = self.params.get('autonumber_size') if autonumber_size is None: autonumber_size = 5 - template_dict['autonumber'] = _Int_digits(self._num_downloads, autonumber_size) + template_dict['autonumber'] = _Int_formatter(self._num_downloads, autonumber_size) if template_dict.get('playlist_index') is not None: - template_dict['playlist_index'] = _Int_digits(template_dict['playlist_index'], len(str(template_dict['n_entries']))) + template_dict['playlist_index'] = _Int_formatter(template_dict['playlist_index'], len(str(template_dict['n_entries']))) if template_dict.get('resolution') is None: if template_dict.get('width') and template_dict.get('height'): template_dict['resolution'] = '%dx%d' % (template_dict['width'], template_dict['height']) @@ -583,7 +594,7 @@ class YoutubeDL(object): template_dict = dict((k, sanitize(k, v)) for k, v in template_dict.items() if v is not None) - template_dict = collections.defaultdict(lambda: 'NA', template_dict) + template_dict = collections.defaultdict(_NA_formatter, template_dict) outtmpl = self.params.get('outtmpl', DEFAULT_OUTTMPL) filename = sanitize_path(string.Formatter().vformat(outtmpl, None, template_dict)) From 916aaebb13cf34f9478d1cd85a9e2c3d86628c5f Mon Sep 17 00:00:00 2001 From: cousteau Date: Sun, 27 Dec 2015 19:21:18 +0100 Subject: [PATCH 12/12] Remove fmt arg from _NA_formatter.__str__() Copy-paste fail. __str__() doesn't take a fmt argument as __format__() does. --- youtube_dl/YoutubeDL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index d69cd32f8..47c52d520 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -110,7 +110,7 @@ class _NA_formatter(object): to prevent stuff such as {upload_date:%Y} from yielding errors""" def __format__(self, fmt): return 'NA' - def __str__(self, fmt): + def __str__(self): return 'NA' # just in case; can probably be removed