添加边转换边输出frame_urls
This commit is contained in:
@@ -29,6 +29,27 @@ else:
|
||||
FFMPEG_PATH = os.path.join(BASE_DIR, 'lib', 'ffmpeg', 'bin', 'ffmpeg')
|
||||
FFPROBE_PATH = os.path.join(BASE_DIR, 'lib', 'ffmpeg', 'bin', 'ffprobe')
|
||||
|
||||
def parse_ffmpeg_frame_progress(line, task_id, task_registry, task_lock):
|
||||
"""解析FFmpeg进度输出并更新任务状态"""
|
||||
import re
|
||||
# 匹配帧数:frame= 180 fps= 90
|
||||
frame_match = re.search(r'frame=\s*(\d+)', line)
|
||||
if frame_match:
|
||||
current_frame = int(frame_match.group(1))
|
||||
|
||||
# 更新任务状态的当前帧数
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['current_frames'] = current_frame
|
||||
|
||||
# 如果有总帧数估计,可以计算进度
|
||||
if 'estimated_total_frames' in task_registry[task_id]:
|
||||
total_frames = task_registry[task_id]['estimated_total_frames']
|
||||
if total_frames > 0:
|
||||
progress = min(80, int((current_frame / total_frames) * 60) + 20) # 20-80%为转换进度
|
||||
task_registry[task_id]['progress'] = progress
|
||||
|
||||
|
||||
def log_subprocess_output(pipe, task_id, task_registry, task_lock, prefix=""):
|
||||
"""从管道实时读取并记录日志"""
|
||||
if not pipe:
|
||||
@@ -38,12 +59,30 @@ def log_subprocess_output(pipe, task_id, task_registry, task_lock, prefix=""):
|
||||
if line:
|
||||
clean_line = line.strip()
|
||||
if clean_line and task_id and task_registry and task_lock:
|
||||
add_task_log(task_id, f"{prefix}{clean_line}", task_registry, task_lock)
|
||||
# 过滤掉一些不必要的进度字符
|
||||
if '\r' in clean_line:
|
||||
clean_line = clean_line.replace('\r', '')
|
||||
|
||||
# 过滤掉过于频繁的进度更新(只记录有意义的内容)
|
||||
if prefix.startswith("[yt-dlp") and clean_line.startswith('['):
|
||||
# 只记录重要的进度信息,过滤掉过于频繁的百分比更新
|
||||
if '%' not in clean_line or '100%' in clean_line or 'Downloading' in clean_line:
|
||||
add_task_log(task_id, f"{prefix}{clean_line}", task_registry, task_lock)
|
||||
elif prefix == "[FFmpeg] " and "frame=" in clean_line:
|
||||
# FFmpeg进度信息
|
||||
add_task_log(task_id, f"{prefix}{clean_line}", task_registry, task_lock)
|
||||
parse_ffmpeg_frame_progress(clean_line, task_id, task_registry, task_lock)
|
||||
else:
|
||||
# 其他日志正常记录
|
||||
add_task_log(task_id, f"{prefix}{clean_line}", task_registry, task_lock)
|
||||
except Exception as e:
|
||||
if task_id and task_registry and task_lock:
|
||||
add_task_log(task_id, f"[日志读取错误] {e}", task_registry, task_lock)
|
||||
finally:
|
||||
pipe.close()
|
||||
try:
|
||||
pipe.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
# ======================
|
||||
# 任务处理函数
|
||||
@@ -104,27 +143,46 @@ def process_video_frame_extraction(data, file_registry, file_lock, task_id=None,
|
||||
# === 替换 yt-dlp 下载部分 ===
|
||||
if task_id and task_registry and task_lock:
|
||||
add_task_log(task_id, f"开始下载视频: {video_url}", task_registry, task_lock)
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['progress'] = 20 # 开始下载,进度20%
|
||||
|
||||
yt_dlp_cmd = [
|
||||
sys.executable, '-m', 'yt_dlp',
|
||||
video_url,
|
||||
'-o', temp_base,
|
||||
'-f', 'bv*[height<=720]+ba/b',
|
||||
'--no-warnings',
|
||||
'--progress', # 启用进度显示
|
||||
'--newline', # 确保换行符正常
|
||||
'--console-title', # 确保进度信息正确输出
|
||||
'--no-colors', # 禁用颜色,避免控制字符干扰
|
||||
]
|
||||
|
||||
# 使用 Popen 实时捕获 stderr
|
||||
# 使用 Popen 实时捕获 stdout 和 stderr
|
||||
proc = subprocess.Popen(
|
||||
yt_dlp_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
universal_newlines=True
|
||||
universal_newlines=True,
|
||||
encoding='utf-8',
|
||||
errors='replace'
|
||||
)
|
||||
|
||||
# 启动日志线程(只读 stderr,因为 yt-dlp 进度在 stderr)
|
||||
# 启动日志线程(yt-dlp 进度主要在 stdout)
|
||||
stdout_thread = threading.Thread(
|
||||
target=log_subprocess_output,
|
||||
args=(proc.stdout, task_id, task_registry, task_lock, "[yt-dlp] "),
|
||||
daemon=True
|
||||
)
|
||||
stdout_thread.start()
|
||||
|
||||
# stderr 也可能有重要信息
|
||||
stderr_thread = threading.Thread(
|
||||
target=log_subprocess_output,
|
||||
args=(proc.stderr, task_id, task_registry, task_lock, "[yt-dlp] "),
|
||||
args=(proc.stderr, task_id, task_registry, task_lock, "[yt-dlp-error] "),
|
||||
daemon=True
|
||||
)
|
||||
stderr_thread.start()
|
||||
@@ -132,12 +190,34 @@ def process_video_frame_extraction(data, file_registry, file_lock, task_id=None,
|
||||
try:
|
||||
proc.wait(timeout=600)
|
||||
if proc.returncode != 0:
|
||||
raise subprocess.CalledProcessError(proc.returncode, yt_dlp_cmd)
|
||||
# 获取更详细的错误信息
|
||||
error_details = []
|
||||
try:
|
||||
if proc.stderr:
|
||||
stderr_content = proc.stderr.read()
|
||||
if stderr_content:
|
||||
error_details.append(f"stderr: {stderr_content}")
|
||||
if proc.stdout:
|
||||
stdout_content = proc.stdout.read()
|
||||
if stdout_content:
|
||||
error_details.append(f"stdout: {stdout_content}")
|
||||
except:
|
||||
pass
|
||||
|
||||
error_msg = f"yt-dlp 下载失败 (返回码: {proc.returncode})"
|
||||
if error_details:
|
||||
error_msg += f" - {' '.join(error_details[:500])}" # 限制错误信息长度
|
||||
|
||||
raise subprocess.CalledProcessError(proc.returncode, yt_dlp_cmd, output=error_msg)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
if task_id and task_registry and task_lock:
|
||||
add_task_log(task_id, "yt-dlp 下载超时(超过10分钟)", task_registry, task_lock)
|
||||
raise Exception("yt-dlp 下载超时(超过10分钟)")
|
||||
finally:
|
||||
stderr_thread.join(timeout=5) # 等待日志线程结束
|
||||
# 等待日志线程结束
|
||||
stdout_thread.join(timeout=5)
|
||||
stderr_thread.join(timeout=5)
|
||||
|
||||
# 查找实际生成的文件
|
||||
candidates = glob.glob(temp_base + ".*")
|
||||
@@ -153,9 +233,79 @@ def process_video_frame_extraction(data, file_registry, file_lock, task_id=None,
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['progress'] = 40
|
||||
|
||||
# 首先处理音频(在视频处理之前)
|
||||
audio_exists = has_audio(temp_video)
|
||||
audio_urls = {}
|
||||
|
||||
if audio_exists:
|
||||
if task_id and task_registry and task_lock:
|
||||
add_task_log(task_id, "提取音频(左/右/混合声道)", task_registry, task_lock)
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['progress'] = 40
|
||||
|
||||
# 公共参数
|
||||
dfpwm_args = [
|
||||
FFMPEG_PATH, '-y',
|
||||
'-i',temp_video,
|
||||
'-vn',
|
||||
'-ar', '48000',
|
||||
'-ac', '1',
|
||||
'-f', 'dfpwm'
|
||||
]
|
||||
|
||||
# 混合声道
|
||||
dfpwm_path_mix = os.path.join(job_dir, "audio.dfpwm")
|
||||
subprocess.run(
|
||||
dfpwm_args + [dfpwm_path_mix],
|
||||
check=True, capture_output=True
|
||||
)
|
||||
|
||||
# 左声道
|
||||
dfpwm_path_left = os.path.join(job_dir, "audio_left.dfpwm")
|
||||
subprocess.run(
|
||||
dfpwm_args + ['-af', 'pan=mono|c0=c0'] + [dfpwm_path_left],
|
||||
check=True, capture_output=True
|
||||
)
|
||||
|
||||
# 右声道
|
||||
dfpwm_path_right = os.path.join(job_dir, "audio_right.dfpwm")
|
||||
subprocess.run(
|
||||
dfpwm_args + ['-af', 'pan=mono|c0=c1'] + [dfpwm_path_right],
|
||||
check=True, capture_output=True
|
||||
)
|
||||
|
||||
audio_urls = {
|
||||
"audio_dfpwm_url": f"/frames/{task_id}/audio.dfpwm",
|
||||
"audio_dfpwm_left_url": f"/frames/{task_id}/audio_left.dfpwm",
|
||||
"audio_dfpwm_right_url": f"/frames/{task_id}/audio_right.dfpwm"
|
||||
}
|
||||
|
||||
# 更新音频URL到任务状态
|
||||
if task_id and task_registry and task_lock:
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['audio_urls'] = audio_urls
|
||||
|
||||
# 获取视频信息
|
||||
duration = get_video_duration(temp_video)
|
||||
|
||||
# 估计总帧数
|
||||
estimated_total_frames = int(duration * fps)
|
||||
|
||||
# 初始化任务状态的帧相关字段
|
||||
if task_id and task_registry and task_lock:
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['estimated_total_frames'] = estimated_total_frames
|
||||
task_registry[task_id]['current_frames'] = 0
|
||||
task_registry[task_id]['generated_frame_urls'] = []
|
||||
task_registry[task_id]['frame_job_dir'] = job_dir
|
||||
task_registry[task_id]['frame_params'] = {
|
||||
'w': w, 'h': h, 'fps': fps,
|
||||
'force_resolution': force_resolution, 'pad_to_target': pad_to_target
|
||||
}
|
||||
|
||||
# 构建滤镜
|
||||
vf = build_video_filter(w, h, fps, force_resolution, pad_to_target)
|
||||
|
||||
@@ -212,46 +362,12 @@ def process_video_frame_extraction(data, file_registry, file_lock, task_id=None,
|
||||
out_w, out_h = get_output_resolution(job_dir)
|
||||
total_frames = len([f for f in os.listdir(job_dir) if f.endswith('.png')])
|
||||
|
||||
# 检查音频
|
||||
audio_exists = has_audio(temp_video)
|
||||
|
||||
if audio_exists:
|
||||
if task_id and task_registry and task_lock:
|
||||
add_task_log(task_id, "提取音频(左/右/混合声道)", task_registry, task_lock)
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['progress'] = 90
|
||||
|
||||
# 公共参数
|
||||
dfpwm_args = [
|
||||
FFMPEG_PATH, '-y',
|
||||
'-i', temp_video,
|
||||
'-vn',
|
||||
'-ar', '48000',
|
||||
'-ac', '1',
|
||||
'-f', 'dfpwm'
|
||||
]
|
||||
|
||||
# 混合声道
|
||||
dfpwm_path_mix = os.path.join(job_dir, "audio.dfpwm")
|
||||
subprocess.run(
|
||||
dfpwm_args + [dfpwm_path_mix],
|
||||
check=True, capture_output=True
|
||||
)
|
||||
|
||||
# 左声道
|
||||
dfpwm_path_left = os.path.join(job_dir, "audio_left.dfpwm")
|
||||
subprocess.run(
|
||||
dfpwm_args + ['-af', 'pan=mono|c0=c0'] + [dfpwm_path_left],
|
||||
check=True, capture_output=True
|
||||
)
|
||||
|
||||
# 右声道
|
||||
dfpwm_path_right = os.path.join(job_dir, "audio_right.dfpwm")
|
||||
subprocess.run(
|
||||
dfpwm_args + ['-af', 'pan=mono|c0=c1'] + [dfpwm_path_right],
|
||||
check=True, capture_output=True
|
||||
)
|
||||
# 保存输出分辨率和实际总帧数到任务状态
|
||||
if task_id and task_registry and task_lock:
|
||||
with task_lock:
|
||||
if task_id in task_registry:
|
||||
task_registry[task_id]['output_resolution'] = {"w": out_w, "h": out_h}
|
||||
task_registry[task_id]['estimated_total_frames'] = total_frames
|
||||
|
||||
# 生成结果
|
||||
result_data = {
|
||||
|
||||
Reference in New Issue
Block a user