fixed bugs
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import json
|
||||
import logging
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from .audio_metrics import (
|
||||
AudioAnalysisError,
|
||||
@@ -85,6 +86,126 @@ def _ensure_webm_cues_front(filepath: Path) -> None:
|
||||
tmp.unlink(missing_ok=True)
|
||||
|
||||
|
||||
_IOS_VIDEO_CODECS = {"h264", "hevc"}
|
||||
_IOS_AUDIO_CODECS = {"aac", "mp3", "ac3", "eac3", "alac"}
|
||||
|
||||
|
||||
def _probe_stream_codecs(filepath: Path) -> Tuple[Optional[str], Optional[str]]:
|
||||
ffprobe = shutil.which("ffprobe")
|
||||
if ffprobe is None:
|
||||
return None, None
|
||||
completed = subprocess.run(
|
||||
[
|
||||
ffprobe,
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-show_streams",
|
||||
"-of",
|
||||
"json",
|
||||
str(filepath),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
return None, None
|
||||
try:
|
||||
payload = json.loads(completed.stdout or "{}")
|
||||
except json.JSONDecodeError:
|
||||
return None, None
|
||||
video_codec = None
|
||||
audio_codec = None
|
||||
for stream in payload.get("streams") or []:
|
||||
codec_type = stream.get("codec_type")
|
||||
codec_name = stream.get("codec_name")
|
||||
if codec_type == "video" and video_codec is None and codec_name not in {"mjpeg", "png"}:
|
||||
video_codec = codec_name
|
||||
elif codec_type == "audio" and audio_codec is None:
|
||||
audio_codec = codec_name
|
||||
return video_codec, audio_codec
|
||||
|
||||
|
||||
def _needs_ios_compatible_transcode(video_codec: Optional[str], audio_codec: Optional[str]) -> bool:
|
||||
if video_codec is None and audio_codec is None:
|
||||
return False
|
||||
video_ok = video_codec is None or video_codec in _IOS_VIDEO_CODECS
|
||||
audio_ok = audio_codec is None or audio_codec in _IOS_AUDIO_CODECS
|
||||
return not (video_ok and audio_ok)
|
||||
|
||||
|
||||
def _ensure_ios_compatible_media(filepath: Path) -> None:
|
||||
"""Transcode AV1/VP9/Opus etc. to H.264/AAC so iOS AVPlayer can show video."""
|
||||
video_codec, audio_codec = _probe_stream_codecs(filepath)
|
||||
if not _needs_ios_compatible_transcode(video_codec, audio_codec):
|
||||
return
|
||||
ffmpeg = shutil.which("ffmpeg")
|
||||
if ffmpeg is None:
|
||||
logger.warning(
|
||||
"ffmpeg not found; skipping iOS-compatible transcode for %s (%s/%s)",
|
||||
filepath.name,
|
||||
video_codec,
|
||||
audio_codec,
|
||||
)
|
||||
return
|
||||
tmp = filepath.with_suffix(".ios_tmp.mp4")
|
||||
command = [
|
||||
ffmpeg,
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-y",
|
||||
"-i",
|
||||
str(filepath),
|
||||
"-map",
|
||||
"0:v:0",
|
||||
"-map",
|
||||
"0:a:0?",
|
||||
"-vf",
|
||||
"scale='min(1920,iw)':-2",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-preset",
|
||||
"veryfast",
|
||||
"-crf",
|
||||
"23",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-ac",
|
||||
"2",
|
||||
"-movflags",
|
||||
"+faststart",
|
||||
str(tmp),
|
||||
]
|
||||
logger.info(
|
||||
"Transcoding %s for iOS playback (%s/%s -> h264/aac)",
|
||||
filepath.name,
|
||||
video_codec,
|
||||
audio_codec,
|
||||
)
|
||||
try:
|
||||
completed = subprocess.run(command, capture_output=True, text=True, timeout=7200)
|
||||
if completed.returncode == 0 and tmp.is_file() and tmp.stat().st_size > 0:
|
||||
tmp.replace(filepath)
|
||||
logger.info("iOS-compatible transcode finished: %s", filepath.name)
|
||||
return
|
||||
logger.warning(
|
||||
"iOS-compatible transcode failed for %s: rc=%d stderr=%s",
|
||||
filepath.name,
|
||||
completed.returncode,
|
||||
(completed.stderr or "")[-400:],
|
||||
)
|
||||
except (subprocess.TimeoutExpired, OSError) as exc:
|
||||
logger.warning("iOS-compatible transcode error for %s: %s", filepath.name, exc)
|
||||
finally:
|
||||
tmp.unlink(missing_ok=True)
|
||||
|
||||
|
||||
class VideoProcessor:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -110,6 +231,7 @@ class VideoProcessor:
|
||||
work_path: Optional[Path] = None
|
||||
try:
|
||||
_ensure_webm_cues_front(media_path)
|
||||
_ensure_ios_compatible_media(media_path)
|
||||
if self.transcriber.available:
|
||||
work_path = self.settings.work_dir / f"{video_hash}-{uuid.uuid4().hex}.wav"
|
||||
extract_audio(media_path, work_path)
|
||||
|
||||
Reference in New Issue
Block a user