Metadata-Version: 2.4
Name: url-media-probe
Version: 0.1.0
Summary: Inspect remote media files via HTTP Range requests — download only what's needed.
Project-URL: Homepage, https://github.com/Pankovea/url-media-probe
Project-URL: Repository, https://github.com/Pankovea/url-media-probe
Author: Pankovea
License-Expression: LGPL-2.1-or-later
License-File: LICENSE
Keywords: async,audio,ffprobe-alternative,http,image,media,metadata,no-ffmpeg,probe,range,video
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Multimedia
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Video
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: aiofiles>=23.0
Requires-Dist: aiohttp>=3.9
Requires-Dist: multidict>=6.0
Requires-Dist: yarl>=1.9
Provides-Extra: dev
Requires-Dist: mkdocs-material>=9.0; extra == 'dev'
Requires-Dist: mkdocs>=1.5; extra == 'dev'
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: types-aiofiles; extra == 'dev'
Description-Content-Type: text/markdown

# url-media-probe

**Просматривайте удаленные медиафайлы с помощью HTTP-запросов - загружайте только то, что необходимо**

Минималистичный пакет для определения параметров медиафайла по URL, локальному пути или байтам без полной загрузки.
Анализирует сигнатуры и заголовки в первых и последних килобайтах файла.
Скачивает минимум данных, докачивает только если не хватило.
После принятия решения о скачивании целиком использует уже скачанные данные и докачивает только то, что осталось.

## Сравнение
| Характеристика | url-media-probe | tinytag | pymediainfo | hachoir | ffprobe (subprocess) |
|---|---|---|---|---|---|
| Pure Python + серверлес | Да (async, без бинарников) | Да | Нет (требуется libmediainfo) | Да | Нет (требуется ffmpeg binary) |
| Асинхронность | asyncio + aiohttp | Синхронный | Синхронный | Синхронный | Синхронный |
| Источники данных | URL, файл, байты в памяти | Файл | Файл | Файл | URL, файл |
| Оптимизация трафика | от 4 KB + 64 KB tail (контроль максимума) | — | — | — | Да, но может скачать весь файл |
| HTTP Range запросы | Прогрессивные (4→8→..→128 KB) + задаётся парсером | — | — | — | Да, но может скчать весь файл |
| Retry логика | Автоматическая | — | — | — | — |
| Типизация | dataclass (frozen) | — | — | — | JSON/dict |
| Форматы | Популярные аудио, видео и фото форматы (~20) | Только аудио | Все форматы MediaInfo (100+) | 33 формата: аудио, видео, изображения, архивы, шрифты | Все форматы ffmpeg (100+) |
| Информативность | Базовая: format, duration, fps, sample rate, bitrate ... | Базовая: duration, bitrate, sample rate, теги | Полная: codec, profile, level, bitrate, контейнер | Полная: codec, profile, bitrate, пиксели, каналы, теги | Полная: codec, profile, level, пиксели, каналы и т.д. |
| Размер дистрибутива | ~300 KB | ~100 KB | ~15 MB (libmediainfo) | ~650 KB | >100 MB (ffmpeg) |
| Сортировка: | | <--- легковесный | | тяжеловесный --> | |

## Установка

```bash
pip install url-media-probe
```

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

```python
import asyncio
from url_media_probe import MediaProbe

async def main():
    probe = MediaProbe()

    # По URL
    info = await probe.from_url("https://example.com/video.mp4")
    print(info.format)    # "MP4"
    print(info.width)     # 1920
    print(info.height)    # 1080
    print(info.duration)  # 15.0
    print(info.status)    # "ok"

    # Локальный файл
    info = await probe.from_file("/path/to/photo.jpg")
    print(info.format)    # "JPEG"

    # Из байтов
    info = await probe.from_bytes(b"...")
    print(info.format)    # определяется по сигнатуре

asyncio.run(main())
```

## Метаданные MediaInfo

```python
info = await probe.from_url("https://example.com/audio.mp3")

# Общее
print(f"Формат: {info.format}")    # "MP3"
print(f"Статус: {info.status}")    # "ok" | "partial" | "error"

# Изображения
if info.width and info.height:
    print(f"Размеры: {info.width}x{info.height}")

# Видео / аудио
if info.duration:
    print(f"Длительность: {info.duration} сек")
if info.fps:
    print(f"FPS: {info.fps}")
if info.bitrate_avg:
    print(f"Битрейт: {info.bitrate_avg} kbps")
if info.sample_rate:
    print(f"Sample rate: {info.sample_rate} Hz")

# Красивый вывод
print(info)
```

## Кодеки

```python
info = await probe.from_url("https://example.com/video.mp4")

# Краткие идентификаторы
print(info.video_codec)  # "h264"
print(info.audio_codec)  # "aac"

# Полные названия (из ffprobe)
print(info.video_codec_long)  # "H.264 / AVC / MPEG-4 part 10"
print(info.audio_codec_long)  # "Advanced Audio Coding"
```

## Как это работает

1. **Загрузка данных** -- `RangeDownloader` делает HTTP Range запросы:
   - Начинает с заголовков + первые 4KB данных
   - Если парсеру не хватило -- докачивает прогрессивно (4 -> 8 -> 16 -> ... -> 128 KB) или сколько попросит парсер, но не более заданного максимального размера.
   - Повторяет при 429/5xx ошибках
   - Всё в одном keep-alive соединении, кроме загрузки хвоста файла (tail), для него отдельное соединение.

2. **Парсинг** -- парсеры анализируют сигнатуры байт и заголовки файлов для определения формата, размеров, длительности, FPS, sample rate, битрейта.

3. **Результат** -- dataclass `MediaInfo` с полями: `format`, `status`, `width`, `height`, `duration`, `fps`, `sample_rate`, `bitrate_nominal`, `bitrate_avg`, `video_codec`, `audio_codec`, `parse_note`.

## Поддерживаемые форматы

**Изображения:** JPEG, PNG, GIF, WebP (VP8/VP8L/VP8X)

**Видео:** MP4/MOV, AVI, MKV, WebM, OGV

**Аудио:** MP3, AAC, WAV, WMA, FLAC, OGG, M4A

## Лицензия

LGPL-2.1
