Качалка видео для Windows и Mac: почему TikTok не пускал собранную программу, а QuickTime принимал видео за музыку

Вставляешь ссылку на видео с YouTube, TikTok, VK или Instagram, выбираешь 1080 p, 720 p или 480 p и жмёшь «Скачать». Через несколько секунд на Рабочем столе лежит mp4, который открывается в QuickTime, на айфоне и в любом плеере Windows. Пока идёт загрузка, по пиксельному офису бегает человечек и складывает в папку листки.

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

Давно хотел делать бесплатно для людей, время пришло.

Из чего собрано

  • yt‑dlp делает всю тяжёлую работу: достаёт видео с сотен сайтов.

  • FFmpeg склеивает видео со звуком. Он едет внутри программы через пакет imageio-ffmpeg, так что ставить ничего не нужно.

  • CustomTkinter рисует окно: тёмная тема, фиолетовые кнопки.

  • PyInstaller собирает Kachalka.exe для Windows и Kachalka.app для Mac.

  • GitHub Actions собирает обе версии по тегу. У меня нет компьютера с Windows, так что exe я ни разу не собирал сам.

«Код писал вместе с Claude Code; идея, решения и проверка — мои».

Почему H.264, а не «лучшее качество»

Первое желание — качать максимум. Но YouTube отдаёт высокие разрешения в VP9 и AV1, а QuickTime и айфон такие файлы часто не открывают. Пользователь скачал видео, кликнул — а оно не играет. Поэтому Качалка всегда просит H.264 и звук AAC:

"format": "bv*+ba/b",
"format_sort": ["vcodec:h264", f"res:{height}", "acodec:m4a"],
"merge_output_format": "mp4",

Тонкость с res: yt‑dlp считает разрешение по меньшей стороне. Поэтому вертикальный TikTok 1080×1920 честно считается 1080 p и не отсекается фильтром «не больше 1080». Если бы я фильтровал по height, вертикальные видео скачивались бы в плохом качестве.

Цена решения — нет 4K: в H.264 его практически никто не отдаёт. Перекодировать 4K на компьютере пользователя — минуты ожидания, для простой качалки это лишнее.

Баг 1: TikTok пускает Python, но не пускает собранную программу

Из кода всё качалось. Собранная программа на той же ссылке падала:

ERROR: [TikTok] 7698495498537143604: Unexpected response from webpage request

Я последовательно исключал причины:

  • Сеть и VPN. VPN работает на весь компьютер, системного прокси нет. И скрипт, и программа ходят в сеть одинаково.

  • Окружение. Программа, запущенная из терминала, а не из Finder, падала так же.

  • Версия yt‑dlp. В логе сборки GitHub та же версия, что у меня, 2026.08.19.

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

Остался Python. Локально у меня одна сборка Python, на GitHub — другая, с другими настройками TLS. По всей видимости, TikTok смотрит на «почерк» TLS‑соединения и одной сборке отдаёт нормальную страницу, а другой — заглушку. yt‑dlp, кстати, сам об этом предупреждал, а я не обратил внимания:

WARNING: [TikTok] The extractor is attempting impersonation,
but no impersonate target is available.

Решение — пакет curl_cffi: он умеет подключаться с отпечатком настоящего Chrome или Safari. Достаточно поставить yt‑dlp с нужным дополнением и не забыть положить пакет в сборку:

# requirements.txt
yt-dlp[default,curl-cffi]

# build.py
"--collect-all=curl_cffi",

Главный урок: проверять надо собранную программу, а не код. Локально ошибка не воспроизводилась ни разу. Я собрал пробную версию в отдельной ветке, скачал её из GitHub Actions и только после этого выпустил исправление.

Баг 2: видео из TikTok открывается как музыка

TikTok начал качаться, но в Finder вместо превью у файла была нота, а QuickTime видел в нём только звук. При этом ffprobe показывал нормальный H.264 1080×1080, а ffmpeg декодировал кадры без единой ошибки.

Странным было одно поле — масштаб времени видеодорожки:

stream|codec_type=video|time_base=1/5995319

Почти шесть миллионов делений в секунду. Я спросил у видеодвижка macOS напрямую, что он видит в файле:

asset = AVFoundation.AVURLAsset.URLAssetWithURL_options_(url, None)
len(asset.tracksWithMediaType_(AVFoundation.AVMediaTypeVideo))
# исходный файл — 0 видеодорожек
# после переупаковки — 1

Ноль. Для macOS видео в этом файле просто не существовало. Лечится переупаковкой без перекодирования: качество то же, работы на пару секунд.

ffmpeg -i in.mp4 -map 0 -c copy -video_track_timescale 90000 -movflags +faststart out.mp4

Теперь Качалка делает это после каждой загрузки. На Windows ffmpeg запускается с флагом CREATE_NO_WINDOW, иначе на секунду мелькает чёрное окно консоли.

Плавный прогресс, когда файлов два

YouTube отдаёт видео и звук отдельно, и прогресс yt‑dlp идёт дважды от 0 до 100%. Пользователь видит, как полоска доходит до конца и прыгает в ноль. Поэтому перед загрузкой Качалка смотрит размеры обеих частей и считает общий прогресс с весами. Полоска не прыгает к цели, а плавно догоняет её — каждые 16 мс проходит 12% оставшегося пути. Последние 3% оставлены на склейку.

Человечек в офисе

Гифка нарисована кодом на Pillow, без готовых ассетов. Холст 160×80 «пикселей» увеличивается в три раза без сглаживания. Офис — это кусок шириной 96 пикселей, который едет на 2 пикселя за кадр: за 48 кадров он проезжает ровно свою ширину, поэтому петля бесшовная. 48 кадров по 40 мс дают 25 кадров в секунду.

Ещё одна мелочь для Retina: CustomTkinter сам масштабирует картинки и размывает пиксель‑арт. Поэтому кадры заранее увеличиваются в два раза «по пикселям», и масштабировать окну уже нечего.

Сборка для двух систем без Windows

Одна матрица GitHub Actions: windows-latest собирает однофайловый exe, macos-latest — .app в zip. По тегу v* оба файла уходят в релиз, а к релизу прикладывается подпись происхождения. Любой может проверить, что файл собран из этого репозитория:

gh attestation verify Kachalka-Windows.exe --owner kozliq

Две вещи, которые пришлось объяснять в README:

  • Программа без платной подписи. Windows показывает «Система Windows защитила ваш компьютер», macOS — «не удаётся проверить разработчика». Это обходится за один шаг, но предупредить нужно.

  • Блокировки. Многие обходят их VPN‑расширением или DNS в настройках браузера. Программа настроек браузера не видит, и YouTube для неё недоступен. Поэтому сырые ошибки yt‑dlp переводятся на человеческий язык: вместо getaddrinfo failed Качалка пишет «Нет связи с сайтом — похоже, он заблокирован у провайдера» и советует включить VPN для всего компьютера.

Что дальше

В планах значок в строке меню Mac: нажал, вставил ссылку, скачал — без большого окна.

Код и готовые файлы для Windows и Mac: github.com/kozliq/kachalka

Если что‑то не качается — заведите issue со ссылкой, разберусь.

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


  1. Diacut
    24.10.2026 06:55

    как оно работает? Мне никак не удаётся запустить голый yt-dlp, чтобы он что-то скачал. Иногда (редко) удаётся, но для этого надо положить рядом deno.exe и дать файл с куками браузера. хитрость какая-то?


    1. zelenin
      24.10.2026 06:55

      гугл активно защищается от yt-dlp. deno/node/bun требуются для скачивания видео с ютуба с весны


      1. Diacut
        24.10.2026 06:55

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


        1. kozliq Автор
          24.10.2026 06:55

          Без хитростей, просто yt-dlp обвешан со всех сторон:

          • рядом стоит deno — с весны YouTube без JS-движка почти ничего не отдаёт, это zelenin верно сказал;

          • yt-dlp обновляется сам 4 раза в день (берём только выпуски старше суток, после обновления качаем пару тестовых роликов, не прошло — откат);

          • куки не используем. Главное — IP. С адресов датацентров YouTube отвечает «Sign in to confirm you're not a bot», поэтому YouTube идёт через отдельный сервер с «чистым» IPv4. Кстати, его IPv6 YouTube забанил целой подсетью, пришлось заставить yt-dlp ходить только по IPv4 (через SOCKS4 — он IPv6 не умеет вообще);

          • если всё равно отказ — один тихий повтор через несколько секунд.

          Иногда «not a bot» всё равно проскакивает, это гонка вооружений, а не решённая задача. У себя на домашнем IP голый yt-dlp + deno обычно работает без кук — если нет, скорее всего, дело в адресе (VPN, хостинг).


          1. Diacut
            24.10.2026 06:55

            всё равно не понял. вижу только ехещник Kachalka-Windows.exe

            ок, ffmpeg и python есть. но deno у меня нет.

            и yt-dlp у меня 2026.03.17

            как оно работает? или куда-то втихаря скачивает нужное?


            1. kozliq Автор
              24.10.2026 06:55

              Всё уже лежит внутри Kachalka-Windows.exe: Python, yt-dlp и ffmpeg упакованы прямо в него. Поэтому exe один и весит прилично. Ставить ничего не нужно, твои Python, ffmpeg и yt-dlp программа вообще не трогает, deno тоже не нужен.

              При запуске exe распаковывает себя во временную папку и работает оттуда. Ничего не докачивает и в систему не ставит. yt-dlp внутри свежий: каждая версия собирается на GitHub из открытого кода, всё видно в репозитории.


              1. Diacut
                24.10.2026 06:55

                Теперь понятно. Спасибо.


  1. ZorkyK
    24.10.2026 06:55

    Веселая качалка. Анимация понравилась. Если бы еще без ВПН могла качать...

    Скачал обзор легендарного матча Ливерпуль-Арсенал 2009 года с покером Аршавина. Спасибо.


    1. kozliq Автор
      24.10.2026 06:55

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


  1. dky
    24.10.2026 06:55

    Некоторые видосы не качает, например "UiOoKIST5zc", впн никаких нет и в браузере все воспроизводится нормально.

    Скрытый текст


    1. kozliq Автор
      24.10.2026 06:55

      Извини за путаницу — программа написала про «робота» и VPN, а на деле это видео 18+: YouTube отдаёт его только после входа в аккаунт, поэтому Качалка его не скачает. В следующей версии будет писать прямо.