diff --git a/README.md b/README.md index d13eb4dc1e..897e0600e1 100644 --- a/README.md +++ b/README.md @@ -963,7 +963,7 @@ The field names themselves (the part inside the parenthesis) can also have some 1. **Date/time Formatting**: Date/time fields can be formatted according to [strftime formatting](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes) by specifying it separated from the field name using a `>`. Eg: `%(duration>%H-%M-%S)s`, `%(upload_date>%Y-%m-%d)s`, `%(epoch-3600>%H-%M-%S)s` 1. **Alternatives**: Alternate fields can be specified seperated with a `,`. Eg: `%(release_date>%Y,upload_date>%Y|Unknown)s` 1. **Default**: A literal default value can be specified for when the field is empty using a `|` seperator. This overrides `--output-na-template`. Eg: `%(uploader|Unknown)s` -1. **More Conversions**: In addition to the normal format types `diouxXeEfFgGcrs`, `B`, `j`, `l`, `q` can be used for converting to **B**ytes, **j**son, a comma seperated **l**ist and a string **q**uoted for the terminal respectively +1. **More Conversions**: In addition to the normal format types `diouxXeEfFgGcrs`, `B`, `j`, `l`, `q` can be used for converting to **B**ytes, **j**son, a comma seperated **l**ist (alternate form flag `#` makes it new line `\n` seperated) and a string **q**uoted for the terminal, respectively 1. **Unicode normalization**: The format type `U` can be used for NFC [unicode normalization](https://docs.python.org/3/library/unicodedata.html#unicodedata.normalize). The alternate form flag (`#`) changes the normalization to NFD and the conversion flag `+` can be used for NFKC/NFKD compatibility equivalence normalization. Eg: `%(title)+.100U` is NFKC To summarize, the general syntax for a field is: diff --git a/test/test_YoutubeDL.py b/test/test_YoutubeDL.py index f6483575f3..e746589450 100644 --- a/test/test_YoutubeDL.py +++ b/test/test_YoutubeDL.py @@ -765,6 +765,7 @@ class TestYoutubeDL(unittest.TestCase): # Custom type casting test('%(formats.:.id)l', 'id1, id2, id3') + test('%(formats.:.id)#l', ('id1\nid2\nid3', 'id1 id2 id3')) test('%(ext)l', 'mp4') test('%(formats.:.id) 15l', ' id1, id2, id3') test('%(formats)j', (json.dumps(FORMATS), sanitize(json.dumps(FORMATS)))) diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index a6eddd7f78..1cbe8dc8d8 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -1033,7 +1033,8 @@ class YoutubeDL(object): str_fmt = f'{fmt[:-1]}s' if fmt[-1] == 'l': # list - value, fmt = ', '.join(variadic(value)), str_fmt + delim = '\n' if '#' in (outer_mobj.group('conversion') or '') else ', ' + value, fmt = delim.join(variadic(value)), str_fmt elif fmt[-1] == 'j': # json value, fmt = json.dumps(value, default=_dumpjson_default), str_fmt elif fmt[-1] == 'q': # quoted diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py index 770d7feb9c..eba89fb8bc 100644 --- a/yt_dlp/utils.py +++ b/yt_dlp/utils.py @@ -2099,7 +2099,9 @@ def sanitize_filename(s, restricted=False, is_id=False): def replace_insane(char): if restricted and char in ACCENT_CHARS: return ACCENT_CHARS[char] - if char == '?' or ord(char) < 32 or ord(char) == 127: + elif not restricted and char == '\n': + return ' ' + elif char == '?' or ord(char) < 32 or ord(char) == 127: return '' elif char == '"': return '' if restricted else '\''