fix(P0): 修复缩略图生成失败时临时文件泄漏 + 素材库视频缩略图生成
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 19s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 9s
AI Code Review / AI Code Review (pull_request) Failing after 29s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 56s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 4m28s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 5m23s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 9m46s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 10m19s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m8s
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 19s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 9s
AI Code Review / AI Code Review (pull_request) Failing after 29s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 56s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 4m28s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 5m23s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 9m46s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 10m19s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m8s
- ingest流程中添加视频缩略图生成(使用15%处抽帧) - 缩略图生成失败不影响主流程,仅打warning日志 - 修复extract_first_frame失败时临时文件未清理的bug - 修复素材库视频全部显示渐变占位图的问题 Issue: #577
This commit is contained in:
@@ -38,53 +38,36 @@ def extract_first_frame(
|
||||
"""
|
||||
from video_processing.ffmpeg_utils import FFMPEG_BIN, probe_duration, run_ffmpeg
|
||||
|
||||
_is_temp_output = False
|
||||
if output_path is None:
|
||||
tmp = tempfile.NamedTemporaryFile(suffix=".jpg", delete=False)
|
||||
tmp.close()
|
||||
output_path = tmp.name
|
||||
|
||||
# 计算抽帧时间点:取视频时长 * seek_ratio,最少 min_seek_seconds 秒
|
||||
try:
|
||||
duration = probe_duration(video_path)
|
||||
seek_time = max(min_seek_seconds, duration * seek_ratio)
|
||||
except Exception:
|
||||
# probe 失败时 fallback 到第1秒
|
||||
seek_time = min_seek_seconds
|
||||
|
||||
# 格式化为 HH:MM:SS.xx
|
||||
seek_str = _format_seek_time(seek_time)
|
||||
|
||||
# -ss 放在 -i 前面(input seeking,更快但精度稍低,缩略图够用)
|
||||
# -vframes 1 只取一帧
|
||||
# -q:v 2 jpeg 高质量
|
||||
scale_filter = f"scale={width}:{height}:force_original_aspect_ratio=decrease"
|
||||
cmd = [
|
||||
FFMPEG_BIN,
|
||||
"-y",
|
||||
"-ss",
|
||||
seek_str,
|
||||
"-i",
|
||||
video_path,
|
||||
"-vframes",
|
||||
"1",
|
||||
"-vf",
|
||||
scale_filter,
|
||||
"-q:v",
|
||||
"2",
|
||||
output_path,
|
||||
]
|
||||
_is_temp_output = True
|
||||
|
||||
try:
|
||||
run_ffmpeg(cmd, capture_output=True, timeout=timeout)
|
||||
except Exception:
|
||||
# 失败时退回到第0帧兜底
|
||||
cmd2 = [
|
||||
# 计算抽帧时间点:取视频时长 * seek_ratio,最少 min_seek_seconds 秒
|
||||
try:
|
||||
duration = probe_duration(video_path)
|
||||
seek_time = max(min_seek_seconds, duration * seek_ratio)
|
||||
except Exception:
|
||||
# probe 失败时 fallback 到第1秒
|
||||
seek_time = min_seek_seconds
|
||||
|
||||
# 格式化为 HH:MM:SS.xx
|
||||
seek_str = _format_seek_time(seek_time)
|
||||
|
||||
# -ss 放在 -i 前面(input seeking,更快但精度稍低,缩略图够用)
|
||||
# -vframes 1 只取一帧
|
||||
# -q:v 2 jpeg 高质量
|
||||
scale_filter = f"scale={width}:{height}:force_original_aspect_ratio=decrease"
|
||||
cmd = [
|
||||
FFMPEG_BIN,
|
||||
"-y",
|
||||
"-ss",
|
||||
seek_str,
|
||||
"-i",
|
||||
video_path,
|
||||
"-ss",
|
||||
"00:00:00",
|
||||
"-vframes",
|
||||
"1",
|
||||
"-vf",
|
||||
@@ -93,12 +76,40 @@ def extract_first_frame(
|
||||
"2",
|
||||
output_path,
|
||||
]
|
||||
run_ffmpeg(cmd2, capture_output=True, timeout=timeout)
|
||||
|
||||
if not Path(output_path).exists() or Path(output_path).stat().st_size == 0:
|
||||
raise RuntimeError(f"Thumbnail generation failed: {output_path}")
|
||||
try:
|
||||
run_ffmpeg(cmd, capture_output=True, timeout=timeout)
|
||||
except Exception:
|
||||
# 失败时退回到第0帧兜底
|
||||
cmd2 = [
|
||||
FFMPEG_BIN,
|
||||
"-y",
|
||||
"-i",
|
||||
video_path,
|
||||
"-ss",
|
||||
"00:00:00",
|
||||
"-vframes",
|
||||
"1",
|
||||
"-vf",
|
||||
scale_filter,
|
||||
"-q:v",
|
||||
"2",
|
||||
output_path,
|
||||
]
|
||||
run_ffmpeg(cmd2, capture_output=True, timeout=timeout)
|
||||
|
||||
return output_path
|
||||
if not Path(output_path).exists() or Path(output_path).stat().st_size == 0:
|
||||
raise RuntimeError(f"Thumbnail generation failed: {output_path}")
|
||||
|
||||
return output_path
|
||||
except Exception:
|
||||
# 失败时清理自己创建的临时文件
|
||||
if _is_temp_output and output_path:
|
||||
try:
|
||||
Path(output_path).unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
raise
|
||||
|
||||
|
||||
def _format_seek_time(seconds: float) -> str:
|
||||
|
||||
Reference in New Issue
Block a user