continue fixing

This commit is contained in:
2026-08-26 11:22:55 +08:00
parent a6297e2f9f
commit b1d3a3bd5a
5 changed files with 169 additions and 31 deletions

View File

@@ -432,9 +432,20 @@ def create_app(
audio_name = f"{uuid.uuid4().hex}{suffix}"
audio_path = service_settings.dub_shares_dir / audio_name
await run_in_threadpool(_persist_upload, upload.file, audio_path, 50 * 1024 * 1024)
await run_in_threadpool(_trim_leading_silence, audio_path)
await run_in_threadpool(_align_share_audio, audio_path, boundary.end_ms / 1000, moss)
saved_files.append(audio_path)
score = scores_by_index.get(sentence_index, {})
score_details = {
key: float(score[key])
for key in (
"content_score",
"fluency_score",
"duration_score",
"pause_score",
"speech_rate_score",
)
if score.get(key) is not None
}
prepared_segments.append({
"sentence_index": sentence_index,
"start_ms": boundary.start_ms,
@@ -444,6 +455,7 @@ def create_app(
"audio_size_bytes": audio_path.stat().st_size,
"overall_score": float(score.get("overall_score", 0)),
"recognized_text": str(score.get("recognized_text") or ""),
"score_details": json.dumps(score_details, ensure_ascii=False),
})
share = video_repository.create_dub_share(
video_hash=video_hash.lower(),
@@ -485,6 +497,7 @@ def create_app(
"text": row["text"],
"overall_score": row["overall_score"],
"recognized_text": row["recognized_text"],
"score_details": json.loads(row["score_details"] or "{}"),
"audio_url": f"/api/v1/dub-shares/{share_id}/audio/{row['audio_filename']}",
}
for row in share["segments"]
@@ -540,32 +553,73 @@ def _persist_upload(source: BinaryIO, destination: Path, max_bytes: int) -> tupl
return digest.hexdigest(), size
def _trim_leading_silence(audio_path: Path) -> None:
def _align_share_audio(audio_path: Path, target_seconds: float, transcriber: Transcriber) -> None:
ffmpeg = shutil.which("ffmpeg")
if ffmpeg is None:
return
trimmed_path = audio_path.with_name(f"{audio_path.stem}-trimmed{audio_path.suffix}")
command = [
ffmpeg,
"-hide_banner",
"-loglevel",
"error",
"-y",
"-i",
str(audio_path),
"-af",
"silenceremove=start_periods=1:start_threshold=-45dB:start_silence=0.08",
"-c:a",
"aac",
str(trimmed_path),
]
aligned_path = audio_path.with_name(f"{audio_path.stem}-aligned{audio_path.suffix}")
try:
transcript = transcriber.transcribe(audio_path)
speech_segments = [segment for segment in transcript.segments if segment.text.strip()]
if not speech_segments:
return
start_seconds = max(0.0, min(segment.start_seconds for segment in speech_segments) - 0.05)
end_seconds = min(
_media_duration_seconds(audio_path),
max(segment.end_seconds for segment in speech_segments) + 0.08,
)
if end_seconds <= start_seconds + 0.1:
return
filters: List[str] = [
"silenceremove=start_periods=1:start_threshold=-45dB:start_silence=0.03"
]
if target_seconds > 0.2:
speed_factor = (end_seconds - start_seconds) / target_seconds
if 0.5 <= speed_factor <= 2.0:
filters.append(f"atempo={speed_factor:.6f}")
command = [
ffmpeg,
"-hide_banner",
"-loglevel",
"error",
"-y",
"-ss",
f"{start_seconds:.3f}",
"-t",
f"{end_seconds - start_seconds:.3f}",
"-i",
str(audio_path),
"-af",
",".join(filters),
"-c:a",
"aac",
str(aligned_path),
]
subprocess.run(command, check=True)
if trimmed_path.stat().st_size == 0:
raise RuntimeError("Trimmed audio is empty.")
trimmed_path.replace(audio_path)
except (OSError, subprocess.SubprocessError):
trimmed_path.unlink(missing_ok=True)
if aligned_path.stat().st_size > 0:
aligned_path.replace(audio_path)
except Exception:
logger.warning("Could not align share audio %s; keeping original.", audio_path.name, exc_info=True)
finally:
aligned_path.unlink(missing_ok=True)
def _media_duration_seconds(audio_path: Path) -> float:
ffprobe = shutil.which("ffprobe")
if ffprobe is None:
return 0.0
try:
completed = subprocess.run(
[ffprobe, "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", str(audio_path)],
check=True,
capture_output=True,
text=True,
)
return float(completed.stdout.strip())
except (OSError, ValueError, subprocess.SubprocessError):
return 0.0
async def _persist_request_stream(