mirror of
https://github.com/ytdl-org/youtube-dl
synced 2024-12-22 20:20:09 +09:00
Fix handling of output template fields with initial '-'
This commit is contained in:
parent
a803582717
commit
51f781c75b
@ -631,6 +631,10 @@ class TestYoutubeDL(unittest.TestCase):
|
||||
'height': 1080,
|
||||
'title1': '$PATH',
|
||||
'title2': '%PATH%',
|
||||
'track': '-track with initial hyphen',
|
||||
'display_id': '-initial_hyphen',
|
||||
'episode': '.episode with initial period',
|
||||
'episode_id': '.initial_period',
|
||||
}
|
||||
|
||||
def fname(templ, na_placeholder='NA'):
|
||||
@ -646,7 +650,9 @@ class TestYoutubeDL(unittest.TestCase):
|
||||
self.assertEqual(fname(NA_TEST_OUTTMPL), 'NA-NA-1234.mp4')
|
||||
# Or by provided placeholder
|
||||
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder='none'), 'none-none-1234.mp4')
|
||||
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder=''), '--1234.mp4')
|
||||
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder=''), '_-1234.mp4')
|
||||
NA_TEST_OUTTMPL = '%(uploader_date)s+%(width)d+%(id)s.%(ext)s'
|
||||
self.assertEqual(fname(NA_TEST_OUTTMPL, na_placeholder=''), '++1234.mp4')
|
||||
self.assertEqual(fname('%(height)d.%(ext)s'), '1080.mp4')
|
||||
self.assertEqual(fname('%(height)6d.%(ext)s'), ' 1080.mp4')
|
||||
self.assertEqual(fname('%(height)-6d.%(ext)s'), '1080 .mp4')
|
||||
@ -664,6 +670,16 @@ class TestYoutubeDL(unittest.TestCase):
|
||||
self.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
|
||||
self.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
|
||||
self.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
|
||||
self.assertEqual(fname('%(track)s at start changes hyphen'),
|
||||
'_track with initial hyphen at start changes hyphen')
|
||||
self.assertEqual(fname('medial %(track)s doesn\'t change hyphen'),
|
||||
'medial -track with initial hyphen doesn\'t change hyphen')
|
||||
self.assertEqual(fname('%(display_id)s at start doesn\'t change hyphen'),
|
||||
'-initial_hyphen at start doesn\'t change hyphen')
|
||||
self.assertEqual(fname('%(episode)s at start changes period'),
|
||||
'_episode with initial period at start changes period')
|
||||
self.assertEqual(fname('%(episode_id)s at start doesn\'t change period'),
|
||||
'.initial_period at start doesn\'t change period')
|
||||
|
||||
def test_format_note(self):
|
||||
ydl = YoutubeDL()
|
||||
|
@ -146,10 +146,8 @@ class TestUtil(unittest.TestCase):
|
||||
sanitize_filename('New World record at 0:12:34'),
|
||||
'New World record at 0_12_34')
|
||||
|
||||
self.assertEqual(sanitize_filename('--gasdgf'), '_-gasdgf')
|
||||
self.assertEqual(sanitize_filename('--gasdgf', is_id=True), '--gasdgf')
|
||||
self.assertEqual(sanitize_filename('.gasdgf'), 'gasdgf')
|
||||
self.assertEqual(sanitize_filename('.gasdgf', is_id=True), '.gasdgf')
|
||||
self.assertEqual(sanitize_filename('__gas__dgf_'), 'gas_dgf')
|
||||
self.assertEqual(sanitize_filename('__gas__dgf_', is_id=True), '__gas__dgf_')
|
||||
|
||||
forbidden = '"\0\\/'
|
||||
for fc in forbidden:
|
||||
|
@ -634,6 +634,20 @@ class YoutubeDL(object):
|
||||
except UnicodeEncodeError:
|
||||
self.to_screen('[download] The file has already been downloaded')
|
||||
|
||||
# As of [1] format syntax is:
|
||||
# %[mapping_key][conversion_flags][minimum_width][.precision][length_modifier]type
|
||||
# 1. https://docs.python.org/2/library/stdtypes.html#string-formatting
|
||||
_FORMAT_RE = r'''(?x)
|
||||
(?<!%)
|
||||
%
|
||||
\({0}\) # mapping key
|
||||
(?:[#0\-+ ]+)? # conversion flags (optional)
|
||||
(?:\d+)? # minimum field width (optional)
|
||||
(?:\.\d+)? # precision (optional)
|
||||
[hlL]? # length modifier (optional)
|
||||
[diouxXeEfFgGcrs%] # conversion type
|
||||
'''
|
||||
|
||||
def prepare_filename(self, info_dict):
|
||||
"""Generate the output filename."""
|
||||
try:
|
||||
@ -652,10 +666,11 @@ class YoutubeDL(object):
|
||||
elif template_dict.get('width'):
|
||||
template_dict['resolution'] = '%dx?' % template_dict['width']
|
||||
|
||||
is_id = lambda k: k == 'id' or k.endswith('_id')
|
||||
sanitize = lambda k, v: sanitize_filename(
|
||||
compat_str(v),
|
||||
restricted=self.params.get('restrictfilenames'),
|
||||
is_id=(k == 'id' or k.endswith('_id')))
|
||||
is_id=is_id(k))
|
||||
template_dict = dict((k, v if isinstance(v, compat_numeric_types) else sanitize(k, v))
|
||||
for k, v in template_dict.items()
|
||||
if v is not None and not isinstance(v, (list, tuple, dict)))
|
||||
@ -683,21 +698,8 @@ class YoutubeDL(object):
|
||||
# output template for missing fields to meet string presentation type.
|
||||
for numeric_field in self._NUMERIC_FIELDS:
|
||||
if numeric_field not in template_dict:
|
||||
# As of [1] format syntax is:
|
||||
# %[mapping_key][conversion_flags][minimum_width][.precision][length_modifier]type
|
||||
# 1. https://docs.python.org/2/library/stdtypes.html#string-formatting
|
||||
FORMAT_RE = r'''(?x)
|
||||
(?<!%)
|
||||
%
|
||||
\({0}\) # mapping key
|
||||
(?:[#0\-+ ]+)? # conversion flags (optional)
|
||||
(?:\d+)? # minimum field width (optional)
|
||||
(?:\.\d+)? # precision (optional)
|
||||
[hlL]? # length modifier (optional)
|
||||
[diouxXeEfFgGcrs%] # conversion type
|
||||
'''
|
||||
outtmpl = re.sub(
|
||||
FORMAT_RE.format(numeric_field),
|
||||
self._FORMAT_RE.format(numeric_field),
|
||||
r'%({0})s'.format(numeric_field), outtmpl)
|
||||
|
||||
# expand_path translates '%%' into '%' and '$$' into '$'
|
||||
@ -711,7 +713,35 @@ class YoutubeDL(object):
|
||||
# because meta fields may contain env variables we don't want to
|
||||
# be expanded. For example, for outtmpl "%(title)s.%(ext)s" and
|
||||
# title "Hello $PATH", we don't want `$PATH` to be expanded.
|
||||
filename = expand_path(outtmpl).replace(sep, '') % template_dict
|
||||
filename = expand_path(outtmpl).replace(sep, '')
|
||||
|
||||
# avoid non-mandatory (#571) initial '-' (#5035): convert to '_'
|
||||
# also remove initial '.'
|
||||
|
||||
# first split the filename around the format fields
|
||||
FORMAT_SPLIT_RE = (
|
||||
'%s(%s)' % tuple(
|
||||
re.split(r'(\(\?[iLmsux]+\))',
|
||||
self._FORMAT_RE.format(r'\w+'), maxsplit=1)[1:]))
|
||||
filename = re.split(FORMAT_SPLIT_RE, filename)
|
||||
# tag any filename fragment that is an ID format
|
||||
FORMAT_FIELD_RE = (
|
||||
'%s(%s)' % tuple(
|
||||
re.split(r'(\(\?[iLmsux]+\))',
|
||||
self._FORMAT_RE.format(r'(?P<field>\w+)'), maxsplit=1)[1:]))
|
||||
field_is_id = (
|
||||
lambda x:
|
||||
(lambda y: y and is_id(y.group('field')))(re.match(FORMAT_FIELD_RE, x)))
|
||||
# list formatted fragments and tags
|
||||
tagged_fn = [(x % template_dict, field_is_id(x)) for x in filename]
|
||||
# list the initial fragments that can be munged from [.-] to _, ie
|
||||
# those that not IDs, or empty
|
||||
mungable = itertools.takewhile(lambda x: not(x[1] and x[0]), tagged_fn)
|
||||
col0 = lambda l: next(iter(zip(*l)), [])
|
||||
mungable = col0(mungable)
|
||||
# finally combine the munged initial part and the rest
|
||||
filename = (re.sub(r'^[.-]', '_', ''.join(mungable))
|
||||
+ ''.join(col0(tagged_fn[len(mungable):])))
|
||||
|
||||
# Temporary fix for #4787
|
||||
# 'Treat' all problem characters by passing filename through preferredencoding
|
||||
|
@ -2104,15 +2104,11 @@ def sanitize_filename(s, restricted=False, is_id=False):
|
||||
s = re.sub(r'[0-9]+(?::[0-9]+)+', lambda m: m.group(0).replace(':', '_'), s)
|
||||
result = ''.join(map(replace_insane, s))
|
||||
if not is_id:
|
||||
while '__' in result:
|
||||
result = result.replace('__', '_')
|
||||
result = re.sub(r'_{2,}', '_', result)
|
||||
result = result.strip('_')
|
||||
# Common case of "Foreign band name - English song title"
|
||||
if restricted and result.startswith('-_'):
|
||||
result = result[2:]
|
||||
if result.startswith('-'):
|
||||
result = '_' + result[len('-'):]
|
||||
result = result.lstrip('.')
|
||||
if not result:
|
||||
result = '_'
|
||||
return result
|
||||
|
Loading…
Reference in New Issue
Block a user