mirror of
https://github.com/linyqh/NarratoAI.git
synced 2026-01-25 21:38:12 +00:00
refactor(app): 重构视频剪辑功能并优化性能
- 移除了未使用的性能监控模块- 重新实现了硬件加速检测逻辑 - 优化了 FFmpeg命令生成和执行流程- 改进了视频文件命名规则 - 调整了错误处理和日志记录方式
This commit is contained in:
parent
bacc1adfad
commit
0ccb019f88
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -7,7 +7,7 @@ from datetime import datetime
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from moviepy.video.io.VideoFileClip import VideoFileClip
|
from moviepy.video.io.VideoFileClip import VideoFileClip
|
||||||
|
|
||||||
@ -307,7 +307,50 @@ def format_timestamp(seconds: float) -> str:
|
|||||||
return f"{hours:02d}:{minutes:02d}:{whole_seconds:02d},{milliseconds:03d}"
|
return f"{hours:02d}:{minutes:02d}:{whole_seconds:02d},{milliseconds:03d}"
|
||||||
|
|
||||||
|
|
||||||
def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> dict:
|
def _detect_hardware_acceleration() -> Optional[str]:
|
||||||
|
"""
|
||||||
|
检测系统可用的硬件加速器
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[str]: 硬件加速参数,如果不支持则返回None
|
||||||
|
"""
|
||||||
|
# 检查NVIDIA GPU支持
|
||||||
|
try:
|
||||||
|
nvidia_check = subprocess.run(
|
||||||
|
["ffmpeg", "-hwaccel", "cuda", "-i", "/dev/null", "-f", "null", "-"],
|
||||||
|
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||||||
|
)
|
||||||
|
if nvidia_check.returncode == 0:
|
||||||
|
return "cuda"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 检查MacOS videotoolbox支持
|
||||||
|
try:
|
||||||
|
videotoolbox_check = subprocess.run(
|
||||||
|
["ffmpeg", "-hwaccel", "videotoolbox", "-i", "/dev/null", "-f", "null", "-"],
|
||||||
|
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||||||
|
)
|
||||||
|
if videotoolbox_check.returncode == 0:
|
||||||
|
return "videotoolbox"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 检查Intel Quick Sync支持
|
||||||
|
try:
|
||||||
|
qsv_check = subprocess.run(
|
||||||
|
["ffmpeg", "-hwaccel", "qsv", "-i", "/dev/null", "-f", "null", "-"],
|
||||||
|
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||||||
|
)
|
||||||
|
if qsv_check.returncode == 0:
|
||||||
|
return "qsv"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> str:
|
||||||
"""
|
"""
|
||||||
保存剪辑后的视频
|
保存剪辑后的视频
|
||||||
|
|
||||||
@ -329,20 +372,25 @@ def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> di
|
|||||||
if not os.path.exists(save_dir):
|
if not os.path.exists(save_dir):
|
||||||
os.makedirs(save_dir)
|
os.makedirs(save_dir)
|
||||||
|
|
||||||
# 生成更规范的视频文件名
|
# 解析时间戳
|
||||||
video_id = f"vid-{timestamp.replace(':', '-').replace(',', '_')}"
|
start_str, end_str = timestamp.split('-')
|
||||||
video_path = os.path.join(save_dir, f"{video_id}.mp4")
|
|
||||||
|
# 格式化输出文件名(使用连字符替代冒号和逗号)
|
||||||
|
safe_start_time = start_str.replace(':', '-').replace(',', '-')
|
||||||
|
safe_end_time = end_str.replace(':', '-').replace(',', '-')
|
||||||
|
output_filename = f"vid_{safe_start_time}@{safe_end_time}.mp4"
|
||||||
|
video_path = os.path.join(save_dir, output_filename)
|
||||||
|
|
||||||
# 如果视频已存在,直接返回
|
# 如果视频已存在,直接返回
|
||||||
if os.path.exists(video_path) and os.path.getsize(video_path) > 0:
|
if os.path.exists(video_path) and os.path.getsize(video_path) > 0:
|
||||||
logger.info(f"视频已存在: {video_path}")
|
logger.info(f"视频已存在: {video_path}")
|
||||||
return {timestamp: video_path}
|
return video_path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 检查视频是否存在
|
# 检查视频是否存在
|
||||||
if not os.path.exists(origin_video):
|
if not os.path.exists(origin_video):
|
||||||
logger.error(f"源视频文件不存在: {origin_video}")
|
logger.error(f"源视频文件不存在: {origin_video}")
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
# 获取视频总时长
|
# 获取视频总时长
|
||||||
try:
|
try:
|
||||||
@ -351,17 +399,16 @@ def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> di
|
|||||||
total_duration = float(subprocess.check_output(probe_cmd).decode('utf-8').strip())
|
total_duration = float(subprocess.check_output(probe_cmd).decode('utf-8').strip())
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logger.error(f"获取视频时长失败: {str(e)}")
|
logger.error(f"获取视频时长失败: {str(e)}")
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
# 解析时间戳
|
# 计算时间点
|
||||||
start_str, end_str = timestamp.split('-')
|
|
||||||
start = time_to_seconds(start_str)
|
start = time_to_seconds(start_str)
|
||||||
end = time_to_seconds(end_str)
|
end = time_to_seconds(end_str)
|
||||||
|
|
||||||
# 验证时间段
|
# 验证时间段
|
||||||
if start >= total_duration:
|
if start >= total_duration:
|
||||||
logger.warning(f"起始时间 {format_timestamp(start)} ({start:.3f}秒) 超出视频总时长 {format_timestamp(total_duration)} ({total_duration:.3f}秒)")
|
logger.warning(f"起始时间 {format_timestamp(start)} ({start:.3f}秒) 超出视频总时长 {format_timestamp(total_duration)} ({total_duration:.3f}秒)")
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
if end > total_duration:
|
if end > total_duration:
|
||||||
logger.warning(f"结束时间 {format_timestamp(end)} ({end:.3f}秒) 超出视频总时长 {format_timestamp(total_duration)} ({total_duration:.3f}秒),将自动调整为视频结尾")
|
logger.warning(f"结束时间 {format_timestamp(end)} ({end:.3f}秒) 超出视频总时长 {format_timestamp(total_duration)} ({total_duration:.3f}秒),将自动调整为视频结尾")
|
||||||
@ -369,73 +416,38 @@ def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> di
|
|||||||
|
|
||||||
if end <= start:
|
if end <= start:
|
||||||
logger.warning(f"结束时间 {format_timestamp(end)} 必须大于起始时间 {format_timestamp(start)}")
|
logger.warning(f"结束时间 {format_timestamp(end)} 必须大于起始时间 {format_timestamp(start)}")
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
# 计算剪辑时长
|
# 计算剪辑时长
|
||||||
duration = end - start
|
duration = end - start
|
||||||
logger.info(f"开始剪辑视频: {format_timestamp(start)} - {format_timestamp(end)},时长 {format_timestamp(duration)}")
|
# logger.info(f"开始剪辑视频: {format_timestamp(start)} - {format_timestamp(end)},时长 {format_timestamp(duration)}")
|
||||||
|
|
||||||
# 检测可用的硬件加速选项
|
# 检测可用的硬件加速选项
|
||||||
hwaccel = _detect_hardware_acceleration()
|
hwaccel = _detect_hardware_acceleration()
|
||||||
|
hwaccel_args = []
|
||||||
# 构建ffmpeg命令
|
|
||||||
ffmpeg_cmd = ["ffmpeg", "-y"]
|
|
||||||
|
|
||||||
# 添加硬件加速参数(如果可用)
|
|
||||||
if hwaccel:
|
if hwaccel:
|
||||||
if hwaccel == "cuda":
|
hwaccel_args = ["-hwaccel", hwaccel]
|
||||||
ffmpeg_cmd.extend(["-hwaccel", "cuda"])
|
|
||||||
elif hwaccel == "videotoolbox": # macOS
|
|
||||||
ffmpeg_cmd.extend(["-hwaccel", "videotoolbox"])
|
|
||||||
elif hwaccel == "qsv": # Intel Quick Sync
|
|
||||||
ffmpeg_cmd.extend(["-hwaccel", "qsv"])
|
|
||||||
elif hwaccel == "vaapi": # Linux VA-API
|
|
||||||
ffmpeg_cmd.extend(["-hwaccel", "vaapi", "-vaapi_device", "/dev/dri/renderD128"])
|
|
||||||
elif hwaccel == "dxva2": # Windows DXVA2
|
|
||||||
ffmpeg_cmd.extend(["-hwaccel", "dxva2"])
|
|
||||||
logger.info(f"使用硬件加速: {hwaccel}")
|
logger.info(f"使用硬件加速: {hwaccel}")
|
||||||
|
|
||||||
# 设置输入选项和精确剪辑时间范围
|
# 转换为FFmpeg兼容的时间格式(逗号替换为点)
|
||||||
ffmpeg_cmd.extend([
|
ffmpeg_start_time = start_str.replace(',', '.')
|
||||||
"-ss", str(start), # 从这个时间点开始
|
ffmpeg_end_time = end_str.replace(',', '.')
|
||||||
"-t", str(duration), # 剪辑的持续时间
|
|
||||||
"-i", origin_video, # 输入文件
|
|
||||||
"-map_metadata", "-1" # 移除元数据
|
|
||||||
])
|
|
||||||
|
|
||||||
# 设置视频编码参数
|
# 构建FFmpeg命令
|
||||||
if hwaccel == "cuda":
|
ffmpeg_cmd = [
|
||||||
ffmpeg_cmd.extend(["-c:v", "h264_nvenc", "-preset", "p4", "-profile:v", "high"])
|
"ffmpeg", "-y", *hwaccel_args,
|
||||||
elif hwaccel == "videotoolbox":
|
"-i", origin_video,
|
||||||
ffmpeg_cmd.extend(["-c:v", "h264_videotoolbox", "-profile:v", "high"])
|
"-ss", ffmpeg_start_time,
|
||||||
elif hwaccel == "qsv":
|
"-to", ffmpeg_end_time,
|
||||||
ffmpeg_cmd.extend(["-c:v", "h264_qsv", "-preset", "medium"])
|
"-c:v", "h264_videotoolbox" if hwaccel == "videotoolbox" else "libx264",
|
||||||
elif hwaccel == "vaapi":
|
"-c:a", "aac",
|
||||||
ffmpeg_cmd.extend(["-c:v", "h264_vaapi", "-profile", "high"])
|
"-strict", "experimental",
|
||||||
else:
|
video_path
|
||||||
ffmpeg_cmd.extend(["-c:v", "libx264", "-preset", "medium", "-profile:v", "high"])
|
]
|
||||||
|
|
||||||
# 音频编码参数(检查是否有音频流)
|
# 执行FFmpeg命令
|
||||||
audio_check_cmd = ["ffprobe", "-i", origin_video, "-show_streams", "-select_streams", "a",
|
logger.info(f"裁剪视频片段: {timestamp} -> {ffmpeg_start_time}到{ffmpeg_end_time}")
|
||||||
"-loglevel", "error", "-print_format", "json"]
|
# logger.debug(f"执行命令: {' '.join(ffmpeg_cmd)}")
|
||||||
audio_result = subprocess.run(audio_check_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
||||||
audio_info = json.loads(audio_result.stdout) if audio_result.stdout else {"streams": []}
|
|
||||||
has_audio = len(audio_info.get("streams", [])) > 0
|
|
||||||
|
|
||||||
if has_audio:
|
|
||||||
ffmpeg_cmd.extend(["-c:a", "aac", "-b:a", "128k"])
|
|
||||||
else:
|
|
||||||
ffmpeg_cmd.extend(["-an"]) # 没有音频
|
|
||||||
|
|
||||||
# 设置输出视频参数
|
|
||||||
ffmpeg_cmd.extend([
|
|
||||||
"-pix_fmt", "yuv420p", # 兼容性更好的颜色格式
|
|
||||||
"-movflags", "+faststart", # 优化MP4文件结构以便快速开始播放
|
|
||||||
video_path # 输出文件
|
|
||||||
])
|
|
||||||
|
|
||||||
# 执行ffmpeg命令
|
|
||||||
logger.debug(f"执行命令: {' '.join(ffmpeg_cmd)}")
|
|
||||||
|
|
||||||
process = subprocess.run(
|
process = subprocess.run(
|
||||||
ffmpeg_cmd,
|
ffmpeg_cmd,
|
||||||
@ -450,7 +462,7 @@ def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> di
|
|||||||
logger.error(f"视频剪辑失败: {process.stderr}")
|
logger.error(f"视频剪辑失败: {process.stderr}")
|
||||||
if os.path.exists(video_path):
|
if os.path.exists(video_path):
|
||||||
os.remove(video_path)
|
os.remove(video_path)
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
# 验证生成的视频文件
|
# 验证生成的视频文件
|
||||||
if os.path.exists(video_path) and os.path.getsize(video_path) > 0:
|
if os.path.exists(video_path) and os.path.getsize(video_path) > 0:
|
||||||
@ -460,68 +472,18 @@ def save_clip_video(timestamp: str, origin_video: str, save_dir: str = "") -> di
|
|||||||
|
|
||||||
if validate_result.returncode == 0:
|
if validate_result.returncode == 0:
|
||||||
logger.info(f"视频剪辑成功: {video_path}")
|
logger.info(f"视频剪辑成功: {video_path}")
|
||||||
return {timestamp: video_path}
|
return video_path
|
||||||
|
|
||||||
logger.error("视频文件验证失败")
|
logger.error("视频文件验证失败")
|
||||||
if os.path.exists(video_path):
|
if os.path.exists(video_path):
|
||||||
os.remove(video_path)
|
os.remove(video_path)
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"视频剪辑过程中发生错误: \n{str(traceback.format_exc())}")
|
logger.error(f"视频剪辑过程中发生错误: \n{str(traceback.format_exc())}")
|
||||||
if os.path.exists(video_path):
|
if os.path.exists(video_path):
|
||||||
os.remove(video_path)
|
os.remove(video_path)
|
||||||
return {}
|
return ''
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def _detect_hardware_acceleration() -> str:
|
|
||||||
"""
|
|
||||||
检测系统可用的硬件加速器
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 可用的硬件加速类型,如果没有找到返回空字符串
|
|
||||||
"""
|
|
||||||
import platform
|
|
||||||
system = platform.system().lower()
|
|
||||||
|
|
||||||
# 测试常见的硬件加速类型
|
|
||||||
acceleration_types = []
|
|
||||||
|
|
||||||
if system == 'darwin': # macOS
|
|
||||||
acceleration_types = ["videotoolbox"]
|
|
||||||
elif system == 'linux':
|
|
||||||
acceleration_types = ["vaapi", "cuda", "nvenc"]
|
|
||||||
elif system == 'windows':
|
|
||||||
acceleration_types = ["cuda", "nvenc", "dxva2", "qsv"]
|
|
||||||
|
|
||||||
for accel in acceleration_types:
|
|
||||||
test_cmd = [
|
|
||||||
"ffmpeg",
|
|
||||||
"-hide_banner",
|
|
||||||
"-loglevel", "error",
|
|
||||||
"-hwaccel", accel,
|
|
||||||
"-i", "/dev/null", # 这不是实际文件,但是足以测试硬件加速器是否可用
|
|
||||||
"-f", "null",
|
|
||||||
"-"
|
|
||||||
]
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = subprocess.run(test_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
|
|
||||||
# 某些硬件加速器会报错,但仍然可以使用,我们主要检查的是CUDA和类似的错误
|
|
||||||
stderr = result.stderr.decode('utf-8', errors='ignore')
|
|
||||||
if result.returncode == 0 or (
|
|
||||||
"No such file or directory" in stderr and
|
|
||||||
not any(x in stderr for x in ["Invalid", "Error", "not supported"])
|
|
||||||
):
|
|
||||||
logger.info(f"检测到可用的硬件加速器: {accel}")
|
|
||||||
return accel
|
|
||||||
except (subprocess.SubprocessError, OSError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.info("未检测到可用的硬件加速器,将使用软件编码")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def clip_videos(task_id: str, timestamp_terms: List[str], origin_video: str, progress_callback=None) -> dict:
|
def clip_videos(task_id: str, timestamp_terms: List[str], origin_video: str, progress_callback=None) -> dict:
|
||||||
@ -544,7 +506,7 @@ def clip_videos(task_id: str, timestamp_terms: List[str], origin_video: str, pro
|
|||||||
saved_video_path = save_clip_video(timestamp=item, origin_video=origin_video, save_dir=material_directory)
|
saved_video_path = save_clip_video(timestamp=item, origin_video=origin_video, save_dir=material_directory)
|
||||||
if saved_video_path:
|
if saved_video_path:
|
||||||
logger.info(f"video saved: {saved_video_path}")
|
logger.info(f"video saved: {saved_video_path}")
|
||||||
video_paths.update(saved_video_path)
|
video_paths.update({index+1:saved_video_path})
|
||||||
|
|
||||||
# 更新进度
|
# 更新进度
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
|
|||||||
@ -166,6 +166,8 @@ def start_subclip(task_id: str, params: VideoClipParams, subclip_path_videos: di
|
|||||||
params: 视频参数
|
params: 视频参数
|
||||||
subclip_path_videos: 视频片段路径
|
subclip_path_videos: 视频片段路径
|
||||||
"""
|
"""
|
||||||
|
global merged_audio_path, merged_subtitle_path
|
||||||
|
|
||||||
logger.info(f"\n\n## 开始任务: {task_id}")
|
logger.info(f"\n\n## 开始任务: {task_id}")
|
||||||
sm.state.update_task(task_id, state=const.TASK_STATE_PROCESSING, progress=0)
|
sm.state.update_task(task_id, state=const.TASK_STATE_PROCESSING, progress=0)
|
||||||
|
|
||||||
@ -259,7 +261,7 @@ def start_subclip(task_id: str, params: VideoClipParams, subclip_path_videos: di
|
|||||||
"""
|
"""
|
||||||
logger.info("\n\n## 4. 合并音频和字幕")
|
logger.info("\n\n## 4. 合并音频和字幕")
|
||||||
total_duration = sum([script["duration"] for script in new_script_list])
|
total_duration = sum([script["duration"] for script in new_script_list])
|
||||||
if tts_results:
|
if tts_segments:
|
||||||
try:
|
try:
|
||||||
# 合并音频文件
|
# 合并音频文件
|
||||||
merged_audio_path = audio_merger.merge_audio_files(
|
merged_audio_path = audio_merger.merge_audio_files(
|
||||||
@ -273,11 +275,10 @@ def start_subclip(task_id: str, params: VideoClipParams, subclip_path_videos: di
|
|||||||
logger.info(f"字幕文件合并成功->{merged_subtitle_path}")
|
logger.info(f"字幕文件合并成功->{merged_subtitle_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"合并音频文件失败: {str(e)}")
|
logger.error(f"合并音频文件失败: {str(e)}")
|
||||||
|
else:
|
||||||
|
logger.warning("没有需要合并的音频/字幕")
|
||||||
merged_audio_path = ""
|
merged_audio_path = ""
|
||||||
merged_subtitle_path = ""
|
merged_subtitle_path = ""
|
||||||
else:
|
|
||||||
logger.error("TTS转换音频失败, 可能是网络不可用! 如果您在中国, 请使用VPN.")
|
|
||||||
return
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
5. 合并视频
|
5. 合并视频
|
||||||
@ -289,6 +290,7 @@ def start_subclip(task_id: str, params: VideoClipParams, subclip_path_videos: di
|
|||||||
logger.info(f"\n\n## 5. 合并视频: => {combined_video_path}")
|
logger.info(f"\n\n## 5. 合并视频: => {combined_video_path}")
|
||||||
# 如果 new_script_list 中没有 video,则使用 subclip_path_videos 中的视频
|
# 如果 new_script_list 中没有 video,则使用 subclip_path_videos 中的视频
|
||||||
video_clips = [new_script['video'] if new_script.get('video') else subclip_path_videos.get(new_script.get('_id', '')) for new_script in new_script_list]
|
video_clips = [new_script['video'] if new_script.get('video') else subclip_path_videos.get(new_script.get('_id', '')) for new_script in new_script_list]
|
||||||
|
|
||||||
merger_video.combine_clip_videos(
|
merger_video.combine_clip_videos(
|
||||||
output_video_path=combined_video_path,
|
output_video_path=combined_video_path,
|
||||||
video_paths=video_clips,
|
video_paths=video_clips,
|
||||||
@ -381,14 +383,15 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# 提前裁剪是为了方便检查视频
|
# 提前裁剪是为了方便检查视频
|
||||||
subclip_path_videos = {
|
subclip_path_videos = {
|
||||||
1: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/588c37cba4e2e62714c24c4c9054fc51/vid-00-00-00_000-00-00-27_000.mp4',
|
1: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/113343d127b5a09d0bf84b68bd1b3b97/vid_00-00-05-390@00-00-57-980.mp4',
|
||||||
2: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/588c37cba4e2e62714c24c4c9054fc51/vid-00-00-30_000-00-00-57_000.mp4',
|
2: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/113343d127b5a09d0bf84b68bd1b3b97/vid_00-00-28-900@00-00-43-700.mp4',
|
||||||
3: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/588c37cba4e2e62714c24c4c9054fc51/vid-00-01-00_000-00-01-27_000.mp4',
|
3: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/113343d127b5a09d0bf84b68bd1b3b97/vid_00-01-17-840@00-01-27-600.mp4',
|
||||||
4: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/588c37cba4e2e62714c24c4c9054fc51/vid-00-01-30_000-00-01-57_000.mp4',
|
4: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/113343d127b5a09d0bf84b68bd1b3b97/vid_00-02-35-460@00-02-52-380.mp4',
|
||||||
|
5: '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/113343d127b5a09d0bf84b68bd1b3b97/vid_00-06-59-520@00-07-29-500.mp4',
|
||||||
}
|
}
|
||||||
|
|
||||||
params = VideoClipParams(
|
params = VideoClipParams(
|
||||||
video_clip_json_path="/Users/apple/Desktop/home/NarratoAI/resource/scripts/2025-0507-185159.json",
|
video_clip_json_path="/Users/apple/Desktop/home/NarratoAI/resource/scripts/2025-0507-223311.json",
|
||||||
video_origin_path="/Users/apple/Desktop/home/NarratoAI/resource/videos/test.mp4",
|
video_origin_path="/Users/apple/Desktop/home/NarratoAI/resource/videos/merged_video_4938.mp4",
|
||||||
)
|
)
|
||||||
start_subclip(task_id, params, subclip_path_videos)
|
start_subclip(task_id, params, subclip_path_videos)
|
||||||
|
|||||||
5
webui.py
5
webui.py
@ -8,7 +8,7 @@ from webui.components import basic_settings, video_settings, audio_settings, sub
|
|||||||
from webui.utils import cache, file_utils
|
from webui.utils import cache, file_utils
|
||||||
from app.utils import utils
|
from app.utils import utils
|
||||||
from app.models.schema import VideoClipParams, VideoAspect
|
from app.models.schema import VideoClipParams, VideoAspect
|
||||||
from webui.utils.performance import PerformanceMonitor
|
|
||||||
|
|
||||||
# 初始化配置 - 必须是第一个 Streamlit 命令
|
# 初始化配置 - 必须是第一个 Streamlit 命令
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
@ -187,9 +187,6 @@ def render_generate_button():
|
|||||||
file_utils.open_task_folder(config.root_dir, task_id)
|
file_utils.open_task_folder(config.root_dir, task_id)
|
||||||
logger.info(tr("视频生成完成"))
|
logger.info(tr("视频生成完成"))
|
||||||
|
|
||||||
# finally:
|
|
||||||
# PerformanceMonitor.cleanup_resources()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from webui.components import (
|
|||||||
audio_settings,
|
audio_settings,
|
||||||
subtitle_settings
|
subtitle_settings
|
||||||
)
|
)
|
||||||
from webui.utils import cache, file_utils, performance
|
from webui.utils import cache, file_utils
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'config',
|
'config',
|
||||||
@ -17,6 +17,5 @@ __all__ = [
|
|||||||
'audio_settings',
|
'audio_settings',
|
||||||
'subtitle_settings',
|
'subtitle_settings',
|
||||||
'cache',
|
'cache',
|
||||||
'file_utils',
|
'file_utils'
|
||||||
'performance'
|
|
||||||
]
|
]
|
||||||
@ -354,12 +354,11 @@ def crop_video(tr, params):
|
|||||||
utils.cut_video(params, update_progress)
|
utils.cut_video(params, update_progress)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
progress_bar.progress(100)
|
progress_bar.progress(100)
|
||||||
status_text.text("剪完成!")
|
|
||||||
st.success("视频剪辑成功完成!")
|
st.success("视频剪辑成功完成!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
st.error(f"剪辑过程中发生错误: {str(e)}")
|
st.error(f"剪辑过程中发生错误: {str(e)}")
|
||||||
finally:
|
finally:
|
||||||
time.sleep(2)
|
time.sleep(1)
|
||||||
progress_bar.empty()
|
progress_bar.empty()
|
||||||
status_text.empty()
|
status_text.empty()
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
from .performance import monitor_performance, PerformanceMonitor
|
|
||||||
from .cache import *
|
|
||||||
from .file_utils import *
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'monitor_performance',
|
|
||||||
'PerformanceMonitor'
|
|
||||||
]
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
# import psutil
|
|
||||||
import os
|
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
class PerformanceMonitor:
|
|
||||||
@staticmethod
|
|
||||||
def monitor_memory():
|
|
||||||
process = psutil.Process(os.getpid())
|
|
||||||
memory_info = process.memory_info()
|
|
||||||
|
|
||||||
logger.debug(f"Memory usage: {memory_info.rss / 1024 / 1024:.2f} MB")
|
|
||||||
|
|
||||||
# 延迟导入torch并检查CUDA
|
|
||||||
try:
|
|
||||||
import torch
|
|
||||||
if torch.cuda.is_available():
|
|
||||||
gpu_memory = torch.cuda.memory_allocated() / 1024 / 1024
|
|
||||||
logger.debug(f"GPU Memory usage: {gpu_memory:.2f} MB")
|
|
||||||
except (ImportError, RuntimeError) as e:
|
|
||||||
# 无法导入torch或触发CUDA相关错误时,静默处理
|
|
||||||
logger.debug(f"无法获取GPU内存信息: {e}")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def cleanup_resources():
|
|
||||||
# 延迟导入torch并清理CUDA
|
|
||||||
try:
|
|
||||||
import torch
|
|
||||||
if torch.cuda.is_available():
|
|
||||||
torch.cuda.empty_cache()
|
|
||||||
logger.debug("CUDA缓存已清理")
|
|
||||||
except (ImportError, RuntimeError) as e:
|
|
||||||
# 无法导入torch或触发CUDA相关错误时,静默处理
|
|
||||||
logger.debug(f"无法清理CUDA资源: {e}")
|
|
||||||
|
|
||||||
import gc
|
|
||||||
gc.collect()
|
|
||||||
|
|
||||||
# 仅报告进程内存,不尝试获取GPU内存
|
|
||||||
process = psutil.Process(os.getpid())
|
|
||||||
memory_info = process.memory_info()
|
|
||||||
logger.debug(f"Memory usage after cleanup: {memory_info.rss / 1024 / 1024:.2f} MB")
|
|
||||||
|
|
||||||
def monitor_performance(func):
|
|
||||||
"""性能监控装饰器"""
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
try:
|
|
||||||
PerformanceMonitor.monitor_memory()
|
|
||||||
result = func(*args, **kwargs)
|
|
||||||
return result
|
|
||||||
finally:
|
|
||||||
PerformanceMonitor.cleanup_resources()
|
|
||||||
return wrapper
|
|
||||||
Loading…
x
Reference in New Issue
Block a user