2025, Nov 27 00:03

Как сохранить корейские названия yt-dlp на Windows при перенаправлении вывода

Разбираем, почему при перенаправлении в Windows теряется хангыль в выводе yt-dlp, и как это исправить: кодировка (UTF-8, cp949), --encoding и Python API.

Юникод в Windows по-прежнему полон ловушек, когда инструменты смешивают корейский и латиницу. Типичный сценарий: yt-dlp корректно печатает названия плейлистов в консоли, но стоит перенаправить вывод в файл или перехватить его из Python — хангыль пропадает или превращается в «заглушки». Секрет в том, чтобы понять, что делает Windows во время перенаправления, и сразу заставить yt-dlp работать в правильной кодировке.

Как воспроизвести проблему

В cmd на Windows можно вывести названия плейлиста YouTube с корейскими и латинскими символами с помощью yt-dlp. В консоли заголовки отображаются корректно при кодовой странице 949, но при перенаправлении вывода — уже нет.

yt-dlp --flat-playlist -i --print title PLySOINx0fqvYr6s8aGdqaK9j8_CAWcP5U

Перенаправьте тот же вывод в файл — и корейский исчезает:

yt-dlp --flat-playlist -i --print title PLySOINx0fqvYr6s8aGdqaK9j8_CAWcP5U > out.txt 2>&1

Перехват через Python ведёт себя так же: символы пропадают в полученной строке, даже если в консоли они видны:

import os
import subprocess

charset = "cp949"
os.environ["PYTHONIOENCODING"] = charset

cmdline = "yt-dlp --flat-playlist -i --print title PLySOINx0fqvYr6s8aGdqaK9j8_CAWcP5U"
result = subprocess.run(cmdline.split(), capture_output=True, text=True, shell=False, encoding=charset)
print((result.stdout, result.stderr))

Что происходит на самом деле

Оператор перенаправления > преобразует текст к кодовой странице консоли.

Именно на этом этапе теряются символы, если активная кодовая страница не может их представить. Минимальная демонстрация в wineconsole показывает это наглядно:

Z:\home\lmc\tmp>chcp
Active code page: 437

Z:\home\lmc\tmp>echo 철갑혹성
철갑혹성

Z:\home\lmc\tmp>echo 철갑혹성 > k.out

Z:\home\lmc\tmp>type k.out
????

Z:\home\lmc\tmp>chcp 949
Active code page: 949

Z:\home\lmc\tmp>echo 철갑혹성 > k.out

Z:\home\lmc\tmp>type k.out
철갑혹성

Когда кодовая страница не умеет сопоставлять часть символов, вы получаете либо вопросительные знаки, либо полное удаление — зависит от того, как обработчик обращается с непредставимыми знаками. Перевести все символы cp949 в cp437 невозможно. Если пытаются транслитерировать, появляются подстановки вроде вопросительных знаков; если неперекодируемые символы игнорируются — они исчезают, что и соответствует наблюдаемому поведению.

Важно и то, что ситуация не универсальна для всех платформ. В Linux Mint запись в файл в том же сценарии работала корректно; а другой терминал — например, kitty, alacritty или warp — может повлиять на отображение. Но суть проблемы Windows остаётся прежней: преобразование в кодировку консоли происходит именно во время перенаправления.

Решение: укажите yt-dlp нужную кодировку

Надёжнее всего заставить yt-dlp выводить текст в предсказуемой кодировке, которая покрывает ваши символы, и записывать этот текст напрямую, без потерь на промежуточном преобразовании. Параметр кодировки в самом yt-dlp устраняет двусмысленность. После этого всё правильно отображается в терминале, в файлах и при захвате строк — и UTF-8, и cp949 работают без проблем.

Один из вариантов — записывать перенаправленный вывод из Python, принудительно задав кодировку yt-dlp:

import subprocess

codec = "cp949"
outfile = open("out.txt", "wb")
subprocess.run(
    f"yt-dlp --encoding '{codec}' --flat-playlist -i --print title PLySOINx0fqvYr6s8aGdqaK9j8_CAWcP5U".split(),
    stdout=outfile,
    encoding=codec,
)

После этого при кодовой странице консоли 949 можно открыть файл и увидеть корректные корейские названия:

wineconsole 2>/dev/null
Microsoft Windows 10.0.2600

Z:\home\lmc\tmp>chcp 949
Active code page: 949

Z:\home\lmc\tmp>type out.txt
Vague (feat. Hey)
새벽 한 시
천 개의 태양
Wish
...

Ещё аккуратнее — вовсе не использовать subprocess, а подключить yt-dlp как Python‑модуль, чтобы уйти от консольного перенаправления и его неявной перекодировки. Библиотека предоставляет опции, повторяющие CLI, и позволяет явно управлять кодировкой.

import yt_dlp

src_url = "https://www.youtube.com/playlist?list=PLySOINx0fqvYr6s8aGdqaK9j8_CAWcP5U"
codec = "cp949"
accum = ""

options = {
    "extract_flat": True,
    "playlist_items": "1-5",
    "encoding": codec,
}

with yt_dlp.YoutubeDL(options) as loader:
    info = loader.extract_info(src_url, download=False)
    for entry in info["entries"]:
        accum += f"{entry['title']}\n"

with open("out.txt", "wb") as handle:
    handle.write(accum.encode(codec))

Почему это важно

Как только вывод перенаправлен, Windows применяет кодовую страницу консоли, и это преобразование может оказаться «с потерями» для многоязычного текста. Если целевая кодовая страница не поддерживает корейский, вы видите подстановки или исчезновение исходных символов. Так и выходит, что заголовки печатаются в консоли, но в файлах или захваченных строках оказываются урезанными. Контроль кодировки у источника предотвращает тихую порчу данных и делает автоматизацию надёжной.

Итоги и практические рекомендации

Если вы перенаправляете или программно перехватываете вывод yt-dlp в Windows и хотите сохранить корейский рядом с латиницей, явно задайте подходящую кодировку параметром --encoding и записывайте вывод напрямую. Использование Python API вообще избавляет от консольного перенаправления и держит единый путь данных. Где возможно, просматривайте и обрабатывайте текст в UTF-8 или cp949 — так вы избегаете «дыр» в сопоставлении, из‑за которых появляются вопросительные знаки или пропадают символы. В Linux Mint та же операция корректно писала в файл; терминалы различаются, но принцип неизменен: контролируйте кодировку от начала до конца — и символы не потеряются.