优化 webui.py;

脚本剪辑不稳定
This commit is contained in:
linyqh 2024-09-25 01:43:55 +08:00
parent 6669b28361
commit d6663fde21
3 changed files with 129 additions and 261 deletions

View File

@ -1,7 +1,8 @@
import logging
import os
import re
import json
import traceback
import streamlit as st
from typing import List
from loguru import logger
from openai import OpenAI
@ -11,6 +12,7 @@ import google.generativeai as gemini
from googleapiclient.errors import ResumableUploadError
from google.api_core.exceptions import FailedPrecondition
from google.generativeai.types import HarmCategory, HarmBlockThreshold
import subprocess
from app.config import config
@ -29,29 +31,29 @@ Method = """
盘点全球最恐怖的10部电影
盘点全球最科幻的10部电影
盘点全球最悲惨的10部电影
全球最值得看的10部灾难电影
全球最值得看的10部灾难电影
盘点全球最值得看的10部励志电影
下面的示例就是最简单的解说文案开头
1.这是XXX国20年来最大尺度的一部剧极度烧脑却让99%的人看得心潮澎湃无法自拔故事开始
2.这是有史以来电影院唯一一部全程开灯放完的电影期间无数人尖叫昏厥他被成为勇敢者的专属因为99%的人都不敢看到结局许多人看完它从此不愿再碰手机他就是大名鼎鼎的暗黑神作XXX
3.这到底是一部什么样的电影能被55个国家公开抵制它甚至为了上映不惜删减掉整整47分钟的剧情
4.是什么样的一个人被豆瓣网友称之为史上最牛P的老太太都70岁了还要去贩毒
4.是什么样的一个人被豆瓣网友称之为史上最牛P的老太太都70岁了还要去贩毒
5.他是M国历史上最NB//猖狂/冤枉的囚犯/抢劫犯/
6.这到底是一部什么样的影片他一个人就拿了4个顶级奖项第一季8.7第二季直接干到9.511万人给出5星好评一共也就6集却斩获26项国际大奖看过的人都说他是近年来最好的xxx剧几乎成为了近年来xxx剧的标杆故事发生在
7.他是国产电影的巅峰佳作更是许多80-90后的青春启蒙曾入选时代周刊获得年度佳片第一可在国内却被尘封多年至今为止都无法在各大视频网站看到完整资源他就是xxxxxx
8.这是一部让所有人看得荷尔蒙飙升的爽片
9.他被成为世界上最虐心绝望的电影至今无人敢看第二遍很难想象他是根据真实事件改编而来
10.这大概是有史以来最令人不寒而栗的电影当年一经放映就点燃了无数人的怒火不少观众不等影片放完就愤然离场它比xxx更让人绝望比比xxx更让人xxx能坚持看完全片的人更是万中无一包括我甚至观影结束后有无数人抵制投诉这部电影认为影片的导演玩弄了他们的情感是顶级神作xxxx
10.这大概是有史以来最令人不寒而栗的电影当年一经放映就点燃了无数人的怒火不少观众不等影片放完就愤然离场它比xxx更让人绝望比比xxx更让人xxx能坚持看完全片的人更是万中无一包括我甚至观影结束后有无数人抵制投诉这部电影认为影片的导演玩弄了他们的情感是顶级神作xxxx
11.这是X国有史以来最高赞的一部悬疑电影然而却因为某些原因国内90%的人没能看过这部片子他就是xxx
12.有这样一部电影这辈子你绝对不想再看第二遍并不是它剧情烂俗而是它的结局你根本承受不起/想象不到甚至有80%的观众在观影途中情绪崩溃中途离场更让许多同行都不想解说这部电影他就是大名鼎鼎的暗黑神作xxx
13.它被誉为史上最牛悬疑片无数人在看完它时候一个月不敢照镜子这样一部仅适合部分年龄段观看的影片究竟有什么样的魅力竟然获得某瓣8.2的高分很多人说这部电影到处都是看点他就是xxx.
13.它被誉为史上最牛悬疑片无数人在看完它时候一个月不敢照镜子这样一部仅适合部分年龄段观看的影片究竟有什么样的魅力竟然获得某瓣8.2的高分很多人说这部电影到处都是看点他就是xxx.
14.这是一部在某瓣上被70万人打出9.3分的高分的电影到底是一部什么样的电影能够在某瓣上被70万人打出9.3分的高分
15.这是一部细思极恐的科幻大片整部电影颠覆你的三观它的名字叫
16.史上最震撼的灾难片每一点都不舍得快进的电影他叫
17.今天给大家带来一部基于真实事件改编的主题介绍一句的故事片这是一部连环悬疑剧如果不看到最后绝对想不到结局竟然是这样的反转
### 方式:情景式、假设性开头
### 方式:情景式、假设性开头
1.他叫你以为他是的吗他是来然后开始叙述
2.你知道原来然后开始叙述
3.如果给你.你会怎么样
@ -71,7 +73,7 @@ Method = """
例如
1.这不是电影这是真实故事两个女人和一个男人被关在可桑拿室喊破喉咙也没有一丝回音窒息感和热度让人抓狂故事就是从这里开始
2.如果你男朋友出轨了他不爱你了你家暴怎么办接下来这部电影就会教你如何让老公服服帖帖的呆在你身边女主是一个开始叙述了
2.如果你男朋友出轨了他不爱你了你家暴怎么办接下来这部电影就会教你如何让老公服服帖帖的呆在你身边女主是一个开始叙述了
3.他力大无穷双眼放光这不是拯救地球的超人吗然而不是今天给大家推荐的这部电影叫
以上是需要看完影片看懂影片然后从里面提炼出精彩的几句话,当然是比较难的当你不会自己去总结前三句的经典的话可以用前面方式一二三
@ -98,8 +100,7 @@ Method = """
比如也可以总结下这部短片如何的好推荐/值得大家去观看之类的话语
其实就是给我们的作品来一个总结总结我们所做的三个视频有开始就要有结束这个结束不一定是固定的模版但是视频一定要有结尾让人感觉有头有尾才最舒服
做解说是一个比较浪费脑细胞的活虽然刚开始比较难一点但是当你正常做三部剧之后所有自己的思路都会被打开以后的基本就可以独立完成来操作来
做解说第一次可能会做两天第二次可能就需要一天了慢慢的时间缩短到8个小时之内是我们平常的制作全部时间
做解说第一次可能会做两天第二次可能就需要一天了慢慢的时间缩短到8个小时之内是我们平的制作全部时间
"""
@ -344,76 +345,73 @@ def _generate_response(prompt: str) -> str:
return content.replace("\n", "")
def compress_video(input_path: str, output_path: str):
"""
压缩视频文件
Args:
input_path: 输入视频文件路径
output_path: 输出压缩后的视频文件路径
"""
ffmpeg_path = "E:\\projects\\NarratoAI_v0.1.2\\lib\\ffmpeg\\ffmpeg-7.0-essentials_build\\ffmpeg.exe" # 指定 ffmpeg 的完整路径
# 如果压缩后的视频文件已经存在,则直接使用
if os.path.exists(output_path):
logger.info(f"压缩视频文件已存在: {output_path}")
return
try:
command = [
ffmpeg_path,
"-i", input_path,
"-c:v", "h264",
"-b:v", "500k",
"-c:a", "aac",
"-b:a", "128k",
output_path
]
subprocess.run(command, check=True)
except subprocess.CalledProcessError as e:
logger.error(f"视频压缩失败: {e}")
raise
def generate_script(
video_subject: str, language: str = "", paragraph_number: int = 1
video_path: str, video_plot: str, video_name: str, language: str = "zh-CN", progress_text: st.empty = st.empty()
) -> str:
prompt = f"""
# Role: Video Script Generator
"""
生成视频剪辑脚本
Args:
video_path: 视频文件路径
video_plot: 视频剧情内容
video_name: 视频名称
language: 语言
## Goals:
Generate a script for a video, depending on the subject of the video.
Returns:
str: 生成的脚本
"""
# 1. 压缩视频
progress_text.text("压缩视频中...")
compressed_video_path = f"{os.path.splitext(video_path)[0]}_compressed.mp4"
compress_video(video_path, compressed_video_path)
## Constrains:
1. the script is to be returned as a string with the specified number of paragraphs.
2. do not under any circumstance reference this prompt in your response.
3. get straight to the point, don't start with unnecessary things like, "welcome to this video".
4. you must not include any type of markdown or formatting in the script, never use a title.
5. only return the raw content of the script.
6. do not include "voiceover", "narrator" or similar indicators of what should be spoken at the beginning of each paragraph or line.
7. you must not mention the prompt, or anything about the script itself. also, never talk about the amount of paragraphs or lines. just write the script.
8. respond in the same language as the video subject.
# 2. 转录视频
transcription = gemini_video_transcription(video_name=video_name, video_path=compressed_video_path, language=language, progress_text=progress_text)
# Initialization:
- video subject: {video_subject}
- number of paragraphs: {paragraph_number}
""".strip()
if language:
prompt += f"\n- language: {language}"
# # 清理压缩后的视频文件
# try:
# os.remove(compressed_video_path)
# except OSError as e:
# logger.warning(f"删除压缩视频文件失败: {e}")
final_script = ""
logger.info(f"subject: {video_subject}")
# 3. 编写解说文案
progress_text.text("解说文案中...")
script = writing_short_play(video_plot, video_name)
def format_response(response):
# Clean the script
# Remove asterisks, hashes
response = response.replace("*", "")
response = response.replace("#", "")
# 4. 文案匹配画面
progress_text.text("画面匹配中...")
matched_script = screen_matching(huamian=transcription, wenan=script)
# Remove markdown syntax
response = re.sub(r"\[.*\]", "", response)
response = re.sub(r"\(.*\)", "", response)
# Split the script into paragraphs
paragraphs = response.split("\n\n")
# Select the specified number of paragraphs
selected_paragraphs = paragraphs[:paragraph_number]
# Join the selected paragraphs into a single string
return "\n\n".join(paragraphs)
for i in range(_max_retries):
try:
response = _generate_response(prompt=prompt)
if response:
final_script = format_response(response)
else:
logging.error("gpt returned an empty response")
# g4f may return an error message
if final_script and "当日额度已消耗完" in final_script:
raise ValueError(final_script)
if final_script:
break
except Exception as e:
logger.error(f"failed to generate script: {e}")
if i < _max_retries:
logger.warning(f"failed to generate video script, trying again... {i + 1}")
logger.success(f"completed: \n{final_script}")
return final_script.strip()
return matched_script
def generate_terms(video_subject: str, video_script: str, amount: int = 5) -> List[str]:
@ -510,7 +508,7 @@ def gemini_video2json(video_origin_name: str, video_origin_path: str, video_plot
**输入示例**
```text
在一个暗的小巷中主角缓慢走进四周静谧无声只有远处隐隐传来猫的叫声突然背后出现一个神秘的身影
在一个<EFBFBD><EFBFBD><EFBFBD>暗的小巷中主角缓慢走进四周静谧无声只有远处隐隐传来猫的叫声突然背后出现一个神秘的身影
```
**输出格式**
@ -566,7 +564,7 @@ def gemini_video2json(video_origin_name: str, video_origin_path: str, video_plot
return response
def gemini_video_transcription(video_origin_name: str, video_origin_path: str, language: str):
def gemini_video_transcription(video_name: str, video_path: str, language: str, progress_text: st.empty = ""):
'''
使用 gemini-1.5-xxx 进行视频画面转录
'''
@ -577,24 +575,25 @@ def gemini_video_transcription(video_origin_name: str, video_origin_path: str, l
model = gemini.GenerativeModel(model_name=model_name)
prompt = """
Please transcribe the audio, include timestamps, and provide visual descriptions, then output in JSON formatuse %s ONLY.
Please transcribe the audio, include timestamps, and provide visual descriptions, then output in JSON format.
Please use %s output
Use this JSON schema:
Graphics = {"timestamp": "MM:SS-MM:SS", "picture": "str", "quotes": "str"(If no one says anything, use an empty string instead.)}
Return: list[Graphics]
""" % language
logger.debug(f"视频名称: {video_origin_name}")
logger.debug(f"视频名称: {video_name}")
try:
gemini_video_file = gemini.upload_file(video_origin_path)
# gemini_video_file = gemini.get_file("files/uxo6r9n80s84")
progress_text.text("上传视频中...")
gemini_video_file = gemini.upload_file(video_path)
logger.debug(f"上传视频至 Google cloud 成功: {gemini_video_file.name}")
while gemini_video_file.state.name == "PROCESSING":
import time
time.sleep(1)
gemini_video_file = gemini.get_file(gemini_video_file.name)
logger.debug(f"视频当前状态(ACTIVE才可用): {gemini_video_file.state.name}")
progress_text.text(f"解析视频中, 当前状态: {gemini_video_file.state.name}")
# logger.debug(f"视频当前状态(ACTIVE才可用): {gemini_video_file.state.name}")
if gemini_video_file.state.name == "FAILED":
raise ValueError(gemini_video_file.state.name)
except ResumableUploadError as err:
@ -604,6 +603,7 @@ def gemini_video_transcription(video_origin_name: str, video_origin_path: str, l
logger.error(f"400 用户位置不支持 Google API 使用。\n{traceback.format_exc()}")
return ""
progress_text.text("视频转录中...")
response = model.generate_content(
[prompt, gemini_video_file],
safety_settings={
@ -613,7 +613,7 @@ def gemini_video_transcription(video_origin_name: str, video_origin_path: str, l
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
}
)
logger.success(f"llm 视频转录: \n{response.text}")
logger.success("视频转录成功")
return response.text
@ -652,8 +652,9 @@ def writing_movie(video_plot, video_name):
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
}
)
print(response.text)
print("字数:", len(response.text))
logger.debug(response.text)
logger.debug("字数:", len(response.text))
return response.text
def writing_short_play(video_plot: str, video_name: str):
@ -697,8 +698,8 @@ def writing_short_play(video_plot: str, video_name: str):
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
}
)
print(response.text)
print("字数:", len(response.text))
logger.success("解说文案生成成功")
return response.text
def screen_matching(huamian: str, wenan: str):
@ -733,9 +734,6 @@ def screen_matching(huamian: str, wenan: str):
script = {'picture': str, 'timestamp': str, "narration": str, "OST": bool}
Return: list[script]
""" % (huamian, wenan)
logger.info(prompt)
response = model.generate_content(
prompt,
generation_config=gemini.types.GenerationConfig(
@ -749,9 +747,8 @@ def screen_matching(huamian: str, wenan: str):
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
}
)
print(response.text)
print("字数:", len(response.text))
logger.success("匹配成功")
return response.text
if __name__ == "__main__":
@ -762,159 +759,12 @@ if __name__ == "__main__":
# gemini_video_transcription(video_subject, video_path, language)
# 2. 解说文案
# video_plot = """
# 李自忠拿着儿子李牧名下的存折,去银行取钱给儿子救命,却被要求证明“你儿子是你儿子”。
# 走投无路时碰到银行被抢劫劫匪给了他两沓钱救命李自忠却因此被银行以抢劫罪起诉并顶格判处20年有期徒刑。
# 苏醒后的李牧坚决为父亲做无罪辩护,面对银行的顶级律师团队,他一个法学院大一学生,能否力挽狂澜,创作奇迹?挥法律之利剑 ,持正义之天平!
# """
# print(video_plot)
# res = writing_short_play(video_plot, "第二十条之无罪释放")
wenan = """
这到底是一部什么样的电影能让银行经理在法庭上公然下跪能让无数网友为之愤怒更能让无数人为之动容\n
他叫李自忠为了给儿子筹集医药费他来到了银行想取出儿子名下的存款却被银行告知要证明你儿子是你儿子走投无路之下他却被卷入了一场银行抢劫案阴差阳错之下劫匪给了他两沓钱让他救儿子本以为是希望没想到却是绝望的开始他因此被认定为抢劫犯被判处20年有期徒刑\n
然而天无绝人之路昏迷的儿子醒了苏醒后的儿子怎么也不敢相信自己的父亲竟然被判为抢劫犯为了给父亲讨回公道他做出了一个决定他要为父亲做无罪辩护要知道他只是一个法学院的大一学生面对银行的顶级律师团队他能成功吗\n
面对种种不利证据他一次次败诉又一次次上诉就像一只打不死的小强为了找到有利的证据他四处奔波走访调查甚至不惜以身犯险只为还原事实真相然而真相真的会到来吗\n
正义或许会迟到但永远不会缺席随着案件的审理越来越多的疑点浮出水面案情也发生了惊天大逆转他究竟发现了什么最后的真相又是什么本案改编自真实事件究竟是人性的扭曲还是道德的沦丧\n
想知道案件的最终结果吗让我们一起走进这部电影寻找最终的真相吧
"""
# 读取指定目录下的 json 文件
with open("../../resource/scripts/zhuanlu.json", "r", encoding="utf-8") as f:
huamian = json.load(f)
screen_matching(huamian, wenan)
# import os
# import sys
# import requests
# from app.utils.utils import get_current_country
#
# # # 添加当前目录到系统路径
# # sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# # proxy_url_http = "http://127.0.0.1:7890"
# # os.environ["HTTP_PROXY"] = proxy_url_http
# # os.environ["HTTPS_PROXY"] = proxy_url_http
#
# video_subject = "卖菜大妈竟是皇嫂"
# video_path = "../../resource/videos/demoyasuo.mp4"
# # video_path = "../../resource/videos/庆余年2-1-1.mp4"
#
# video_plot = ''' '''
# language = "zh-CN"
# # res = gemini_video2json(video_subject, video_path, video_plot, language)
# script = gemini_video_transcription(video_subject, video_path, language)
# cleaned_string = script.strip("```json").strip("```")
# res = json.loads(cleaned_string)
# print(res)
# get_current_country()
# api_key = config.app.get("gemini_api_key")
# model_name = config.app.get("gemini_model_name")
# gemini.configure(api_key=api_key)
# model = gemini.GenerativeModel(model_name=model_name)
# # 卖菜大妈竟是皇嫂 测试视频
# video_name = "files/y3npkshvldsd"
# video_file = gemini.get_file(video_name)
# logger.debug(f"视频当前状态(ACTIVE才可用): {video_file.state.name}")
#
# # 转录视频并提供视觉说明
# prompt = "Transcribe the audio, giving timestamps. Also provide visual descriptions. use ZH-CN ONLY"
# # Make the LLM request.
# print("发出 LLM 推理请求...")
# streams = model.generate_content([prompt, video_file],
# request_options={"timeout": 600},
# stream=True)
# response = []
# for chunk in streams:
# response.append(chunk.text)
#
# response = "".join(response)
# logger.success(f"llm response: \n{response}")
wenan = """
重要提示每一部剧的文案前几句必须吸引人
首先我们在看完看懂电影后大脑里面要先有一个大概的轮廓也就是一个类似于作文的大纲电影主题线在哪里首先要找到
一般将文案分为开头内容结尾
## 开头部分
文案开头三句话是留住用户的关键
### 方式一:开头概括总结
文案的前三句是整部电影的概括总结2-3句介绍后开始叙述故事剧情
推荐新手新号盘点型
盘点全球最恐怖的10部电影
盘点全球最科幻的10部电影
盘点全球最悲惨的10部电影
盘点全球最值得看的10部灾难电影
盘点全球最值得看的10部励志电影
下面的示例就是最简单的解说文案开头
1.这是XXX国20年来最大尺度的一部剧极度烧脑却让99%的人看得心潮澎湃无法自拔故事开始
2.这是有史以来电影院唯一一部全程开灯放完的电影期间无数人尖叫昏厥他被成为勇敢者的专属因为99%的人都不敢看到结局许多人看完它从此不愿再碰手机他就是大名鼎鼎的暗黑神作XXX
3.这到底是一部什么样的电影能被55个国家公开抵制它甚至为了上映不惜删减掉整整47分钟的剧情
4.是什么样的一个人被豆瓣网友称之为史上最牛P的老太太都70岁了还要去贩毒
5.他是M国历史上最NB//猖狂/冤枉的囚犯/抢劫犯/
6.这到底是一部什么样的影片他一个人就拿了4个顶级奖项第一季8.7第二季直接干到9.511万人给出5星好评一共也就6集却斩获26项国际大奖看过的人都说他是近年来最好的xxx剧几乎成为了近年来xxx剧的标杆故事发生在
7.他是国产电影的巅峰佳作更是许多80-90后的青春启蒙曾入选时代周刊获得年度佳片第一可在国内却被尘封多年至今为止都无法在各大视频网站看到完整资源他就是xxxxxx
8.这是一部让所有人看得荷尔蒙飙升的爽片
9.他被成为世界上最虐心绝望的电影至今无人敢看第二遍很难想象他是根据真实事件改编而来
10.这大概是有史以来最令人不寒而栗的电影当年一经放映就点燃了无数人的怒火不少观众不等影片放完就愤然离场它比xxx更让人绝望比比xxx更让人xxx能坚持看完全片的人更是万中无一包括我甚至观影结束后有无数人抵制投诉这部电影认为影片的导演玩弄了他们的情感他就是顶级神作xxxx
11.这是X国有史以来最高赞的一部悬疑电影然而却因为某些原因国内90%的人没能看过这部片子他就是xxx
12.有这样一部电影这辈子你绝对不想再看第二遍并不是它剧情烂俗而是它的结局你根本承受不起/想象不到甚至有80%的观众在观影途中情绪崩溃中途离场更让许多同行都不想解说这部电影他就是大名鼎鼎的暗黑神作xxx
13.它被誉为史上最牛悬疑片无数人在看完它时候一个月不敢照镜子这样一部仅适合部分年龄段观看的影片究竟有什么样的魅力竟然获得某瓣8.2的高分很多人说这部电影到处都是看点他就是xxx.
14.这是一部在某瓣上被70万人打出9.3分的高分的电影到底是一部什么样的电影能够在某瓣上被70万人打出9.3分的高分
15.这是一部细思极恐的科幻大片整部电影颠覆你的三观它的名字叫
16.史上最震撼的灾难片每一点都不舍得快进的电影他叫
17.今天给大家带来一部基于真实事件改编的主题介绍一句的故事片这是一部连环悬疑剧如果不看到最后绝对想不到结局竟然是这样的反转
### 方式二:情景式、假设性开头
1.他叫你以为他是的吗他是来然后开始叙述
2.你知道原来然后开始叙述
3.如果给你.你会怎么样
4.如果你是.你会怎么样
### 方式三:以国家为开头!简单明了。话语不需要多,但是需要讲解透彻!
1.这是一部韩国最新灾难片你一定没有看过
2.这是一部印度高分悬疑片
3.这部电影原在日本因为而被下架
4.这是韩国最恐怖的犯罪片
5.这是最近国产片评分最高的悬疑片
以上均按照影片国家来区分然后简单介绍下主题就可以开始直接叙述作品也是一个很不错的方法
### 方式四:如何自由发挥
正常情况下每一部电影都有非常关键的一个大纲这部电影的主题其实是可以用一句话两句话概括的只要看懂电影就能找到这个主题大纲
我们提前把这个主题大纲给放到影视最前面作为我们的前三句的文案将会非常吸引人
例如
1.这不是电影这是真实故事两个女人和一个男人被关在可桑拿室喊破喉咙也没有一丝回音窒息感和热度让人抓狂故事就是从这里开始
2.如果你男朋友出轨了他不爱你了还对你家暴怎么办接下来这部电影就会教你如何让老公服服帖帖的呆在你身边女主是一个开始叙述了
3.他力大无穷双眼放光这不是拯救地球的超人吗然而不是今天给大家推荐的这部电影叫
以上是需要看完影片看懂影片然后从里面提炼出精彩的几句话,当然是比较难的当你不会自己去总结前三句的经典的话可以用前面方式一二三
实在想不出来如何去提炼可以去搜索这部剧对这部电影的影评也会给你带过来很多灵感的
## 内容部分
开头有了剩下的就是开始叙述正文了主题介绍是根据影片内容来介绍如果实在自己想不出来可以参考其他平台中对这部电影的精彩介绍提取2-3句也可以
正常情况下我们叙述的时候其实是非常简单的把整部电影主题线叙述下来其实文案就是加些修饰词把电影重点内容叙述下来加上一些修饰词
以悬疑剧为例
竟然突然原来但是可是结果直到如果果然发现只是出奇之后没错不止更是当然因为所以
以上是比较常用的当然还有很多需要靠平时思考和阅读的积累因悬疑剧会有多处反转剧情所以需要用到反转的修饰词比较多只有用到这些词才能体现出各种反转剧情
建议大家在刚开始做的时候做8分钟内的不要太长分成三段每段也是不超过三分钟这样时间刚好可以比较好的完成完播率
## 结尾部分
最后故事的结局除了反转可以来点人生的道理如果刚开始不会可以不写
后面水平越来越高的时候可以进行人生道理的讲评
比如这部电影告诉我们
类似于哲理性质的作为一个总结
也可以把最后的影视反转原生放出来留下悬念
比如也可以总结下这部短片如何的好推荐/值得大家去观看之类的话语
其实就是给我们的作品来一个总结总结我们所做的三个视频有开始就要有结束这个结束不一定是固定的模版但是视频一定要有结尾让人感觉有头有尾才最舒服
做解说是一个比较浪费脑细胞的活虽然刚开始比较难一点但是当你正常做三部剧之后所有自己的思路都会被打开以后的基本就可以独立完成来操作来
做解说第一次可能会做两天第二次可能就需要一天了慢慢的时间缩短到8个小时之内是我们平常的制作全部时间
video_path = "E:\\projects\\NarratoAI\\resource\\videos\\2.mp4"
video_plot = """
李自忠拿着儿子李牧名下的存折去银行取钱给儿子救命却被要求证明"你儿子是你儿子"
走投无路时碰到银行被抢劫劫匪给了他两沓钱救命李自忠却因此被银行以抢劫罪起诉并顶格判处20年有期徒刑
苏醒后的李牧坚决为父亲做无罪辩护面对银行的顶级律师团队他一个法学院大一学生能否力挽狂澜创作奇迹挥法律之利剑 持正义之天平
"""
res = generate_script(video_path, video_plot, video_name="第二十条之无罪释放")
# res = generate_script(video_path, video_plot, video_name="海岸")
print("res \n", res)

View File

@ -23,7 +23,7 @@ if root_dir not in sys.path:
sys.path.append(root_dir)
print("******** sys.path ********")
print(sys.path)
print("")
print("*" * 20)
st.set_page_config(
page_title="NarratoAI",
@ -67,6 +67,8 @@ if 'video_plot' not in st.session_state:
st.session_state['video_plot'] = ''
if 'ui_language' not in st.session_state:
st.session_state['ui_language'] = config.ui.get("language", system_locale)
if 'script_generation_status' not in st.session_state:
st.session_state['script_generation_status'] = ""
def get_all_fonts():
@ -197,7 +199,6 @@ with st.expander(tr("Basic Settings"), expanded=False):
# qwen (通义千问)
# gemini
# ollama
# llm_providers = ['Gemini', 'OpenAI', 'Moonshot', 'Azure', 'Qwen', 'Ollama', 'G4f', 'OneAPI', "Cloudflare"]
llm_providers = ['Gemini']
saved_llm_provider = config.app.get("llm_provider", "OpenAI").lower()
saved_llm_provider_index = 0
@ -295,27 +296,30 @@ with left_panel:
video_json_file = params.video_clip_json
# 视频文件处理
files = []
video_files = []
for suffix in ["*.mp4", "*.mov", "*.avi", "*.mkv"]:
files.extend(glob.glob(os.path.join(utils.video_dir(), suffix)))
files = files[::-1]
video_files.extend(glob.glob(os.path.join(utils.video_dir(), suffix)))
video_files = video_files[::-1]
video_list = []
for file in files:
for video_file in video_files:
video_list.append({
"name": os.path.basename(file),
"size": os.path.getsize(file),
"file": file,
"name": os.path.basename(video_file),
"size": os.path.getsize(video_file),
"file": video_file,
"ctime": os.path.getctime(video_file) # 获取文件创建时间
})
# 按创建时间降序排序
video_list.sort(key=lambda x: x["ctime"], reverse=True)
video_path = [("None", ""), (tr("Upload Local Files"), "local")]
for code in [file['file'] for file in video_list]:
video_path.append((code, code))
# 视频文件
selected_video_index = st.selectbox(tr("Video File"),
index=0,
options=range(len(video_path)), # 使用索引作为内部选项值
format_func=lambda x: video_path[x][0] # 显示给用户的是标
format_func=lambda x: video_path[x][0] # 显示给用户的是标
)
params.video_origin_path = video_path[selected_video_index][1]
config.app["video_origin_path"] = params.video_origin_path
@ -343,7 +347,8 @@ with left_panel:
st.success(tr("File Uploaded Successfully"))
time.sleep(1)
st.rerun()
# 视频名称
video_name = st.text_input(tr("Video Name"))
# 剧情内容
video_plot = st.text_area(
tr("Plot Description"),
@ -352,16 +357,26 @@ with left_panel:
)
# 生成视频脚本
st.session_state['script_generation_status'] = "开始生成视频脚本"
if st.button(tr("Video Script Generate"), key="auto_generate_script"):
with st.spinner(tr("Video Script Generate")):
with st.spinner("正在生成脚本..."):
# 这里可以用 st.empty() 来动态更新文本
progress_text = st.empty()
progress_text.text("正在处理...")
if video_json_file == "" and params.video_origin_path != "":
progress_text.text("开始压缩...")
# 使用大模型生成视频脚本
script = llm.gemini_video2json(
video_origin_name=os.path.basename(params.video_origin_path),
video_origin_path=params.video_origin_path,
script = llm.generate_script(
video_path=params.video_origin_path,
video_plot=video_plot,
video_name=video_name,
language=params.video_language,
progress_text=progress_text
)
if script is None:
st.error("生成脚本失败,请检查日志")
st.stop()
st.session_state['video_clip_json'] = script
cleaned_string = script.strip("```json").strip("```")
st.session_state['video_script_list'] = json.loads(cleaned_string)
@ -434,6 +449,8 @@ with left_panel:
if st.session_state.get('video_script_list', None) is not None:
video_script_list = st.session_state.video_script_list
print(video_script_list)
print(type(video_script_list))
time_list = [i['timestamp'] for i in video_script_list]
subclip_videos = material.clip_videos(
task_id=st.session_state['task_id'],

View File

@ -9,6 +9,7 @@
"Generate Video Script and Keywords": "点击使用AI根据**主题**生成 【视频文案】 和 【视频关键词】",
"Auto Detect": "自动检测",
"Auto Generate": "自动生成",
"Video Name": "视频名称",
"Video Script": "视频脚本(:blue[①可不填使用AI生成 ②合理使用标点断句,有助于生成字幕]",
"Save Script": "保存脚本",
"Crop Video": "裁剪视频",