mirror of
https://github.com/linyqh/NarratoAI.git
synced 2025-12-17 15:42:50 +00:00
refactor(script): 优化脚本生成中的时间戳处理(毫秒级)
- 重构 calculate_duration_and_word_count 函数,提高时间戳解析精度到毫秒级 - 更新 get_batch_timestamps 函数,支持毫秒级时间戳格式- 优化字数计算公式,调整为每0.4秒一个字,并限制在10-500字范围内 -增加日志输出,提高代码可调试性
This commit is contained in:
parent
f6ba1824e9
commit
974a219dd3
@ -374,22 +374,65 @@ class ScriptProcessor:
|
|||||||
记住:要敢于用"温和的违反"制造笑点,但要把握好尺度,让观众在轻松愉快中感受到乐趣。"""
|
记住:要敢于用"温和的违反"制造笑点,但要把握好尺度,让观众在轻松愉快中感受到乐趣。"""
|
||||||
|
|
||||||
def calculate_duration_and_word_count(self, time_range: str) -> int:
|
def calculate_duration_and_word_count(self, time_range: str) -> int:
|
||||||
|
"""
|
||||||
|
计算时间范围的持续时长并估算合适的字数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
time_range: 时间范围字符串,格式为 "HH:MM:SS,mmm-HH:MM:SS,mmm"
|
||||||
|
例如: "00:00:50,100-00:01:21,500"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 估算的合适字数
|
||||||
|
基于经验公式: 每0.35秒可以说一个字
|
||||||
|
例如: 10秒可以说约28个字 (10/0.35≈28.57)
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
start_str, end_str = time_range.split('-')
|
start_str, end_str = time_range.split('-')
|
||||||
|
|
||||||
def time_to_seconds(time_str):
|
def time_to_seconds(time_str: str) -> float:
|
||||||
minutes, seconds = map(int, time_str.split(':'))
|
"""
|
||||||
return minutes * 60 + seconds
|
将时间字符串转换为秒数(带毫秒精度)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
time_str: 时间字符串,格式为 "HH:MM:SS,mmm"
|
||||||
|
例如: "00:00:50,100" 表示50.1秒
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 转换后的秒数(带毫秒)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 处理毫秒部分
|
||||||
|
time_part, ms_part = time_str.split(',')
|
||||||
|
hours, minutes, seconds = map(int, time_part.split(':'))
|
||||||
|
milliseconds = int(ms_part)
|
||||||
|
|
||||||
|
# 转换为秒
|
||||||
|
total_seconds = (hours * 3600) + (minutes * 60) + seconds + (milliseconds / 1000)
|
||||||
|
return total_seconds
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"时间格式解析错误: {time_str}, error: {e}")
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# 计算开始和结束时间的秒数
|
||||||
start_seconds = time_to_seconds(start_str)
|
start_seconds = time_to_seconds(start_str)
|
||||||
end_seconds = time_to_seconds(end_str)
|
end_seconds = time_to_seconds(end_str)
|
||||||
|
|
||||||
|
# 计算持续时间(秒)
|
||||||
duration = end_seconds - start_seconds
|
duration = end_seconds - start_seconds
|
||||||
word_count = int(duration / 0.35)
|
|
||||||
|
# 根据经验公式计算字数: 每0.5秒一个字
|
||||||
|
word_count = int(duration / 0.4)
|
||||||
|
|
||||||
|
# 确保字数在合理范围内
|
||||||
|
word_count = max(10, min(word_count, 500)) # 限制在10-500字之间
|
||||||
|
|
||||||
|
logger.debug(f"时间范围 {time_range} 的持续时间为 {duration:.3f}秒, 估算字数: {word_count}")
|
||||||
return word_count
|
return word_count
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(f"时间格式转换错误: {traceback.format_exc()}")
|
logger.warning(f"字数计算错误: {traceback.format_exc()}")
|
||||||
return 100
|
return 100 # 发生错误时返回默认字数
|
||||||
|
|
||||||
def process_frames(self, frame_content_list: List[Dict]) -> List[Dict]:
|
def process_frames(self, frame_content_list: List[Dict]) -> List[Dict]:
|
||||||
for frame_content in frame_content_list:
|
for frame_content in frame_content_list:
|
||||||
|
|||||||
@ -20,52 +20,95 @@ from webui.utils import file_utils
|
|||||||
|
|
||||||
def get_batch_timestamps(batch_files, prev_batch_files=None):
|
def get_batch_timestamps(batch_files, prev_batch_files=None):
|
||||||
"""
|
"""
|
||||||
获取一批文件的时间戳范围
|
解析一批文件的时间戳范围,支持毫秒级精度
|
||||||
返回: (first_timestamp, last_timestamp, timestamp_range)
|
|
||||||
|
|
||||||
文件名格式: keyframe_001253_000050.jpg
|
|
||||||
其中 000050 表示 00:00:50 (50秒)
|
|
||||||
000101 表示 00:01:01 (1分1秒)
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
batch_files: 当前批次的文件列表
|
batch_files: 当前批次的文件列表
|
||||||
prev_batch_files: 上一个批次的文件列表,用于处理单张图片的情况
|
prev_batch_files: 上一个批次的文件列表,用于处理单张图片的情况
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (first_timestamp, last_timestamp, timestamp_range)
|
||||||
|
时间戳格式: HH:MM:SS,mmm (时:分:秒,毫秒)
|
||||||
|
例如: 00:00:50,100 表示50秒100毫秒
|
||||||
|
|
||||||
|
示例文件名格式:
|
||||||
|
keyframe_001253_000050100.jpg
|
||||||
|
其中 000050100 表示 00:00:50,100 (50秒100毫秒)
|
||||||
"""
|
"""
|
||||||
if not batch_files:
|
if not batch_files:
|
||||||
logger.warning("Empty batch files")
|
logger.warning("Empty batch files")
|
||||||
return "00:00", "00:00", "00:00-00:00"
|
return "00:00:00,000", "00:00:00,000", "00:00:00,000-00:00:00,000"
|
||||||
|
|
||||||
|
def get_frame_files():
|
||||||
|
"""获取首帧和尾帧文件名"""
|
||||||
|
if len(batch_files) == 1 and prev_batch_files and prev_batch_files:
|
||||||
|
# 单张图片情况:使用上一批次最后一帧作为首帧
|
||||||
|
first = os.path.basename(prev_batch_files[-1])
|
||||||
|
last = os.path.basename(batch_files[0])
|
||||||
|
logger.debug(f"单张图片批次,使用上一批次最后一帧作为首帧: {first}")
|
||||||
|
else:
|
||||||
|
first = os.path.basename(batch_files[0])
|
||||||
|
last = os.path.basename(batch_files[-1])
|
||||||
|
return first, last
|
||||||
|
|
||||||
|
def extract_time(filename):
|
||||||
|
"""从文件名提取时间信息"""
|
||||||
|
try:
|
||||||
|
# 提取类似 000050100 的时间戳部分
|
||||||
|
time_str = filename.split('_')[2].replace('.jpg', '')
|
||||||
|
if len(time_str) < 9: # 处理旧格式
|
||||||
|
time_str = time_str.ljust(9, '0')
|
||||||
|
return time_str
|
||||||
|
except (IndexError, AttributeError) as e:
|
||||||
|
logger.warning(f"Invalid filename format: {filename}, error: {e}")
|
||||||
|
return "000000000"
|
||||||
|
|
||||||
|
def format_timestamp(time_str):
|
||||||
|
"""
|
||||||
|
将时间字符串转换为 HH:MM:SS,mmm 格式
|
||||||
|
|
||||||
# 如果当前批次只有一张图片,且有上一个批次的文件,则使用上一批次的最后一张作为首帧
|
Args:
|
||||||
if len(batch_files) == 1 and prev_batch_files and len(prev_batch_files) > 0:
|
time_str: 9位数字字符串,格式为 MMSSSMMM
|
||||||
first_frame = os.path.basename(prev_batch_files[-1])
|
例如: 000050100 表示 00分50秒100毫秒
|
||||||
last_frame = os.path.basename(batch_files[0])
|
|
||||||
logger.debug(f"单张图片批次,使用上一批次最后一帧作为首帧: {first_frame}")
|
Returns:
|
||||||
else:
|
str: HH:MM:SS,mmm 格式的时间戳
|
||||||
# 提取首帧和尾帧的时间戳
|
"""
|
||||||
first_frame = os.path.basename(batch_files[0])
|
try:
|
||||||
last_frame = os.path.basename(batch_files[-1])
|
if len(time_str) < 9:
|
||||||
|
logger.warning(f"Invalid timestamp format: {time_str}")
|
||||||
|
return "00:00:00,000"
|
||||||
|
|
||||||
|
# 提取分钟、秒和毫秒
|
||||||
|
minutes = int(time_str[-9:-6]) # 取后9位的前3位作为分钟
|
||||||
|
seconds = int(time_str[-6:-3]) # 取中间3位作为秒数
|
||||||
|
milliseconds = int(time_str[-3:]) # 取最后3位作为毫秒
|
||||||
|
|
||||||
|
# 处理进位
|
||||||
|
if seconds >= 60:
|
||||||
|
minutes += seconds // 60
|
||||||
|
seconds = seconds % 60
|
||||||
|
|
||||||
|
if minutes >= 60:
|
||||||
|
hours = minutes // 60
|
||||||
|
minutes = minutes % 60
|
||||||
|
else:
|
||||||
|
hours = 0
|
||||||
|
|
||||||
|
return f"{hours:02d}:{minutes:02d}:{seconds:02d},{milliseconds:03d}"
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
logger.warning(f"时间戳格式转换失败: {time_str}, error: {e}")
|
||||||
|
return "00:00:00,000"
|
||||||
|
|
||||||
|
# 获取首帧和尾帧文件名
|
||||||
|
first_frame, last_frame = get_frame_files()
|
||||||
|
|
||||||
# 从文件名中提取时间信息
|
# 从文件名中提取时间信息
|
||||||
first_time = first_frame.split('_')[2].replace('.jpg', '') # 000050
|
first_time = extract_time(first_frame)
|
||||||
last_time = last_frame.split('_')[2].replace('.jpg', '') # 000101
|
last_time = extract_time(last_frame)
|
||||||
|
|
||||||
# 转换为分:秒格式
|
|
||||||
def format_timestamp(time_str):
|
|
||||||
# 时间格式为 MMSS,如 0050 表示 00:50, 0101 表示 01:01
|
|
||||||
if len(time_str) < 4:
|
|
||||||
logger.warning(f"Invalid timestamp format: {time_str}")
|
|
||||||
return "00:00"
|
|
||||||
|
|
||||||
minutes = int(time_str[-4:-2]) # 取后4位的前2位作为分钟
|
|
||||||
seconds = int(time_str[-2:]) # 取后2位作为秒数
|
|
||||||
|
|
||||||
# 处理进位
|
|
||||||
if seconds >= 60:
|
|
||||||
minutes += seconds // 60
|
|
||||||
seconds = seconds % 60
|
|
||||||
|
|
||||||
return f"{minutes:02d}:{seconds:02d}"
|
|
||||||
|
|
||||||
|
# 转换为标准时间戳格式
|
||||||
first_timestamp = format_timestamp(first_time)
|
first_timestamp = format_timestamp(first_time)
|
||||||
last_timestamp = format_timestamp(last_time)
|
last_timestamp = format_timestamp(last_time)
|
||||||
timestamp_range = f"{first_timestamp}-{last_timestamp}"
|
timestamp_range = f"{first_timestamp}-{last_timestamp}"
|
||||||
@ -542,7 +585,7 @@ def generate_script(tr, params):
|
|||||||
# 处理帧内容生成脚本
|
# 处理帧内容生成脚本
|
||||||
script_result = processor.process_frames(frame_content_list)
|
script_result = processor.process_frames(frame_content_list)
|
||||||
|
|
||||||
# <EFBFBD><EFBFBD>结果转换为JSON字符串
|
# 结果转换为JSON字符串
|
||||||
script = json.dumps(script_result, ensure_ascii=False, indent=2)
|
script = json.dumps(script_result, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -567,7 +610,7 @@ def generate_script(tr, params):
|
|||||||
if not api_key:
|
if not api_key:
|
||||||
raise ValueError("未配置 Narrato API Key,请在基础设置中配置")
|
raise ValueError("未配置 Narrato API Key,请在基础设置中配置")
|
||||||
|
|
||||||
# 准<EFBFBD><EFBFBD><EFBFBD>API请求
|
# 准备API请求
|
||||||
headers = {
|
headers = {
|
||||||
'X-API-Key': api_key,
|
'X-API-Key': api_key,
|
||||||
'accept': 'application/json'
|
'accept': 'application/json'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user