NarratoAI/app/services/update_script.py
linyq 8b5ff0658b feat(task): 优化脚本时间戳更新逻辑并添加音频路径- 修改 update_script_timestamps 函数,支持同时更新视频和音频路径
- 在任务处理流程中生成 tts_clip_result 字典,用于更新脚本中的音频路径
-优化脚本列表的更新逻辑,确保所有时间戳都计算持续时间
2025-05-07 00:59:07 +08:00

152 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project: NarratoAI
@File : update_script
@Author : 小林同学
@Date : 2025/5/6 下午11:00
'''
import re
import os
from typing import Dict, List, Any, Tuple
def extract_timestamp_from_video_path(video_path: str) -> str:
"""
从视频文件路径中提取时间戳
Args:
video_path: 视频文件路径
Returns:
提取出的时间戳,格式为 'HH:MM:SS-HH:MM:SS'
"""
# 使用正则表达式从文件名中提取时间戳
filename = os.path.basename(video_path)
match = re.search(r'vid-(\d{2}-\d{2}-\d{2})-(\d{2}-\d{2}-\d{2})\.mp4', filename)
if match:
# 提取并格式化时间戳
start_time = match.group(1).replace('-', ':')
end_time = match.group(2).replace('-', ':')
return f"{start_time}-{end_time}"
return ""
def calculate_duration(timestamp: str) -> float:
"""
计算时间戳范围的持续时间(秒)
Args:
timestamp: 格式为 'HH:MM:SS-HH:MM:SS' 的时间戳
Returns:
持续时间(秒)
"""
try:
start_time, end_time = timestamp.split('-')
# 解析时间
start_h, start_m, start_s = map(int, start_time.split(':'))
end_h, end_m, end_s = map(int, end_time.split(':'))
# 转换为秒
start_seconds = start_h * 3600 + start_m * 60 + start_s
end_seconds = end_h * 3600 + end_m * 60 + end_s
# 计算时间差(秒)
return round(end_seconds - start_seconds, 2)
except (ValueError, AttributeError):
return 0.0
def update_script_timestamps(script_list: List[Dict[str, Any]], video_result: Dict[str, str], audio_result: Dict[str, str] = None) -> List[Dict[str, Any]]:
"""
根据 video_result 中的视频文件更新 script_list 中的时间戳,添加持续时间,并根据 audio_result 添加音频路径
Args:
script_list: 原始脚本列表
video_result: 视频结果字典,键为原时间戳,值为视频文件路径
audio_result: 音频结果字典,键为原时间戳,值为音频文件路径
Returns:
更新后的脚本列表
"""
# 创建副本,避免修改原始数据
updated_script = []
# 建立原始时间戳到视频路径和新时间戳的映射
timestamp_mapping = {}
for orig_timestamp, video_path in video_result.items():
new_timestamp = extract_timestamp_from_video_path(video_path)
if new_timestamp:
timestamp_mapping[orig_timestamp] = {
'new_timestamp': new_timestamp,
'video_path': video_path
}
# 更新脚本中的时间戳
for item in script_list:
item_copy = item.copy()
orig_timestamp = item_copy.get('timestamp', '')
# 初始化音频路径为空字符串
item_copy['audio'] = ""
# 如果提供了音频结果字典且时间戳存在于音频结果中,直接使用对应的音频路径
if audio_result and orig_timestamp in audio_result:
item_copy['audio'] = audio_result[orig_timestamp]
if orig_timestamp in timestamp_mapping:
# 更新时间戳
item_copy['timestamp'] = timestamp_mapping[orig_timestamp]['new_timestamp']
# 计算持续时间
item_copy['duration'] = calculate_duration(item_copy['timestamp'])
elif orig_timestamp:
# 对于未更新的时间戳,也计算并添加持续时间
item_copy['duration'] = calculate_duration(orig_timestamp)
updated_script.append(item_copy)
return updated_script
if __name__ == '__main__':
list_script = [
{'picture': '【解说】好的,各位,欢迎回到我的频道!《庆余年 2》刚开播就给了我们一个王炸范闲在北齐""了?这怎么可能!',
'timestamp': '00:00:00-00:01:15',
'narration': '好的各位,欢迎回到我的频道!《庆余年 2》刚开播就给了我们一个王炸范闲在北齐""了?这怎么可能!上集片尾那个巨大的悬念,这一集就立刻揭晓了!范闲假死归来,他面临的第一个,也是最大的难关,就是如何面对他最敬爱的,同时也是最可怕的那个人——庆帝!',
'OST': 0},
{'picture': '【解说】上一集我们看到,范闲在北齐遭遇了惊天变故,生死不明!', 'timestamp': '00:01:15-00:04:40',
'narration': '但我们都知道,他绝不可能就这么轻易退场!第二集一开场,范闲就已经秘密回到了京都。他的生死传闻,可不像我们想象中那样只是小范围流传,而是…',
'OST': 0}, {'picture': '画面切到王启年小心翼翼地向范闲汇报。', 'timestamp': '00:04:41-00:04:58',
'narration': '我发现大人的死讯不光是在民间,在官场上也它传开了,所以呢,所以啊,可不是什么好事,将来您跟陛下怎么交代,这可是欺君之罪',
'OST': 1},
{'picture': '【解说】"欺君之罪"!在封建王朝,这可是抄家灭族的大罪!搁一般人,肯定脚底抹油溜之大吉了。',
'timestamp': '00:04:58-00:05:45',
'narration': '"欺君之罪"!在封建王朝,这可是抄家灭族的大罪!搁一般人,肯定脚底抹油溜之大吉了。但范闲是谁啊?他偏要反其道而行之!他竟然决定,直接去见庆帝!冒着天大的风险,用"假死"这个事实去赌庆帝的态度!',
'OST': 0}, {'picture': '【解说】但想见庆帝,哪有那么容易?范闲艺高人胆大,竟然选择了最激进的方式——闯宫!',
'timestamp': '00:05:45-00:06:00',
'narration': '但想见庆帝,哪有那么容易?范闲艺高人胆大,竟然选择了最激进的方式——闯宫!', 'OST': 0},
{'picture': '画面切换到范闲蒙面闯入皇宫,被侍卫包围的场景。', 'timestamp': '00:06:00-00:06:03',
'narration': '抓刺客', 'OST': 1}]
video_res = {
'00:00:00-00:01:15': '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-00-00-00-00-26.mp4',
'00:01:15-00:04:40': '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-01-15-00-01-29.mp4',
'00:04:58-00:05:45': '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-04-58-00-05-20.mp4',
'00:05:45-00:06:00': '/Users/apple/Desktop/home/NarratoAI/storage/temp/clip_video/0ac14d474144b54d614c26a5c87cffe7/vid-00-05-45-00-05-53.mp4'}
audio_res = {
'00:00:00-00:01:15': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_00_00-00_01_15.mp3',
'00:01:15-00:04:40': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_01_15-00_04_40.mp3',
'00:04:58-00:05:45': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_04_58-00_05_45.mp3',
'00:05:45-00:06:00': '/Users/apple/Desktop/home/NarratoAI/storage/tasks/qyn2-2-demo/audio_00_05_45-00_06_00.mp3'}
# 更新并打印结果
updated_list_script = update_script_timestamps(list_script, video_res, audio_res)
for item in updated_list_script:
print(
f"Picture: {item['picture'][:20]}... | Timestamp: {item['timestamp']} | Duration: {item['duration']} 秒 | Audio: {item['audio']}")