Files
yt-shorts-downloader/README.md
T
ВяткинАртём 0a3cf97200
CI / Quality Checks (push) Successful in 13s
CI / Test Suite (push) Successful in 10s
CI / Publish to Gitea Packages (push) Successful in 11s
Обновить документацию: уточнить формат скачиваемых видео, добавить примеры использования и требования к session file
2026-05-27 17:59:21 +03:00

282 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## Установка
Для использования библиотеки:
```bash
uv pip install .
```
Для разработки:
```bash
uv sync --group dev
```
## Быстрый старт
### Python API
```python
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
```
Если нужен файл на диске:
```python
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
```bash
uv run yt-shorts-downloader \
'https://www.youtube.com/shorts/VIDEO_ID' \
cookies.txt \
--output-dir downloads
```
CLI печатает в stdout итоговый путь к скачанному файлу.
## Пример интеграции с FastAPI
```python
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 до попытки скачивания. Поля результата:
- `exists`
- `structurally_valid`
- `fresh`
- `is_usable`
- `message`
## Требования к session file
Проект ожидает Netscape cookie file, например экспортированный из браузера.
Проверяется:
- существование файла
- структура Netscape cookie format с 7 tab-separated columns
- наличие cookie для YouTube или Google
- наличие достаточного количества актуальных auth-cookie
Ключевые cookie-имена:
- `SID`
- `HSID`
- `SSID`
- `APISID`
- `SAPISID`
- `__Secure-1PSID`
- `__Secure-3PSID`
- `LOGIN_INFO`
Если файл структурно валиден, но auth-cookie устарели или неполные, библиотека выбрасывает `InvalidSessionError` до запуска yt-dlp.
## Ошибки
Пакет экспортирует следующие исключения:
- `YtShortsDownloaderError`: базовое исключение библиотеки
- `InvalidUrlError`: неподдерживаемая или некорректная ссылка
- `InvalidSessionError`: невалидный или устаревший session file
- `JsRuntimeUnavailableError`: не найден Deno или Node.js 22+
- `VideoDownloadError`: yt-dlp не смог получить валидный MP4
## Диагностика и troubleshooting
Частые причины проблем:
- `InvalidUrlError`: ссылка не относится к YouTube или имеет неверную схему
- `InvalidSessionError`: cookie-файл не найден, повреждён или содержит устаревшие auth-cookie
- `JsRuntimeUnavailableError`: в системе нет 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 entrypoint
- `src/yt_shorts_downloader/models`: доменные модели и типизированные возвращаемые объекты
- `src/yt_shorts_downloader/services`: логика валидации session file
- `src/yt_shorts_downloader/core`: интеграция с yt-dlp, поиск runtime и URL validation
- `stubs`: локальные typing stubs для внешних пакетов без полной типизации
Это позволяет держать публичный API компактным, а инфраструктурный код изолировать в `core`.
## Разработка
Инструменты настраиваются в `pyproject.toml`.
- runtime-зависимости находятся в `[project.dependencies]`
- инструменты разработки находятся в `[dependency-groups.dev]`
- mypy работает в strict-режиме
- Ruff отвечает за lint и format
Основные команды:
```bash
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 на `main` workflow проверяет, существует ли текущая версия в Gitea Packages
- если версии ещё нет, пакет собирается и публикуется
- если версия уже существует, публикация пропускается без падения workflow
- старые версии пакета сохраняются в registry
Необходимые secrets для CI:
- `PYPI_REPOSITORY_URL`: endpoint вида `https://gitea.example.com/api/packages/<owner>/pypi`
- `PACKAGE_USERNAME`: пользователь Gitea
- `PACKAGE_TOKEN`: personal access token с правом package write
Локальная публикация:
```bash
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_URL`
- `GITEA_PACKAGE_USERNAME`
- `GITEA_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`.