Metadata-Version: 2.5
Name: async-yt-dlp
Version: 0.1.2
Summary: Production-ready, strictly typed async wrapper for yt-dlp
Project-URL: Homepage, https://github.com/baton-spb/AsyncYTDLP
Project-URL: Documentation, https://github.com/baton-spb/AsyncYTDLP#readme
Project-URL: Repository, https://github.com/baton-spb/AsyncYTDLP.git
Project-URL: Issues, https://github.com/baton-spb/AsyncYTDLP/issues
Author: async-yt-dlp contributors
License-Expression: MIT
License-File: LICENSE
Keywords: async,asyncio,download,video,youtube,yt-dlp
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: yt-dlp>=2024.01.01
Provides-Extra: dev
Requires-Dist: coverage>=7.6.0; extra == 'dev'
Requires-Dist: graphifyy>=0.9.64; extra == 'dev'
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Provides-Extra: ffmpeg
Requires-Dist: aio-ffmpeg>=0.1.0; extra == 'ffmpeg'
Provides-Extra: full
Requires-Dist: aio-ffmpeg>=0.1.0; extra == 'full'
Requires-Dist: brotli>=1.0.0; extra == 'full'
Requires-Dist: certifi>=2024.01.01; extra == 'full'
Requires-Dist: curl-cffi>=0.5.10; extra == 'full'
Requires-Dist: deno>=2.0.0; extra == 'full'
Requires-Dist: mutagen>=1.47.0; extra == 'full'
Requires-Dist: pycryptodomex>=3.20.0; extra == 'full'
Requires-Dist: requests>=2.31.0; extra == 'full'
Requires-Dist: urllib3>=2.0.0; extra == 'full'
Requires-Dist: websockets>=13.0; extra == 'full'
Requires-Dist: yt-dlp-ejs>=0.8.0; extra == 'full'
Description-Content-Type: text/markdown

# async-yt-dlp

[![CI](https://github.com/baton-spb/AsyncYTDLP/actions/workflows/ci.yml/badge.svg)](https://github.com/baton-spb/AsyncYTDLP/actions)
[![PyPI version](https://img.shields.io/pypi/v/async-yt-dlp.svg)](https://pypi.org/project/async-yt-dlp/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![Typing: Typed](https://img.shields.io/badge/typing-typed-green.svg)](https://peps.python.org/pep-0561/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Строго типизированная асинхронная обёртка над [yt-dlp](https://github.com/yt-dlp/yt-dlp) для Python 3.11+.

Все блокирующие операции yt-dlp выполняются через `asyncio.to_thread`, поэтому event loop не блокируется. Подходит для Telegram-ботов, Discord-ботов, веб-сервисов (FastAPI, Litestar, aiohttp) и фоновых очередей задач.

---

## Возможности

- **Асинхронность**: блокирующие вызовы yt-dlp вынесены в `asyncio.to_thread`, event loop свободен.
- **Типизация**: модели `MediaInfo`, `FormatInfo`, `DownloadResult`, `ProgressEvent` — frozen dataclass со `slots=True`. PEP 561 `py.typed`, совместимо с `mypy --strict`.
- **Потокобезопасность**: каждая операция получает изолированный экземпляр `YoutubeDL`.
- **Стриминг прогресса**: асинхронный генератор `download_with_progress` с адаптивным троттлингом.
- **Контроль параллельности**: `DownloadManager` на базе `asyncio.Semaphore` с ограничением очереди (backpressure).
- **Отмена и таймауты**: корректная обработка `task.cancel()`, `asyncio.timeout` и graceful shutdown.
- **Объектная конфигурация**: `FormatSelector` (fluent-построитель форматов), `OutputTemplate` (построитель шаблонов имён файлов), `VideoContainer` (выбор контейнера).
- **Маскировка данных**: пароли, токены, cookies и прокси автоматически скрываются в логах.
- **Диагностика окружения**: `check_dependencies()` проверяет наличие `yt-dlp`, `ffmpeg`, `ffprobe` и JS-движков.

---

## Установка

Требуется Python **3.11+**.

```bash
# Базовая установка:
pip install async-yt-dlp

# С интеграцией aio-ffmpeg (постобработка видео):
pip install "async-yt-dlp[ffmpeg]"

# Полный набор (aio-ffmpeg + curl-cffi, websockets и др.):
pip install "async-yt-dlp[full]"
```

Или через `uv`:
```bash
uv add async-yt-dlp
```

---

## Быстрый старт

### 1. Извлечение метаданных

```python
import asyncio
from async_yt_dlp import AsyncYTDLP


async def main() -> None:
    async with AsyncYTDLP() as ytdlp:
        info = await ytdlp.extract_info("https://www.youtube.com/watch?v=BaW_jenozKc")
        print(f"Название: {info.title}")
        print(f"Автор: {info.uploader}")
        print(f"Длительность: {info.duration_seconds} сек.")


asyncio.run(main())
```

### 2. Скачивание видео

```python
import asyncio
from pathlib import Path
from async_yt_dlp import AsyncYTDLP, YTDLPOptions, FormatSelector, OutputTemplate, VideoContainer


async def main() -> None:
    options = YTDLPOptions(
        format=FormatSelector.preset_720p(container=VideoContainer.MP4),
        container=VideoContainer.MP4,
        output_path=Path("./downloads"),
        output_template=OutputTemplate.title_only(),
    )

    async with AsyncYTDLP(default_options=options) as ytdlp:
        result = await ytdlp.download("https://www.youtube.com/watch?v=BaW_jenozKc")
        print(f"Файл: {result.filepath} ({result.file_size} байт)")


asyncio.run(main())
```

### 3. Стриминг прогресса загрузки

```python
import asyncio
from async_yt_dlp import AsyncYTDLP, DownloadStatus


async def main() -> None:
    async with AsyncYTDLP() as ytdlp:
        async for event in ytdlp.download_with_progress(
            "https://www.youtube.com/watch?v=BaW_jenozKc",
            throttle_interval=0.5,
        ):
            if event.status == DownloadStatus.DOWNLOADING:
                print(
                    f"\rЗагрузка: {event.percent:.1f}% | {event.speed_str} | ETA: {event.eta_str}",
                    end="",
                )
            elif event.status == DownloadStatus.COMPLETE:
                print("\nГотово!")


asyncio.run(main())
```

---

## Объектная конфигурация

### FormatSelector — построитель строки `--format`

```python
from async_yt_dlp import FormatSelector, VideoContainer

# Готовые пресеты:
FormatSelector.preset_720p()                              # 720p видео + аудио
FormatSelector.preset_1080p(container=VideoContainer.MP4) # 1080p, приоритет mp4
FormatSelector.preset_audio_only("m4a")                   # только аудио
FormatSelector.preset_max_quality()                       # максимальное качество

# Ручная сборка:
fmt = FormatSelector.video().max_height(480).ext("mp4").merge(FormatSelector.audio())
```

### OutputTemplate — построитель шаблона имени файла

```python
from async_yt_dlp import OutputTemplate

# Готовые пресеты:
OutputTemplate.title_only()         # "%(title)s.%(ext)s"
OutputTemplate.title_and_id()       # "%(title)s [%(id)s].%(ext)s"
OutputTemplate.dated()              # "%(upload_date)s - %(title)s.%(ext)s"
OutputTemplate.playlist_folder()    # "%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s"
OutputTemplate.channel_folder()     # "%(uploader)s/%(upload_date)s - %(title)s.%(ext)s"

# Ручная сборка через fluent-API:
tpl = OutputTemplate().channel().dir().title().ext()  # "%(channel)s/%(title)s.%(ext)s"

# Операторы:
tpl = OutputTemplate().channel() / OutputTemplate.title_only()  # то же самое
```

### VideoContainer — гарантия формата выходного файла

```python
from async_yt_dlp import VideoContainer, YTDLPOptions

# Гарантирует .mp4 на выходе (ffmpeg remux без перекодирования):
options = YTDLPOptions(container=VideoContainer.MP4)
# Доступные: MP4, MKV, WEBM, MOV, AVI, FLV, TS
```

---

## Архитектура

```mermaid
flowchart TD
    App["Приложение<br/>(Telegram, Web, CLI, Bot)"] --> Client["AsyncYTDLP<br/>фасад, lifecycle, API"]
    Client --> Manager["DownloadManager<br/>Semaphore, backpressure"]
    Manager --> Backend["ThreadBackend<br/>asyncio.to_thread"]
    Backend --> YTDLP["yt_dlp.YoutubeDL<br/>синхронное ядро"]
```

---

## Примеры использования

В каталоге [`examples/`](examples/) представлены готовые примеры:

- [`simple_extract.py`](examples/simple_extract.py) — извлечение метаданных
- [`simple_download.py`](examples/simple_download.py) — скачивание файла
- [`progress.py`](examples/progress.py) — отображение прогресса
- [`playlist.py`](examples/playlist.py) — работа с плейлистами
- [`audio_extraction.py`](examples/audio_extraction.py) — извлечение аудио
- [`postprocessing_pipeline.py`](examples/postprocessing_pipeline.py) — постобработка через aio-ffmpeg
- [`custom_options.py`](examples/custom_options.py) — настройка параметров
- [`cancellation.py`](examples/cancellation.py) — отмена задач и таймауты
- [`concurrency.py`](examples/concurrency.py) — параллельная загрузка

---

## Лицензия

Проект распространяется под лицензией MIT. См. файл [LICENSE](LICENSE).
