## 本次更新:

- 新增播放原声ost配置;
- 优化解说提示词
- 修改视频合成配置,支持播放原声视频
- 新增获取当前网络区域
- 优化readme
This commit is contained in:
linyq 2024-09-14 18:39:01 +08:00
parent 0669149378
commit 11bd56bd02
8 changed files with 188 additions and 161 deletions

View File

@ -56,8 +56,9 @@ NarratoAI 是一个自动化影视解说工具基于LLM实现文案撰写、
2. 点击 `Get API Key` 申请 API Key 2. 点击 `Get API Key` 申请 API Key
3. 申请的 API Key 填入 `config.example.toml` 文件中的 `gemini_api_key` 配置 3. 申请的 API Key 填入 `config.example.toml` 文件中的 `gemini_api_key` 配置
### 配置 proxy VPN ### 配置 proxy VPN 😑
> 配置vpn的方法不限只要能正常访问 Google 网络即可,本文采用的是 chash > 配置vpn的方法不限只要能正常访问 Google 网络即可,本文采用的是 [clash](https://github.com/Z-Siqi/Clash-for-Windows_Chinese) \
> 最近发现 clash 非常不稳定,在对比后发现 [v2rayN](https://github.com/2dust/v2rayN) 要好用一些 👍
1. 记住 clash 服务的端口,一般为 `http://127.0.0.1:7890` 1. 记住 clash 服务的端口,一般为 `http://127.0.0.1:7890`
2. 若端口不为 `7890`,请修改 `docker-compose.yml` 文件中的 `VPN_PROXY_URL` 为你的代理地址 2. 若端口不为 `7890`,请修改 `docker-compose.yml` 文件中的 `VPN_PROXY_URL` 为你的代理地址
```yaml ```yaml

View File

@ -57,7 +57,7 @@ Note:
3. Enter the obtained API Key into the `gemini_api_key` setting in the `config.example.toml` file. 3. Enter the obtained API Key into the `gemini_api_key` setting in the `config.example.toml` file.
### Configure Proxy VPN ### Configure Proxy VPN
> The method to configure VPN is not restricted, as long as you can access Google's network. Here, `clash` is used as an example. > The method to configure VPN is not restricted, as long as you can access Google's network. Here, [clash](https://github.com/Z-Siqi/Clash-for-Windows_Chinese) is used as an example.
1. Note the port of the clash service, usually `http://127.0.0.1:7890`. 1. Note the port of the clash service, usually `http://127.0.0.1:7890`.
2. If the port is not `7890`, modify the `VPN_PROXY_URL` in the `docker-compose.yml` file to your proxy address. 2. If the port is not `7890`, modify the `VPN_PROXY_URL` in the `docker-compose.yml` file to your proxy address.
```yaml ```yaml

View File

@ -405,43 +405,48 @@ def gemini_video2json(video_origin_name: str, video_origin_path: str, video_plot
model = gemini.GenerativeModel(model_name=model_name) model = gemini.GenerativeModel(model_name=model_name)
prompt = """ prompt = """
# 角色设定: **角色设定**
你是一位影视解说专家擅长根据剧情描述视频的画面和故事生成一段有趣且吸引人的解说文案你特别熟悉 tiktok/抖音 风格的影视解说文案创作 你是一位影视解说专家擅长根据剧情生成引人入胜的短视频解说文案特别熟悉适用于TikTok/抖音风格的快速抓人视频解说
# 任务目标: **任务目标**
1. 根据给定的剧情描述详细描述视频画面并展开叙述尤其是对重要画面进行细致刻画 1. 根据给定剧情详细描述画面重点突出重要场景和情节
2. 生成风格符合 tiktok/抖音 的影视解说文案使其节奏快内容抓人 2. 生成符合TikTok/抖音风格的解说节奏紧凑语言简洁吸引观众
3. 最终结果以 JSON 格式输出字段包含 3. 解说的时候需要解说一段播放一段原视频原视频一般为有台词的片段原视频的控制有 OST 字段控制
"picture"画面描述 4. 结果输出为JSON格式包含字段
"timestamp"时间戳表示画面出现的时间-画面结束的时间 - "picture"画面描述
"narration"对应的解说文案 - "timestamp"画面出现的时间范围
- "narration"解说内容
- "OST": 是否开启原声true / false
# 输入示例: **输入示例**
```text ```text
在一个黑暗的小巷中主角缓慢走进四周静谧无声只有远处隐隐传来猫的叫声突然背后出现一个神秘的身影 在一个黑暗的小巷中主角缓慢走进四周静谧无声只有远处隐隐传来猫的叫声突然背后出现一个神秘的身影
``` ```
# 输出格式: **输出格式**
```json ```json
[ [
{ {
"picture": "黑暗的小巷中,主角缓慢走进,四周静谧无声,远处有模糊的猫叫声。", "picture": "黑暗的小巷,主角缓慢走入,四周安静,远处传来猫叫声。",
"timestamp": "00:00-00:17", "timestamp": "00:00-00:17",
"narration": "昏暗的小巷里,他独自前行,空气中透着一丝不安,隐约中能听到远处的猫叫声。 " "narration": "静谧的小巷里,主角步步前行,气氛渐渐变得压抑。"
"OST": False
}, },
{ {
"picture": "主角背后突然出现一个神秘的身影,气氛骤然紧张。", "picture": "神秘身影突然出现,紧张气氛加剧。",
"timestamp": "00:17-00:39", "timestamp": "00:17-00:39",
"narration": "就在他以为安全时,一个身影悄无声息地出现在他身后,危险一步步逼近! " "narration": "原声播放"
"OST": True
} }
...
] ]
``` ```
# 提示:
- 生成的解说文案应简洁有力符合短视频平台用户的偏好 **提示**
- 叙述中应有强烈的代入感和悬念以吸引观众持续观看 - 文案要简短有力契合短视频平台用户的观赏习惯
- 文案语言为%s - 保持强烈的悬念和情感代入吸引观众继续观看
- 剧情内容如下%s (若为空则忽略) - 解说一段后播放一段原声原声内容尽量和解说匹配
- 文案语言为%s
- 剧情内容%s (为空则忽略)
""" % (language, video_plot) """ % (language, video_plot)
@ -472,59 +477,46 @@ def gemini_video2json(video_origin_name: str, video_origin_path: str, video_plot
if __name__ == "__main__": if __name__ == "__main__":
video_subject = "摔跤吧!爸爸 Dangal" """
video_path = "/NarratoAI/resource/videos/test.mp4" File API 可让您为每个项目存储最多 20 GB 的文件每个项目使用 每个文件的大小上限为 2 GB文件会存储 48 小时
video_plot = ''' 它们可以是 在此期间使用您的 API 密钥访问但无法下载 使用任何 API它已在使用 Gemini 的所有地区免费提供 API 可用
马哈维亚阿米尔· Aamir Khan 曾经是一名前途无量的摔跤运动员在放弃了职业生涯后他最大的遗憾就是没有能够替国家赢得金牌马哈维亚将这份希望寄托在了尚未出生的儿子身上哪知道妻子接连给他生了两个女儿取名吉塔法缇玛·萨那·纱卡 Fatima Sana Shaikh 和巴比塔桑亚·玛荷塔 Sanya Malhotra 让马哈维亚没有想到的是两个姑娘展现出了杰出的摔跤天赋让他幡然醒悟就算是女孩也能够昂首挺胸的站在比赛场上为了国家和她们自己赢得荣誉 """
就这样在马哈维亚的指导下吉塔和巴比塔开始了艰苦的训练两人进步神速很快就因为在比赛中连连获胜而成为了当地的名人为了获得更多的机会吉塔进入了国家体育学院学习在那里她将面对更大的诱惑和更多的选择 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__))))
#
video_subject = "卖菜大妈竟是皇嫂"
video_path = "/NarratoAI/resource/videos/demoyasuo.mp4"
video_plot = ''' '''
language = "zh-CN" language = "zh-CN"
res = gemini_video2json(video_subject, video_path, video_plot, language) res = gemini_video2json(video_subject, video_path, video_plot, language)
print(res) print(res)
# video_subject = "生命的意义是什么" # get_current_country()
# script = generate_script( # api_key = config.app.get("gemini_api_key")
# video_subject=video_subject, language="zh-CN", paragraph_number=1 # model_name = config.app.get("gemini_model_name")
# ) # gemini.configure(api_key=api_key)
# print("######################") # model = gemini.GenerativeModel(model_name=model_name)
# print(script) # # 卖菜大妈竟是皇嫂 测试视频
# search_terms = generate_terms( # video_name = "files/y3npkshvldsd"
# video_subject=video_subject, video_script=script, amount=5 # video_file = gemini.get_file(video_name)
# ) # logger.debug(f"视频当前状态(ACTIVE才可用): {video_file.state.name}")
# print("######################")
# print(search_terms)
# prompt = """
# # Role: 影视解说专家
# #
# ## Background: # # 转录视频并提供视觉说明
# 擅长根据剧情描述视频的画面和故事,能够生成一段非常有趣的解说文案。 # 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)
# #
# ## Goals: # response = "".join(response)
# 1. 根据剧情描述视频的画面和故事,并对重要的画面进行展开叙述 # logger.success(f"llm response: \n{response}")
# 2. 根据剧情内容,生成符合 tiktok/抖音 风格的影视解说文案
# 3. 将结果直接以json格式输出给用户需要包含字段 picture 画面描述, timestamp 时间戳, narration 解说文案
# 4. 剧情内容如下:{%s}
#
# ## Skills
# - 精通 tiktok/抖音 等短视频影视解说文案撰写
# - 能够理解视频中的故事和画面表现
# - 能精准匹配视频中的画面和时间戳
# - 能精准把控旁白和时长
# - 精通中文
# - 精通JSON数据格式
#
# ## Constrains
# - 解说文案的时长要和时间戳的时长尽量匹配
# - 忽略视频中关于广告的内容
# - 忽略视频中片头和片尾
# - 不得在脚本中包含任何类型的 Markdown 或格式
#
# ## Format
# - 对应JSON的key为picture timestamp narration
#
# # Initialization:
# - video subject: {video_subject}
# - number of paragraphs: {paragraph_number}
# """.strip()
# if language:
# prompt += f"\n- language: {language}"

View File

@ -346,6 +346,7 @@ def start_subclip(task_id, params: VideoClipParams, subclip_path_videos):
with open(video_script_path, "r", encoding="utf-8") as f: with open(video_script_path, "r", encoding="utf-8") as f:
list_script = json.load(f) list_script = json.load(f)
video_list = [i['narration'] for i in list_script] video_list = [i['narration'] for i in list_script]
video_ost = [i['OST'] for i in list_script]
time_list = [i['timestamp'] for i in list_script] time_list = [i['timestamp'] for i in list_script]
video_script = " ".join(video_list) video_script = " ".join(video_list)
@ -421,12 +422,14 @@ def start_subclip(task_id, params: VideoClipParams, subclip_path_videos):
index = i + 1 index = i + 1
combined_video_path = path.join(utils.task_dir(task_id), f"combined-{index}.mp4") combined_video_path = path.join(utils.task_dir(task_id), f"combined-{index}.mp4")
logger.info(f"\n\n## 5. 合并视频: {index} => {combined_video_path}") logger.info(f"\n\n## 5. 合并视频: {index} => {combined_video_path}")
video.combine_clip_videos(combined_video_path=combined_video_path, video.combine_clip_videos(
video_paths=subclip_videos, combined_video_path=combined_video_path,
video_script_list=video_list, video_paths=subclip_videos,
audio_file=audio_file, video_ost_list=video_ost,
video_aspect=params.video_aspect, audio_file=audio_file,
threads=n_threads) video_aspect=params.video_aspect,
threads=n_threads
)
_progress += 50 / params.video_count / 2 _progress += 50 / params.video_count / 2
sm.state.update_task(task_id, progress=_progress) sm.state.update_task(task_id, progress=_progress)

View File

@ -352,7 +352,7 @@ def preprocess_video(materials: List[MaterialInfo], clip_duration=4):
def combine_clip_videos(combined_video_path: str, def combine_clip_videos(combined_video_path: str,
video_paths: List[str], video_paths: List[str],
video_script_list: List[str], video_ost_list: List[str],
audio_file: str, audio_file: str,
video_aspect: VideoAspect = VideoAspect.portrait, video_aspect: VideoAspect = VideoAspect.portrait,
threads: int = 2, threads: int = 2,
@ -385,8 +385,12 @@ def combine_clip_videos(combined_video_path: str,
video_duration = 0 video_duration = 0
# 一遍又一遍地添加下载的剪辑,直到达到音频的持续时间 max_duration # 一遍又一遍地添加下载的剪辑,直到达到音频的持续时间 max_duration
while video_duration < audio_duration: while video_duration < audio_duration:
for video_path, video_script in zip(video_paths, video_script_list): for video_path, video_ost in zip(video_paths, video_ost_list):
clip = VideoFileClip(video_path).without_audio() clip = VideoFileClip(video_path)
if video_ost:
clip = clip.set_audio(audio_clip)
else:
clip = clip.set_audio(audio_clip).without_audio()
# 检查剪辑是否比剩余音频长 # 检查剪辑是否比剩余音频长
if (audio_duration - video_duration) < clip.duration: if (audio_duration - video_duration) < clip.duration:
clip = clip.subclip(0, (audio_duration - video_duration)) clip = clip.subclip(0, (audio_duration - video_duration))

View File

@ -1288,67 +1288,68 @@ if __name__ == "__main__":
voice_name = parse_voice_name(voice_name) voice_name = parse_voice_name(voice_name)
voice_name = is_azure_v2_voice(voice_name) voice_name = is_azure_v2_voice(voice_name)
print(voice_name) print(voice_name)
a = tts("预计未来3天深圳冷空气活动频繁, 等待5个字5个字结束", "zh-CN-YunyangNeural", 1.2, "/NarratoAI/test123.mp3")
print(a)
# voices = get_all_azure_voices()
# print(len(voices))
voices = get_all_azure_voices() # async def _do():
print(len(voices)) # temp_dir = utils.storage_dir("temp")
#
async def _do(): # voice_names = [
temp_dir = utils.storage_dir("temp") # "zh-CN-XiaoxiaoMultilingualNeural",
# # 女性
voice_names = [ # "zh-CN-XiaoxiaoNeural",
"zh-CN-XiaoxiaoMultilingualNeural", # "zh-CN-XiaoyiNeural",
# 女性 # # 男性
"zh-CN-XiaoxiaoNeural", # "zh-CN-YunyangNeural",
"zh-CN-XiaoyiNeural", # "zh-CN-YunxiNeural",
# 男性 # ]
"zh-CN-YunyangNeural", # text = """
"zh-CN-YunxiNeural", # 静夜思是唐代诗人李白创作的一首五言古诗。这首诗描绘了诗人在寂静的夜晚,看到窗前的明月,不禁想起远方的家乡和亲人,表达了他对家乡和亲人的深深思念之情。全诗内容是:“床前明月光,疑是地上霜。举头望明月,低头思故乡。”在这短短的四句诗中,诗人通过“明月”和“思故乡”的意象,巧妙地表达了离乡背井人的孤独与哀愁。首句“床前明月光”设景立意,通过明亮的月光引出诗人的遐想;“疑是地上霜”增添了夜晚的寒冷感,加深了诗人的孤寂之情;“举头望明月”和“低头思故乡”则是情感的升华,展现了诗人内心深处的乡愁和对家的渴望。这首诗简洁明快,情感真挚,是中国古典诗歌中非常著名的一首,也深受后人喜爱和推崇。
] # """
text = """ #
静夜思是唐代诗人李白创作的一首五言古诗这首诗描绘了诗人在寂静的夜晚看到窗前的明月不禁想起远方的家乡和亲人表达了他对家乡和亲人的深深思念之情全诗内容是床前明月光疑是地上霜举头望明月低头思故乡在这短短的四句诗中诗人通过明月思故乡的意象巧妙地表达了离乡背井人的孤独与哀愁首句床前明月光设景立意通过明亮的月光引出诗人的遐想疑是地上霜增添了夜晚的寒冷感加深了诗人的孤寂之情举头望明月低头思故乡则是情感的升华展现了诗人内心深处的乡愁和对家的渴望这首诗简洁明快情感真挚是中国古典诗歌中非常著名的一首也深受后人喜爱和推崇 # text = """
""" # What is the meaning of life? This question has puzzled philosophers, scientists, and thinkers of all kinds for centuries. Throughout history, various cultures and individuals have come up with their interpretations and beliefs around the purpose of life. Some say it's to seek happiness and self-fulfillment, while others believe it's about contributing to the welfare of others and making a positive impact in the world. Despite the myriad of perspectives, one thing remains clear: the meaning of life is a deeply personal concept that varies from one person to another. It's an existential inquiry that encourages us to reflect on our values, desires, and the essence of our existence.
# """
text = """ #
What is the meaning of life? This question has puzzled philosophers, scientists, and thinkers of all kinds for centuries. Throughout history, various cultures and individuals have come up with their interpretations and beliefs around the purpose of life. Some say it's to seek happiness and self-fulfillment, while others believe it's about contributing to the welfare of others and making a positive impact in the world. Despite the myriad of perspectives, one thing remains clear: the meaning of life is a deeply personal concept that varies from one person to another. It's an existential inquiry that encourages us to reflect on our values, desires, and the essence of our existence. # text = """
""" # 预计未来3天深圳冷空气活动频繁未来两天持续阴天有小雨出门带好雨具
# 10-11日持续阴天有小雨日温差小气温在13-17℃之间体感阴凉
text = """ # 12日天气短暂好转早晚清凉
预计未来3天深圳冷空气活动频繁未来两天持续阴天有小雨出门带好雨具 # """
10-11日持续阴天有小雨日温差小气温在13-17之间体感阴凉 #
12日天气短暂好转早晚清凉 # text = "[Opening scene: A sunny day in a suburban neighborhood. A young boy named Alex, around 8 years old, is playing in his front yard with his loyal dog, Buddy.]\n\n[Camera zooms in on Alex as he throws a ball for Buddy to fetch. Buddy excitedly runs after it and brings it back to Alex.]\n\nAlex: Good boy, Buddy! You're the best dog ever!\n\n[Buddy barks happily and wags his tail.]\n\n[As Alex and Buddy continue playing, a series of potential dangers loom nearby, such as a stray dog approaching, a ball rolling towards the street, and a suspicious-looking stranger walking by.]\n\nAlex: Uh oh, Buddy, look out!\n\n[Buddy senses the danger and immediately springs into action. He barks loudly at the stray dog, scaring it away. Then, he rushes to retrieve the ball before it reaches the street and gently nudges it back towards Alex. Finally, he stands protectively between Alex and the stranger, growling softly to warn them away.]\n\nAlex: Wow, Buddy, you're like my superhero!\n\n[Just as Alex and Buddy are about to head inside, they hear a loud crash from a nearby construction site. They rush over to investigate and find a pile of rubble blocking the path of a kitten trapped underneath.]\n\nAlex: Oh no, Buddy, we have to help!\n\n[Buddy barks in agreement and together they work to carefully move the rubble aside, allowing the kitten to escape unharmed. The kitten gratefully nuzzles against Buddy, who responds with a friendly lick.]\n\nAlex: We did it, Buddy! We saved the day again!\n\n[As Alex and Buddy walk home together, the sun begins to set, casting a warm glow over the neighborhood.]\n\nAlex: Thanks for always being there to watch over me, Buddy. You're not just my dog, you're my best friend.\n\n[Buddy barks happily and nuzzles against Alex as they disappear into the sunset, ready to face whatever adventures tomorrow may bring.]\n\n[End scene.]"
""" #
# text = "大家好,我是乔哥,一个想帮你把信用卡全部还清的家伙!\n今天我们要聊的是信用卡的取现功能。\n你是不是也曾经因为一时的资金紧张而拿着信用卡到ATM机取现如果是那你得好好看看这个视频了。\n现在都2024年了我以为现在不会再有人用信用卡取现功能了。前几天一个粉丝发来一张图片取现1万。\n信用卡取现有三个弊端。\n一信用卡取现功能代价可不小。会先收取一个取现手续费比如这个粉丝取现1万按2.5%收取手续费收取了250元。\n二信用卡正常消费有最长56天的免息期但取现不享受免息期。从取现那一天开始每天按照万5收取利息这个粉丝用了11天收取了55元利息。\n三频繁的取现行为银行会认为你资金紧张会被标记为高风险用户影响你的综合评分和额度。\n那么如果你资金紧张了该怎么办呢\n乔哥给你支一招用破思机摩擦信用卡只需要少量的手续费而且还可以享受最长56天的免息期。\n最后如果你对玩卡感兴趣可以找乔哥领取一本《卡神秘籍》用卡过程中遇到任何疑惑也欢迎找乔哥交流。\n别忘了关注乔哥回复用卡技巧免费领取《2024用卡技巧》让我们一起成为用卡高手"
text = "[Opening scene: A sunny day in a suburban neighborhood. A young boy named Alex, around 8 years old, is playing in his front yard with his loyal dog, Buddy.]\n\n[Camera zooms in on Alex as he throws a ball for Buddy to fetch. Buddy excitedly runs after it and brings it back to Alex.]\n\nAlex: Good boy, Buddy! You're the best dog ever!\n\n[Buddy barks happily and wags his tail.]\n\n[As Alex and Buddy continue playing, a series of potential dangers loom nearby, such as a stray dog approaching, a ball rolling towards the street, and a suspicious-looking stranger walking by.]\n\nAlex: Uh oh, Buddy, look out!\n\n[Buddy senses the danger and immediately springs into action. He barks loudly at the stray dog, scaring it away. Then, he rushes to retrieve the ball before it reaches the street and gently nudges it back towards Alex. Finally, he stands protectively between Alex and the stranger, growling softly to warn them away.]\n\nAlex: Wow, Buddy, you're like my superhero!\n\n[Just as Alex and Buddy are about to head inside, they hear a loud crash from a nearby construction site. They rush over to investigate and find a pile of rubble blocking the path of a kitten trapped underneath.]\n\nAlex: Oh no, Buddy, we have to help!\n\n[Buddy barks in agreement and together they work to carefully move the rubble aside, allowing the kitten to escape unharmed. The kitten gratefully nuzzles against Buddy, who responds with a friendly lick.]\n\nAlex: We did it, Buddy! We saved the day again!\n\n[As Alex and Buddy walk home together, the sun begins to set, casting a warm glow over the neighborhood.]\n\nAlex: Thanks for always being there to watch over me, Buddy. You're not just my dog, you're my best friend.\n\n[Buddy barks happily and nuzzles against Alex as they disappear into the sunset, ready to face whatever adventures tomorrow may bring.]\n\n[End scene.]" #
# text = """
text = "大家好,我是乔哥,一个想帮你把信用卡全部还清的家伙!\n今天我们要聊的是信用卡的取现功能。\n你是不是也曾经因为一时的资金紧张而拿着信用卡到ATM机取现如果是那你得好好看看这个视频了。\n现在都2024年了我以为现在不会再有人用信用卡取现功能了。前几天一个粉丝发来一张图片取现1万。\n信用卡取现有三个弊端。\n信用卡取现功能代价可不小。会先收取一个取现手续费比如这个粉丝取现1万按2.5%收取手续费收取了250元。\n信用卡正常消费有最长56天的免息期但取现不享受免息期。从取现那一天开始每天按照万5收取利息这个粉丝用了11天收取了55元利息。\n三,频繁的取现行为,银行会认为你资金紧张,会被标记为高风险用户,影响你的综合评分和额度。\n那么,如果你资金紧张了,该怎么办呢?\n乔哥给你支一招用破思机摩擦信用卡只需要少量的手续费而且还可以享受最长56天的免息期。\n最后,如果你对玩卡感兴趣,可以找乔哥领取一本《卡神秘籍》,用卡过程中遇到任何疑惑,也欢迎找乔哥交流。\n别忘了关注乔哥回复用卡技巧免费领取《2024用卡技巧》让我们一起成为用卡高手" # 2023全年业绩速览
# 公司全年累计实现营业收入1476.94亿元同比增长19.01%归母净利润747.34亿元同比增长19.16%。EPS达到59.49元。第四季度单季营业收入444.25亿元同比增长20.26%环比增长31.86%归母净利润218.58亿元同比增长19.33%环比增长29.37%。这一阶段
text = """ # 的业绩表现不仅突显了公司的增长动力和盈利能力,也反映出公司在竞争激烈的市场环境中保持了良好的发展势头。
2023全年业绩速览 # 2023年Q4业绩速览
公司全年累计实现营业收入1476.94亿元同比增长19.01%归母净利润747.34亿元同比增长19.16%EPS达到59.49第四季度单季营业收入444.25亿元同比增长20.26%环比增长31.86%归母净利润218.58亿元同比增长19.33%环比增长29.37%这一阶段 # 第四季度营业收入贡献主要增长点销售费用高增致盈利能力承压税金同比上升27%,扰动净利率表现。
的业绩表现不仅突显了公司的增长动力和盈利能力也反映出公司在竞争激烈的市场环境中保持了良好的发展势头 # 业绩解读
2023年Q4业绩速览 # 利润方面2023全年贵州茅台>归母净利润增速为19%其中营业收入正贡献18%,营业成本正贡献百分之一,管理费用正贡献百分之一点四。(注:归母净利润增速值=营业收入增速+各科目贡献,展示贡献/拖累的前四名科目,且要求贡献值/净利润增速>15%)
第四季度营业收入贡献主要增长点销售费用高增致盈利能力承压税金同比上升27%扰动净利率表现 # """
业绩解读 # text = "静夜思是唐代诗人李白创作的一首五言古诗。这首诗描绘了诗人在寂静的夜晚,看到窗前的明月,不禁想起远方的家乡和亲人"
利润方面2023全年贵州茅台>归母净利润增速为19%其中营业收入正贡献18%营业成本正贡献百分之一管理费用正贡献百分之一点四(归母净利润增速值=营业收入增速+各科目贡献展示贡献/拖累的前四名科目且要求贡献值/净利润增速>15%) #
""" # text = _format_text(text)
text = "静夜思是唐代诗人李白创作的一首五言古诗。这首诗描绘了诗人在寂静的夜晚,看到窗前的明月,不禁想起远方的家乡和亲人" # lines = utils.split_string_by_punctuations(text)
# print(lines)
text = _format_text(text) #
lines = utils.split_string_by_punctuations(text) # for voice_name in voice_names:
print(lines) # voice_file = f"{temp_dir}/tts-{voice_name}.mp3"
# subtitle_file = f"{temp_dir}/tts.mp3.srt"
for voice_name in voice_names: # sub_maker = azure_tts_v2(
voice_file = f"{temp_dir}/tts-{voice_name}.mp3" # text=text, voice_name=voice_name, voice_file=voice_file
subtitle_file = f"{temp_dir}/tts.mp3.srt" # )
sub_maker = azure_tts_v2( # create_subtitle(sub_maker=sub_maker, text=text, subtitle_file=subtitle_file)
text=text, voice_name=voice_name, voice_file=voice_file # audio_duration = get_audio_duration(sub_maker)
) # print(f"voice: {voice_name}, audio duration: {audio_duration}s")
create_subtitle(sub_maker=sub_maker, text=text, subtitle_file=subtitle_file) #
audio_duration = get_audio_duration(sub_maker) # loop = asyncio.get_event_loop_policy().get_event_loop()
print(f"voice: {voice_name}, audio duration: {audio_duration}s") # try:
# loop.run_until_complete(_do())
loop = asyncio.get_event_loop_policy().get_event_loop() # finally:
try: # loop.close()
loop.run_until_complete(_do())
finally:
loop.close()

View File

@ -1,6 +1,6 @@
import locale import locale
import os import os
import platform import requests
import threading import threading
from typing import Any from typing import Any
from loguru import logger from loguru import logger
@ -269,3 +269,27 @@ def reduce_video_time(txt: str, duration: float = 0.21531):
# 返回结果四舍五入为整数 # 返回结果四舍五入为整数
duration = len(txt) * duration duration = len(txt) * duration
return int(duration) return int(duration)
def get_current_country():
"""
判断当前网络IP地址所在的国家
"""
try:
# 使用ipapi.co的免费API获取IP地址信息
response = requests.get('https://ipapi.co/json/')
data = response.json()
# 获取国家名称
country = data.get('country_name')
if country:
logger.debug(f"当前网络IP地址位于{country}")
return country
else:
logger.debug("无法确定当前网络IP地址所在的国家")
return None
except requests.RequestException:
logger.error("获取IP地址信息时发生错误请检查网络连接")
return None

View File

@ -354,6 +354,7 @@ with left_panel:
if st.button(tr("Video Script Generate"), key="auto_generate_script"): if st.button(tr("Video Script Generate"), key="auto_generate_script"):
with st.spinner(tr("Video Script Generate")): with st.spinner(tr("Video Script Generate")):
if video_json_file == "" and params.video_origin_path != "": if video_json_file == "" and params.video_origin_path != "":
# 使用大模型生成视频脚本
script = llm.gemini_video2json( script = llm.gemini_video2json(
video_origin_name=params.video_origin_path.split("\\")[-1], video_origin_name=params.video_origin_path.split("\\")[-1],
video_origin_path=params.video_origin_path, video_origin_path=params.video_origin_path,
@ -732,6 +733,7 @@ with st.expander(tr("Video Check"), expanded=False):
text1 = st.text_area(tr("timestamp"), value=initial_timestamp, height=20) text1 = st.text_area(tr("timestamp"), value=initial_timestamp, height=20)
with text_panels[1]: with text_panels[1]:
text2 = st.text_area(tr("Picture description"), value=initial_picture, height=20) text2 = st.text_area(tr("Picture description"), value=initial_picture, height=20)
logger.debug(initial_narration)
text3 = st.text_area(tr("Narration"), value=initial_narration, height=100) text3 = st.text_area(tr("Narration"), value=initial_narration, height=100)
# 重新生成按钮 # 重新生成按钮