9.2 KiB
yt-shorts-downloader
Библиотека и CLI для скачивания YouTube Shorts в формате MP4 через Netscape cookie session file.
Основной контракт проекта:
- вход: ссылка YouTube и путь к cookie-файлу
- выход для API: MP4 в памяти
- выход для CLI: путь к сохранённому MP4-файлу
Возможности
- типизированный Python API
- CLI для сохранения файла на диск
- предварительная валидация YouTube URL
- предварительная валидация session file
- интеграция с yt-dlp
- strict mypy и локальные stubs для нетипизированных зависимостей
- автоматическая публикация пакета в Gitea Packages после успешного CI
Требования
- Python 3.12+
- валидный Netscape cookie file из авторизованной YouTube-сессии
- один поддерживаемый JavaScript runtime:
- Deno
- Node.js 22+
- опционально, но желательно: ffmpeg
Если ffmpeg отсутствует, проект использует более простой MP4 format selector.
Установка
Для использования библиотеки:
uv pip install .
Для разработки:
uv sync --group dev
Быстрый старт
Python API
from yt_shorts_downloader import download
video = download(
'https://www.youtube.com/shorts/VIDEO_ID',
'cookies.txt',
)
assert video.media_type == 'video/mp4'
assert video.filename.endswith('.mp4')
binary_mp4 = video.content
Если нужен файл на диске:
from yt_shorts_downloader import download_to_path
file_path = download_to_path(
'https://www.youtube.com/shorts/VIDEO_ID',
'cookies.txt',
output_dir='downloads',
)
CLI
uv run yt-shorts-downloader \
'https://www.youtube.com/shorts/VIDEO_ID' \
cookies.txt \
--output-dir downloads
CLI печатает в stdout итоговый путь к скачанному файлу.
Пример интеграции с FastAPI
from fastapi import FastAPI, HTTPException
from fastapi.responses import Response
from yt_shorts_downloader import download
from yt_shorts_downloader.exceptions import YtShortsDownloaderError
app = FastAPI()
@app.get('/download')
def download_short(url: str) -> Response:
try:
video = download(url, 'cookies.txt')
except YtShortsDownloaderError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return Response(
content=video.content,
media_type=video.media_type,
headers={
'Content-Disposition': f'attachment; filename="{video.filename}"',
},
)
Публичный API
download(url, session_path) -> DownloadedVideo
Скачивает видео во временную директорию, проверяет, что итоговый артефакт имеет формат MP4, и возвращает объект DownloadedVideo.
Поля DownloadedVideo:
filename: имя файлаcontent: MP4-байтыmedia_type: всегдаvideo/mp4
download_short(url, session_path) -> DownloadedVideo
Алиас для download.
download_to_path(url, session_path, output_dir=None) -> Path
Скачивает итоговый MP4 на диск и возвращает Path к файлу.
validate_session_file(path) -> SessionValidation
Проверяет session file до попытки скачивания. Поля результата:
existsstructurally_validfreshis_usablemessage
Требования к session file
Проект ожидает Netscape cookie file, например экспортированный из браузера.
Проверяется:
- существование файла
- структура Netscape cookie format с 7 tab-separated columns
- наличие cookie для YouTube или Google
- наличие достаточного количества актуальных auth-cookie
Ключевые cookie-имена:
SIDHSIDSSIDAPISIDSAPISID__Secure-1PSID__Secure-3PSIDLOGIN_INFO
Если файл структурно валиден, но auth-cookie устарели или неполные, библиотека выбрасывает InvalidSessionError до запуска yt-dlp.
Ошибки
Пакет экспортирует следующие исключения:
YtShortsDownloaderError: базовое исключение библиотекиInvalidUrlError: неподдерживаемая или некорректная ссылкаInvalidSessionError: невалидный или устаревший session fileJsRuntimeUnavailableError: не найден Deno или Node.js 22+VideoDownloadError: yt-dlp не смог получить валидный MP4
Диагностика и troubleshooting
Частые причины проблем:
InvalidUrlError: ссылка не относится к YouTube или имеет неверную схемуInvalidSessionError: cookie-файл не найден, повреждён или содержит устаревшие auth-cookieJsRuntimeUnavailableError: в системе нет Deno и нет Node.js 22+VideoDownloadError: yt-dlp не смог скачать видео или итоговый файл не оказался MP4- публикация в Gitea Packages пропускается: версия из
pyproject.tomlуже существует в registry
Практические рекомендации:
- переэкспортируйте cookies после переавторизации в браузере
- установите
ffmpeg, если хотите лучший вариант объединения аудио и видео - увеличивайте
project.versionвpyproject.tomlперед новой публикацией пакета - не коммитьте cookie-файлы в репозиторий
Архитектура
Структура проекта разделена по зонам ответственности:
src/yt_shorts_downloader/api.py: публичный API библиотекиsrc/yt_shorts_downloader/cli.py: CLI entrypointsrc/yt_shorts_downloader/models: доменные модели и типизированные возвращаемые объектыsrc/yt_shorts_downloader/services: логика валидации session filesrc/yt_shorts_downloader/core: интеграция с yt-dlp, поиск runtime и URL validationstubs: локальные typing stubs для внешних пакетов без полной типизации
Это позволяет держать публичный API компактным, а инфраструктурный код изолировать в core.
Разработка
Инструменты настраиваются в pyproject.toml.
- runtime-зависимости находятся в
[project.dependencies] - инструменты разработки находятся в
[dependency-groups.dev] - mypy работает в strict-режиме
- Ruff отвечает за lint и format
Основные команды:
make format
make lint
make typecheck
make test
make ci-check
make build
make package-version
Пакетирование и публикация
Метаданные пакета и версия берутся из [project] в pyproject.toml.
Текущий publish flow:
- Gitea Actions запускает quality checks и tests на pull request и push в
main - после успешного CI на
mainworkflow проверяет, существует ли текущая версия в Gitea Packages - если версии ещё нет, пакет собирается и публикуется
- если версия уже существует, публикация пропускается без падения workflow
- старые версии пакета сохраняются в registry
Необходимые secrets для CI:
PYPI_REPOSITORY_URL: endpoint видаhttps://gitea.example.com/api/packages/<owner>/pypiPACKAGE_USERNAME: пользователь GiteaPACKAGE_TOKEN: personal access token с правом package write
Локальная публикация:
export PYPI_REPOSITORY_URL='https://gitea.example.com/api/packages/<owner>/pypi'
export PACKAGE_USERNAME='<username>'
export PACKAGE_TOKEN='<token>'
make package-version
make publish-gitea
Для обратной совместимости publish-gitea также принимает:
GITEA_PYPI_REPOSITORY_URLGITEA_PACKAGE_USERNAMEGITEA_PACKAGE_TOKEN
CI/CD
Основной workflow:
.gitea/workflows/ci.yml
Он выполняет:
- Ruff linting
- strict mypy type checking
- deptry dependency checks
- pytest test suite
- условную публикацию в Gitea Packages после успешного push CI на
main
Лицензия
См. LICENSE.