CMVR-AI-ANALYSIS/app/media.py

73 lines
2.4 KiB
Python
Raw Normal View History

2026-08-13 16:56:32 +08:00
import ipaddress
import socket
import subprocess
import uuid
from pathlib import Path
from urllib.parse import urlparse
import httpx
from app.config import settings
def _validate_remote_url(url: str) -> None:
parsed = urlparse(url)
if parsed.scheme not in {"http", "https"} or not parsed.hostname:
raise ValueError("Only HTTP or HTTPS media URLs are supported")
default_port = 443 if parsed.scheme == "https" else 80
for address in socket.getaddrinfo(parsed.hostname, parsed.port or default_port):
ip = ipaddress.ip_address(address[4][0])
if ip.is_loopback or ip.is_link_local or ip.is_multicast or ip.is_unspecified:
raise ValueError("The media URL resolves to a prohibited address")
async def download_media(url: str, suffix: str, max_bytes: int) -> Path:
_validate_remote_url(url)
settings.jobs_dir.mkdir(parents=True, exist_ok=True)
destination = settings.jobs_dir / f"{uuid.uuid4().hex}{suffix}"
total = 0
timeout = httpx.Timeout(settings.media_timeout_seconds)
try:
async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
async with client.stream("GET", url) as response:
response.raise_for_status()
with destination.open("wb") as output:
async for chunk in response.aiter_bytes(1024 * 1024):
total += len(chunk)
if total > max_bytes:
raise ValueError("Media file exceeds the configured size limit")
output.write(chunk)
if total == 0:
raise ValueError("Downloaded media file is empty")
return destination
except Exception:
destination.unlink(missing_ok=True)
raise
def decode_audio(path: Path, sample_rate: int = 16000) -> tuple[bytes, int]:
process = subprocess.run(
[
"ffmpeg",
"-v",
"error",
"-i",
str(path),
"-f",
"s16le",
"-acodec",
"pcm_s16le",
"-ac",
"1",
"-ar",
str(sample_rate),
"pipe:1",
],
capture_output=True,
check=False,
)
if process.returncode != 0 or not process.stdout:
error = process.stderr.decode("utf-8", errors="replace")[-500:]
raise ValueError(f"Unable to decode audio: {error}")
return process.stdout, sample_rate