MediaRouter / app /operations /compress.py
basyx's picture
Upload 142 files
fba6023 verified
Raw
History Blame Contribute Delete
3.06 kB
from __future__ import annotations
from collections.abc import Sequence
from pathlib import Path
from typing import Any
from app.core.exceptions import InputError
from app.models.media import InputMedia, OperationResult
from app.operations.common import (
VIDEO_FORMATS,
execute,
format_param,
output_path,
require_inputs,
video_codecs,
)
from app.services.ffmpeg_service import FFmpegService
from app.services.validator import bounded_number
async def compress_video(
ffmpeg: FFmpegService,
inputs: Sequence[InputMedia],
params: dict[str, Any],
output_dir: Path,
) -> OperationResult:
require_inputs(inputs)
extension = format_param(params, "mp4", VIDEO_FORMATS)
try:
crf = int(params.get("crf", 28))
except (TypeError, ValueError) as exc:
raise InputError("'crf' must be an integer") from exc
if not 0 <= crf <= 51:
raise InputError("'crf' must be between 0 and 51")
preset = str(params.get("preset", "medium"))
if preset not in {"ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow"}:
raise InputError("Unsupported x264 preset")
output = output_path(output_dir, "compressed", extension)
args: list[str | Path] = ["-i", inputs[0].temp_path]
scale = params.get("max_width")
if scale:
try:
max_width = int(scale)
except (TypeError, ValueError) as exc:
raise InputError("'max_width' must be an integer") from exc
args += ["-vf", f"scale='min({max_width},iw)':-2"]
args += video_codecs(extension, crf=crf, preset=preset)
if params.get("bitrate"):
bitrate = str(params["bitrate"])
if not bitrate.replace("k", "").replace("M", "").isdigit():
raise InputError("Invalid video bitrate")
args += ["-b:v", bitrate]
args += [output]
return await execute(ffmpeg, args, output, "video.compress", {"crf": crf, "format": extension})
async def normalize_video(
ffmpeg: FFmpegService,
inputs: Sequence[InputMedia],
params: dict[str, Any],
output_dir: Path,
) -> OperationResult:
require_inputs(inputs)
output = output_path(output_dir, "normalized", "mp4")
args: list[str | Path] = ["-i", inputs[0].temp_path]
if inputs[0].metadata.get("audio_streams"):
args += ["-af", "loudnorm=I=-16:LRA=11:TP=-1.5"]
args += [*video_codecs("mp4", crf=23), output]
return await execute(ffmpeg, args, output, "video.normalize")
async def normalize_audio(
ffmpeg: FFmpegService,
inputs: Sequence[InputMedia],
params: dict[str, Any],
output_dir: Path,
) -> OperationResult:
require_inputs(inputs)
target = bounded_number(params, "target_lufs", -16, -70, -5)
output = output_path(output_dir, "normalized", "wav")
args = [
"-i",
inputs[0].temp_path,
"-af",
f"loudnorm=I={target}:LRA=11:TP=-1.5",
"-c:a",
"pcm_s16le",
output,
]
return await execute(ffmpeg, args, output, "audio.normalize", {"target_lufs": target})