Документация url-media-probe
url-media-probe — Минималистичный асинхронный пакет для определения параметров медиафайла без полной загрузки.
По URL скачивает минимум данных: начальный фрагмент всего 4 КБ, дальше парсер сам решает, что докачать (ещё head — точным размером или прогрессивно, слайс moov от известной позиции или tail), и всё — в пределах лимита. Локальные файлы и bytes по умолчанию читаются целиком (до 20 МБ) — для анимированных GIF/WebP это даёт точную длительность без экстраполяции. Есть режим «только заголовки» (max_ratio=0 или max_total=0) — тело не скачивается вовсе.
Лимиты чтения
Лимиты задаются едиными параметрами max_total (максимум байт) и max_ratio (доля размера файла) во всех источниках:
| Источник | По умолчанию |
|---|---|
URL (from_url) |
max_total=256 000, max_ratio=0.1 → бюджет min(256 КБ, 10% размера) |
Файл (from_file) |
max_total=20 971 520, max_ratio=1.0 — читается целиком |
bytes (from_bytes) |
max_total=20 971 520, max_ratio=1.0 — читается целиком |
Общий бюджет — минимум из абсолютного и относительного лимитов: min(max_total, file_size · max_ratio), но не меньше начального head (INITIAL_HEAD, 4 КБ). Head, tail и слайс суммарно не превышают бюджет; каждая порция ограничена остатком.
- Начальный head (URL): минимум —
INITIAL_HEAD(4 КБ) или размер файла, если он меньше. Дальше head растёт только по запросу парсера: точный размер (need_head=moov_endу MP4 с moov в начале) или прогрессивно (4 → 8 → … → 128 КБ). - Tail (хвост файла): верхняя граница —
min(64 КБ, бюджет). Изначально читается конец файла (EOF), а при необходимости растёт: назад (если парсер запросил ещё хвоста) или вперёд от вычисленной позиции (например, атомmoovу потоковых MP4). Резервирования места в бюджете нет: head минимален, и бюджет тратится только на запрошенное парсером.
max_ratio=0 или max_total=0 (URL) → режим «только заголовки»: тело не скачивается, MediaInfo(status="ok") строится по HTTP-метаданным (Content-Type, Content-Length, имя файла).
Быстрый старт
pip install url-media-probe
Пример — определение видео по URL
import asyncio
from url_media_probe import MediaProbe
async def main():
probe = MediaProbe()
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"
asyncio.run(main())
Пример — определение локального файла
info = await probe.from_file("/path/to/photo.jpg")
print(info.format) # "JPEG"
Пример — определение из байтов
info = await probe.from_bytes(b"\xff\xd8\xff...")
print(info.format) # "JPEG"
Источники данных
| Источник | Метод | Описание |
|---|---|---|
| URL | from_url() |
HTTP: минимальный head (4 КБ), докачка по запросу парсера (head/tail/слайс), retry; max_ratio=0 → только заголовки |
| Локальный файл | from_file() |
Чтение целиком (до max_total = 20 МБ) |
| bytes / BytesIO | from_bytes() |
Чтение целиком (до max_total = 20 МБ) |
| Готовые head/tail | from_bytes_range() |
Парсинг по уже скачанным частям |
Метаданные MediaInfo
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")
# Кодеки
if info.video_codec:
print(f"Видео: {info.video_codec_long or info.video_codec}")
if info.audio_codec:
print(f"Аудио: {info.audio_codec_long or info.audio_codec}")
# Красивый вывод
print(info)
Как это работает
- Загрузка данных —
RangeDownloaderделает HTTP запросы: - Сначала GET-запрос: читает метаданные (
Content-Type,Content-Length, имя файла) и начальный фрагмент 4 КБ - Парсер смотрит на head и сам сообщает, что докачать: ещё head (точный размер или прогрессивно 4 → 8 → 16 → ... → 128 КБ), слайс от известной позиции (
moov) или tail — но не более общего бюджетаmin(max_total, file_size · max_ratio) - Хвост файла (tail) — конец файла или слайс от известной позиции (
moovу MP4), растёт по запросам парсера (до 64 КБ) - Повторяет при 429/5xx ошибках
- Всё в одном keep-alive соединении
max_ratio=0илиmax_total=0→ режим «только заголовки»: тело не скачивается,MediaInfo(status="ok")строится по HTTP-метаданным
Для локальных файлов и bytes по умолчанию max_ratio=1.0 и max_total=20 МБ — данные читаются целиком.
-
Парсинг — парсеры анализируют сигнатуры байт и заголовки файлов для определения формата, размеров, длительности, FPS, sample rate, битрейта
-
Результат — dataclass
MediaInfoс полями:format,status,width,height,duration,fps,sample_rate,bitrate_nominal,bitrate_avg,video_codec,audio_codec,parse_note
Никогда не бросает исключение. Все ошибки перехватываются внутри и отражаются в
statusиparse_note(см. Обработка ошибок).
Обработка ошибок
Никогда не бросает исключения — все ошибки попадают в
MediaInfo.status/parse_note.
status |
Что значит |
|---|---|
"ok" |
Все параметры определены |
"partial" |
Часть данных получена, чего-то не хватает |
"error" |
Формат не распознан, файл повреждён, данные не извлечены |
Типичные parse_note
parse_note |
Причина | Что делать |
|---|---|---|
Недостаточно данных для определения параметров |
Файл слишком мал (0–3 байт) | Увеличить max_total или max_ratio |
Файл не является медиа (HTML-страница) |
По ссылке HTML, а не медиафайл | Проверить URL |
Формат не поддерживается или не распознан |
Формат нет в парсерах | Использовать другой инструмент |
Сетевая ошибка: ... |
Таймаут, ConnectionError | Увеличить timeout, проверить сеть |
Не удалось определить длительность |
Нет Xing/Info фрейма (VBR MP3) или moov (MP4) | Увеличить max_total |
Файл не найден |
Локальный файл не существует | Проверить путь |
Проброс сетевых ошибок
По умолчанию сетевые ошибки (aiohttp.ClientError) тоже попадают в status / parse_note. Если удобнее работать с исключениями:
import url_media_probe.media_probe as cfg
from url_media_probe import MediaProbe
cfg.raise_on_network = True # глобально — ко всем экземплярам
probe = MediaProbe() # наследует глобальную настройку
await probe.from_url("https://example.com/broken.mp4")
# → бросает aiohttp.ClientError (timeout, ConnectionError и т.д.)
Локально — на уровне экземпляра:
probe = MediaProbe(raise_on_network=True)
probe_safe = MediaProbe(raise_on_network=False) # ловит ошибки как обычно
Парсерные ошибки (невалидный файл, неподдерживаемый формат) всегда остаются в
status/parse_note—raise_on_networkна них не влияет.
Подробнее — в разделе Обработка ошибок.
Поддерживаемые форматы
Изображения: JPEG, PNG, GIF, WebP (VP8/VP8L/VP8X)
Видео: MP4/MOV, AVI, MKV, WebM, OGV
Аудио: MP3, AAC, WAV, WMA, FLAC, OGG, M4A