mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-04-09 23:36:47 +00:00
133 lines
4.5 KiB
Python
133 lines
4.5 KiB
Python
"""
|
|
PyAV audio padding processor.
|
|
|
|
Pads audio tracks with silence directly in-process (no HTTP).
|
|
Reuses the shared PyAV utilities from reflector.utils.audio_padding.
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
import tempfile
|
|
|
|
import av
|
|
import requests
|
|
|
|
from reflector.logger import logger
|
|
from reflector.processors.audio_padding import AudioPaddingProcessor, PaddingResponse
|
|
from reflector.processors.audio_padding_auto import AudioPaddingAutoProcessor
|
|
from reflector.utils.audio_padding import apply_audio_padding_to_file
|
|
|
|
S3_TIMEOUT = 60
|
|
|
|
|
|
class AudioPaddingPyavProcessor(AudioPaddingProcessor):
|
|
"""Audio padding processor using PyAV (no HTTP backend)."""
|
|
|
|
async def pad_track(
|
|
self,
|
|
track_url: str,
|
|
output_url: str,
|
|
start_time_seconds: float,
|
|
track_index: int,
|
|
) -> PaddingResponse:
|
|
"""Pad audio track with silence via PyAV.
|
|
|
|
Args:
|
|
track_url: Presigned GET URL for source audio track
|
|
output_url: Presigned PUT URL for output WebM
|
|
start_time_seconds: Amount of silence to prepend
|
|
track_index: Track index for logging
|
|
"""
|
|
if not track_url:
|
|
raise ValueError("track_url cannot be empty")
|
|
if start_time_seconds <= 0:
|
|
raise ValueError(
|
|
f"start_time_seconds must be positive, got {start_time_seconds}"
|
|
)
|
|
|
|
log = logger.bind(track_index=track_index, padding_seconds=start_time_seconds)
|
|
log.info("Starting local PyAV padding")
|
|
|
|
loop = asyncio.get_event_loop()
|
|
return await loop.run_in_executor(
|
|
None,
|
|
self._pad_track_blocking,
|
|
track_url,
|
|
output_url,
|
|
start_time_seconds,
|
|
track_index,
|
|
)
|
|
|
|
def _pad_track_blocking(
|
|
self,
|
|
track_url: str,
|
|
output_url: str,
|
|
start_time_seconds: float,
|
|
track_index: int,
|
|
) -> PaddingResponse:
|
|
"""Blocking padding work: download, pad with PyAV, upload."""
|
|
log = logger.bind(track_index=track_index, padding_seconds=start_time_seconds)
|
|
temp_dir = tempfile.mkdtemp()
|
|
input_path = None
|
|
output_path = None
|
|
|
|
try:
|
|
# Download source audio
|
|
log.info("Downloading track for local padding")
|
|
response = requests.get(track_url, stream=True, timeout=S3_TIMEOUT)
|
|
response.raise_for_status()
|
|
|
|
input_path = os.path.join(temp_dir, "track.webm")
|
|
total_bytes = 0
|
|
with open(input_path, "wb") as f:
|
|
for chunk in response.iter_content(chunk_size=8192):
|
|
if chunk:
|
|
f.write(chunk)
|
|
total_bytes += len(chunk)
|
|
log.info("Track downloaded", bytes=total_bytes)
|
|
|
|
# Apply padding using shared PyAV utility
|
|
output_path = os.path.join(temp_dir, "padded.webm")
|
|
with av.open(input_path) as in_container:
|
|
apply_audio_padding_to_file(
|
|
in_container,
|
|
output_path,
|
|
start_time_seconds,
|
|
track_index,
|
|
logger=logger,
|
|
)
|
|
|
|
file_size = os.path.getsize(output_path)
|
|
log.info("Local padding complete", size=file_size)
|
|
|
|
# Upload padded track
|
|
log.info("Uploading padded track to S3")
|
|
with open(output_path, "rb") as f:
|
|
upload_response = requests.put(output_url, data=f, timeout=S3_TIMEOUT)
|
|
upload_response.raise_for_status()
|
|
log.info("Upload complete", size=file_size)
|
|
|
|
return PaddingResponse(size=file_size)
|
|
|
|
except Exception as e:
|
|
log.error("Local padding failed", error=str(e), exc_info=True)
|
|
raise
|
|
finally:
|
|
if input_path and os.path.exists(input_path):
|
|
try:
|
|
os.unlink(input_path)
|
|
except Exception as e:
|
|
log.warning("Failed to cleanup input file", error=str(e))
|
|
if output_path and os.path.exists(output_path):
|
|
try:
|
|
os.unlink(output_path)
|
|
except Exception as e:
|
|
log.warning("Failed to cleanup output file", error=str(e))
|
|
try:
|
|
os.rmdir(temp_dir)
|
|
except Exception as e:
|
|
log.warning("Failed to cleanup temp directory", error=str(e))
|
|
|
|
|
|
AudioPaddingAutoProcessor.register("pyav", AudioPaddingPyavProcessor)
|