Привет, Хабр!

Все мы любим YouTube, но иногда хочется сохранить видео для просмотра офлайн, отправить другу в Telegram или просто вырезать интересный момент. И тут мы сталкиваемся с реальностью: десятки сайтов-загрузчиков, которые завалены агрессивной рекламой, всплывающими окнами и капчами. Простая задача — скачать ролик — превращается в пятиминутный квест. А если нужно скачать несколько видео, это становится настоящей головной болью.

Как разработчикам, нам под силу решить эту проблему раз и навсегда. Сегодня мы создадим собственный консольный загрузчик на .NET, который будет делать ровно то, что нужно, и ничего лишнего. В этой статье мы не только напишем рабочий код, но и разберемся, с какими подводными камнями можно столкнуться на этом пути, чтобы вы не тратили свое время.

Наша цель — написать утилиту, которая:

  • Скачивает видео по ссылке.

  • Позволяет выбрать качество через простой конфиг-файл.

  • Автоматически объединяет видео с лучшим доступным звуком.

Звучит неплохо? Тогда приступим.

Наш арсенал: выбираем правильные инструменты

Чтобы не изобретать велосипед, мы воспользуемся несколькими мощными и популярными библиотеками. Для красивого вывода в консоль возьмем Spectre.Console, а для основной магии — YoutubeExplode.

YoutubeExplode — это сердце нашего проекта. Она умеет "разбирать" страницы YouTube, получая доступ к метаданным и прямым ссылкам на видео- и аудиопотоки. И все это без официального API и связанных с ним квот. Вместе с дополнением YoutubeExplode.Converter она становится универсальным комбайном, который сам скачает и объединит нужные потоки.

Шаг 1: Настройка проекта и конфигурации

Создаем новый консольный проект (dotnet new console) и подключаем необходимые зависимости:

dotnet add package YoutubeExplode
dotnet add package YoutubeExplode.Converter
dotnet add package Spectre.Console
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.Binder

А чтобы сделать наш загрузчик гибким, воспользуемся стандартным для .NET механизмом конфигурации Microsoft.Extensions.Configuration. Вынесем настройки в appsettings.json. Это позволит пользователю легко менять качество видео и путь сохранения, не пересобирая программу.

appsettings.json

{
  "Settings": {
    "PreferredQuality": 720,
    // (здесь {YourUserName} нужно заменить на ваше имя пользователя в системе)
    "OutputPath": "C:\\Users\\{YourUserName}\\Downloads"
  }
}

Шаг 2: Получение информации о видео и выбор качества

После того как пользователь вставил ссылку, YoutubeExplode вступает в игру. Сначала мы получаем всю информацию о видео: название, длительность и, самое главное, "манифест" — список всех доступных потоков (стримов).

var youtube = new YoutubeClient();
var video = await youtube.Videos.GetAsync(videoUrl);
var streamManifest = await youtube.Videos.Streams.GetManifestAsync(video.Id);

Далее нам нужно выбрать подходящие видео и аудиопотоки. Может возникнуть вопрос: "А почему бы не взять готовый поток, где уже есть и видео, и звук?". Раньше YouTube так и делал, но сейчас, для оптимизации трафика, видео высокого качества (720p и выше) и аудио почти всегда передаются раздельно, а плеер объединяет их "на лету".

Для нас это даже плюс: мы можем выбрать видео нужного качества и независимо от него — аудио с самым высоким битрейтом.

Начнем с видео. Наша логика проста: сначала ищем поток с качеством, указанным в appsettings.json. Если его нет, не беда, ищем лучший из доступных вариантов качеством пониже. Для аудио все еще проще: YoutubeExplode предоставляет удобный метод, который сам найдет поток с самым высоким битрейтом.

Шаг 3: Скачивание и конвертация в один клик

Теперь, когда потоки выбраны, их нужно скачать и объединить. Для этого давно существует стандарт де-факто — ffmpeg. Но чтобы не вызывать его вручную, мы используем YoutubeExplode.Converter. Эта библиотека-помощник берет на себя всю грязную работу.

// Определяем итоговый путь к файлу
var outputFile = Path.Combine(outputPath, $"{outputFileName}.mp4");

// Запускаем процесс
await youtube.Videos.DownloadAsync(
    [audioStreamInfo, videoStreamInfo], // Передаем нужные потоки
    new ConversionRequestBuilder(outputFile).Build(), // Указываем, куда сохранить
    progress // И передаем обработчик для прогресс-бара
);

В это время мы можем показывать пользователю красивый прогресс-бар от Spectre.Console. Просто и эффективно.

Вот весь код

// Получим конфиг
var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build()
    .Get<AppSettings>() ?? new AppSettings();

// Получим ссылку на видео из youtube
Console.Write("Enter a YouTube video URL: ");
string? videoUrl = Console.ReadLine();

// 1. Получим всю информацию о видео
var youtube = new YoutubeClient();
var video = await youtube.Videos.GetAsync(videoUrl);

var streamManifest = await youtube.Videos.Streams.GetManifestAsync(video.Id);

// 2. Выберем потоки
var videoStreamInfo = streamManifest
    .GetVideoStreams()
    .Where(s => s.Container == Container.Mp4 && s.VideoQuality.MaxHeight == config.PreferredQuality)
    .OrderByDescending(s => s.VideoQuality.Framerate)
    .FirstOrDefault();

if (videoStreamInfo == null)
{
    // Если видеопоток, котрый хотели не нашли, найдем доустпное, качеством по ниже
    videoStreamInfo = streamManifest
        .GetVideoStreams()
        .Where(s => s.Container == Container.Mp4 && s.VideoQuality.MaxHeight < config.PreferredQuality)
        .OrderByDescending(s => s.VideoQuality.MaxHeight + s.VideoQuality.Framerate)
        .FirstOrDefault();
    if (videoStreamInfo == null)
    {
        Console.WriteLine("Error: No suitable MP4 video stream found.");
        return;
    }
}

// Подберем аудиопоток с наилучшим качеством
var audioStreamInfo = streamManifest
    .GetAudioStreams()
    .Where(s => s.Container == Container.Mp4)
    .GetWithHighestBitrate();

if (audioStreamInfo == null)
{
    Console.WriteLine("Error: No audio stream found.");
    return;
}

// 3. Скачаем и объединим потоки в видео файл

// Наимеониваение файла будет из названия видео. Отчистим от недопустимых символов
var outputFileName = SanitizeFilename(video.Title);
// Определим папку для скачивания, если не из конфига, пусть будет папка стандартная папка для видео
var outputPath = config.OutputPath ?? Environment.GetFolderPath(Environment.SpecialFolder.CommonVideos);
var outputFile = Path.Combine(outputPath, $"{outputFileName}.mp4");

await AnsiConsole.Progress() // Это прогрессбар для консоли
    .StartAsync(async ctx =>
    {
        var progressTask = ctx.AddTask($"[green]Downloading Video[/]");

        var progress = new Progress<double>(percent => progressTask.Increment(percent * 100 - progressTask.Percentage));

        // Скачиваем и обхединяем одной командой!
        await youtube.Videos.DownloadAsync(
            [audioStreamInfo, videoStreamInfo],
            new ConversionRequestBuilder(outputFile).Build(),
            progress
        );
    });

Console.WriteLine("Video saved successfully!");
Console.ReadLine();

static string SanitizeFilename(string filename)
{
    if (string.IsNullOrWhiteSpace(filename))
    {
        return $"video{Random.Shared.Next()}";
    }

    foreach (var c in Path.GetInvalidFileNameChars())
    {
        filename = filename.Replace(c.ToString(), "");
    }

    if (string.IsNullOrWhiteSpace(filename))
    {
        return $"video{Random.Shared.Next()}";
    }

    const int MaxFileNameLength = 200;

    if (filename.Length > MaxFileNameLength)
    {
        filename = filename.Substring(0, MaxFileNameLength);
    }

    return filename;
}

public class AppSettings
{
    public int PreferredQuality { get; set; } = 720;
    public string? OutputPath { get; set; }
}

Важные нюансы, о которых стоит знать

А теперь что бы сэкономить ваше время если решите попробовать, есть пара важных моментов.

1. Нюанс с YoutubeExplode и региональными ограничениями

Автор YoutubeExplode ввел ограничения на использование библиотеки на территории России и Беларуси. При запуске кода на машине с региональными настройками этих стран приложение аварийно завершится с сообщением от автора. В самом сообщении будут инструкции, как можно обойти это ограничение (например, через переменные окружения). Если вы находитесь за пределами этих стран, код будет работать без каких-либо проблем.

2. Установка ffmpeg

Библиотека YoutubeExplode.Converter не содержит в себе ffmpeg, а только вызывает его. Поэтому ffmpeg.exe должен быть доступен для вашего приложения. Напомню, это готовое решение, и одно из самых известных для работы с видео и аудио. Даже если вы используете удобную .NET-библиотеку, под капотом она все равно формирует и выполняет консольные команды для ffmpeg.exe. Что бы его получить, самый простой способ — сделать следующее:

  1. Скачайте FFmpeg. Перейдите на официальный сайт и скачайте статическую сборку для вашей операционной системы (для Windows популярны сборки от Gyan.dev или BtbN).

  2. Распакуйте архив и найдите в папке bin файл ffmpeg.exe.

  3. Поместите ffmpeg.exe в корневую папку вашего проекта (рядом с .csproj файлом).

В .csproj файле вашего проекта нужно добавить строки, чтобы ffmpeg.exe копировался в папку со скомпилированной программой:

<ItemGroup>
  <None Update="ffmpeg.exe">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Теперь ваше приложение сможет найти и использовать ffmpeg для своей работы.
Если вы планируете использовать приложение в коммерческих целях, обязательно изучите лицензии компонентов FFmpeg. Для личного использования никаких проблем нет.

3. Ошибка 403 Forbidden: когда YouTube говорит "нет"

При активном использовании загрузчика вы можете время от времени сталкиваться с ошибкой HTTP 403 Forbidden. Не пугайтесь, это не баг в вашем коде или библиотеке. Это сам YouTube вежливо, но настойчиво просит вас сбавить обороты.

Почему это происходит?
YouTube, как и любой крупный сервис, защищается от ботов и чрезмерной нагрузки. Если с вашего IP-адреса поступает слишком много запросов за короткое время (например, вы пытаетесь скачать 100 видео подряд), его автоматические системы могут временно вас заблокировать. Блокировка обычно длится от нескольких минут до нескольких часов.

Если вы скачиваете по несколько видео за раз, вы, скорее всего, редко будете сталкиваться с этой проблемой. Если же ошибка возникла — просто сделайте перерыв.

Для нашей простой консольной утилиты это не критично, но знать о таком поведении YouTube очень полезно.

Заключение

Мы создали простой, но мощный инструмент, который решает реальную проблему — скачивание видео с YouTube без рекламы и лишних сложностей. Мы увидели, как современные .NET-библиотеки позволяют писать элегантный и функциональный код, который приятно не только использовать, но и разрабатывать.

Это лишь отправная точка. Проект можно развивать дальше: добавить скачивание плейлистов, выбор формата, графический интерфейс на Avalonia UI или MAUI. Но даже в таком виде он уже отлично справляется со своей задачей.

Надеюсь, эта статья вдохновит вас на создание собственных полезных утилит.

Полный исходный код проекта, как и всегда, доступен на GitHub. Заходите, ставьте звезды и предлагайте свои улучшения!

Спасибо за внимание

Комментарии (17)


  1. Gosha04ye
    15.08.2025 14:48

    Техчасть объяснена грамотно: код компактный, без лишних зависимостей, логика понятная. Использование FFmpeg через .NET - решение простое и рабочее.


  1. andreymal
    15.08.2025 14:48

    Если разработка утилиты не самоцель, то рациональнее просто взять готовый yt-dlp

    Условия использования у YoutubeExplode кстати интересные


    1. DNsam Автор
      15.08.2025 14:48

      спасибо за комментарий! Вы абсолютно правы, yt-dlp - это мощнейший и, пожалуй, лучший универсальный инструмент для этой задачи.

      Цель моей статьи была скорее образовательная: показать, как с помощью C# и такой удобной библиотеки, как YoutubeExplode, можно собрать подобный инструмент самостоятельно, для души или для встраивания в свои .NET-проекты. Это больше про "как это сделать", а не про "какой готовый инструмент лучше".

      И отдельное спасибо, что обратили внимание на "интересные" условия использования YoutubeExplode. Вы очень точно подметили их, скажем так, нетривиальность. Это довольно известная особенность библиотеки, и, насколько я знаю, именно она объясняет такое большое количество форков на GitHub. При желании этот специфичный код довольно легко найти и убрать из локальной копии.


  1. MEGA_Nexus
    15.08.2025 14:48

    Хочется простой и быстрый инструмент для скачивания видео с YouTube? Решение есть — написать его самому!

    Плохое решение. Стабильно раз в месяц youtube меняет алгоритмы, поэтому старые утилиты для скачивания с него видео перестают работать. Так что вам придётся постоянно следить за её актуальностью, иначе ваша утилита через месяц перестанет нормально работать.


    1. HardWrMan
      15.08.2025 14:48

      Более того, ютуб будет троттлить такую закачку. Там же скрипт-детектор выполняется на клиенте. Тут была статья про это. Тот же yt-dlp это использует. А для закачки нужно знать только 2 команды: -F для получения списка потоков и -fY+X для закачивания, где X и Y номера потоков из предыдущей команды.


      1. DNsam Автор
        15.08.2025 14:48

        огромное спасибо за такой глубокий технический комментарий! Это именно тот ценный инсайт, который обогащает статью и показывает, насколько сложнее всё устроено «под капотом».

        Вы совершенно правы насчет троттлинга. То, что yt-dlp умеет обходить это с помощью эмуляции клиентских скриптов - это действительно высший пилотаж и одна из причин, почему он по праву считается лучшим инструментом в своем классе.

        Я и сам, кстати, столкнулся с этим на практике, что только подтверждает ваши слова. Когда я тестировал утилиту и пытался скачать подряд несколько видео для отладки, YouTube действительно временно заблокировал доступ с ошибкой 403. Однако, ограничение проходит само собой через некоторое время.

        Поэтому для моей цели -скачать для личных нужд пару роликов в день - это не стало критичной проблемой. Но ваше замечание абсолютно в точку: как только речь заходит о больших объемах, без продвинутых техник, которые использует yt-dlp, уже не обойтись.

        Еще раз спасибо, что поделились своим опытом и знаниями. Такие комментарии делают Хабр лучше!


    1. DNsam Автор
      15.08.2025 14:48

      вы затронули самую главную "боль" всех подобных проектов! И вы совершенно правы: поддерживать такую утилиту в одиночку - это огромный труд.

      Именно поэтому я и сделал ставку на библиотеку YoutubeExplode. Её автор и сообщество как раз и занимаются тем, что оперативно отслеживают изменения YouTube и выпускают обновления. Таким образом, "поддержка" моей утилиты сводится к простому обновлению NuGet-пакета. Это хороший пример того, как использование библиотек с активной поддержкой решает сложные проблемы.


  1. PelmenBlin
    15.08.2025 14:48

    Или используем Hitomi downloader. Он там вообще с приличного списка сайтов может качать. В виде программы для windows на гитхабе он есть.


    1. DNsam Автор
      15.08.2025 14:48

      Спасибо, что поделились! Не знал про этот инструмент, выглядит интересно. Всегда полезно знать о хороших альтернативах.


  1. morginalium
    15.08.2025 14:48

    Любят же люди себе жизнь усложнять.

    Все просто:

    1. Создаете группу тг и добавляете туда как участника любого бота для скачивания видео (даете ему максимальные права)

    2. Через "собака"vid ищете сылку на видео и отправляете ее чат. В ту же секунду бот присылает сообщение, где можно выбрать формат скачивания.

    все намного проще чем свой код писать


    1. DNsam Автор
      15.08.2025 14:48

      Классное решение, но код писать тоже просто =)


    1. fossfusion
      15.08.2025 14:48

      Зачем группа, можно сразу написать боту?


    1. winkyBrain
      15.08.2025 14:48

      все намного проще чем свой код писать

      но код есть на гитхабе, его не нужно писать, если не хочется. странная ремарка.

      к тому же здесь вроде как большинство людей любит писать код. по крайней мере я очень на это надеюсь)


      1. DNsam Автор
        15.08.2025 14:48

        спасибо за поддержку! Вы идеально сформулировали мысль: Хабр - это место, где мы любим сам процесс создания чего-то нового своими руками. Рад, что здесь так много единомышленников =)


  1. Kolonist
    15.08.2025 14:48

    Так самое интересное же как раз в библиотеке YoutubeExplode! Я-то думал, тут как раз расскажут, как ссылки на видео и аудио найти, как чанки в нужном качестве скачать, че там за формат, какие кодеки, как из них целый видос собрать.

    А тут статья вида «берем готовую либу и используем (


    1. valm0unt
      15.08.2025 14:48

      Тоже ждал написание логики с нуля, а не готовую либу


    1. DNsam Автор
      15.08.2025 14:48

      Вау, классный комментарии. Я понимаю ваши ожидания. Тема разбора внутреннего устройства YouTube, поиска ссылок на чанки и их сборки - невероятно интересная и сложная.

      Честно говоря, такой глубокий анализ потянул бы на целую серию статей, а не на один гайд. Моей целью было показать, как можно быстро решить практическую задачу, используя современные инструменты в экосистеме .NET.

      Но ваша обратная связь очень ценна. Она показывает, что есть интерес и к более "низкоуровневым" статьям. Возможно, в будущем я попробую разобрать какой-то из этих аспектов отдельно. Спасибо вам огромное за то, что поделились своим мнением и подтолкнули в этом направлении. Это ценно!