This commit is contained in:
highkay
2024-04-11 22:55:18 +08:00
10 changed files with 67 additions and 40 deletions

View File

@@ -66,6 +66,9 @@
- [ ] 支持更多的语音合成服务商,比如 OpenAI TTS, Azure TTS - [ ] 支持更多的语音合成服务商,比如 OpenAI TTS, Azure TTS
- [ ] 自动上传到YouTube平台 - [ ] 自动上传到YouTube平台
## 交流讨论 💬
<img src="docs/wechat-01.jpg" width="300">
## 视频演示 📺 ## 视频演示 📺
### 竖屏 9:16 ### 竖屏 9:16
@@ -102,8 +105,17 @@
</tbody> </tbody>
</table> </table>
## 配置要求 📦
- 建议最低 CPU 4核或以上内存 8G 或以上,显卡非必须
- Windows 10 或 MacOS 11.0 以上系统
## 安装部署 📥 ## 安装部署 📥
> 不想部署的可以直接下载安装包,解压直接使用
- **Windows** 版本下载地址
- 百度网盘: https://pan.baidu.com/s/1BB3SGtAFTytzFLS5t2d8Gg?pwd=5bry
### 前提条件
- 尽量不要使用 **中文路径**,避免出现一些无法预料的问题 - 尽量不要使用 **中文路径**,避免出现一些无法预料的问题
- 请确保你的 **网络** 是正常的VPN需要打开`全局流量`模式 - 请确保你的 **网络** 是正常的VPN需要打开`全局流量`模式
@@ -375,14 +387,6 @@ pip install Pillow==8.4.0
- 可以提交 [issue](https://github.com/harry0703/MoneyPrinterTurbo/issues) - 可以提交 [issue](https://github.com/harry0703/MoneyPrinterTurbo/issues)
或者 [pull request](https://github.com/harry0703/MoneyPrinterTurbo/pulls)。 或者 [pull request](https://github.com/harry0703/MoneyPrinterTurbo/pulls)。
- 也可以关注我的 **抖音****视频号**`网旭哈瑞.AI`
- 我会在上面发布一些 **使用教程****纯技术** 分享。
- 如果有更新和优化,我也会在上面 **及时通知**
- 有问题也可以在上面 **留言**,我会 **尽快回复**
| 抖音 | | 视频号 |
|:---------------------------------------:|:------------:|:-------------------------------------------:|
| <img src="docs/douyin.jpg" width="180"> | | <img src="docs/shipinghao.jpg" width="200"> |
## 参考项目 📚 ## 参考项目 📚

View File

@@ -53,7 +53,7 @@ listen_port = _cfg.get("listen_port", 8080)
project_name = _cfg.get("project_name", "MoneyPrinterTurbo") project_name = _cfg.get("project_name", "MoneyPrinterTurbo")
project_description = _cfg.get("project_description", project_description = _cfg.get("project_description",
"<a href='https://github.com/harry0703/MoneyPrinterTurbo'>https://github.com/harry0703/MoneyPrinterTurbo</a>") "<a href='https://github.com/harry0703/MoneyPrinterTurbo'>https://github.com/harry0703/MoneyPrinterTurbo</a>")
project_version = _cfg.get("project_version", "1.0.1") project_version = _cfg.get("project_version", "1.1.0")
reload_debug = False reload_debug = False
imagemagick_path = app.get("imagemagick_path", "") imagemagick_path = app.get("imagemagick_path", "")

View File

@@ -5,6 +5,8 @@ from typing import List
from loguru import logger from loguru import logger
from openai import OpenAI from openai import OpenAI
from openai import AzureOpenAI from openai import AzureOpenAI
from openai.types.chat import ChatCompletion
from app.config import config from app.config import config
@@ -154,7 +156,15 @@ def _generate_response(prompt: str) -> str:
messages=[{"role": "user", "content": prompt}] messages=[{"role": "user", "content": prompt}]
) )
if response: if response:
if isinstance(response, ChatCompletion):
content = response.choices[0].message.content content = response.choices[0].message.content
else:
raise Exception(
f"[{llm_provider}] returned an invalid response: \"{response}\", please check your network "
f"connection and try again.")
else:
raise Exception(
f"[{llm_provider}] returned an empty response, please check your network connection and try again.")
return content.replace("\n", "") return content.replace("\n", "")

View File

@@ -1,4 +1,5 @@
import json import json
import os.path
import re import re
from faster_whisper import WhisperModel from faster_whisper import WhisperModel
@@ -17,8 +18,13 @@ model = None
def create(audio_file, subtitle_file: str = ""): def create(audio_file, subtitle_file: str = ""):
global model global model
if not model: if not model:
logger.info(f"loading model: {model_size}, device: {device}, compute_type: {compute_type}") model_path = f"{utils.root_dir()}/models/whisper-{model_size}"
model = WhisperModel(model_size_or_path=model_size, model_bin_file = f"{model_path}/model.bin"
if not os.path.isdir(model_path) or not os.path.isfile(model_bin_file):
model_path = model_size
logger.info(f"loading model: {model_path}, device: {device}, compute_type: {compute_type}")
model = WhisperModel(model_size_or_path=model_path,
device=device, device=device,
compute_type=compute_type) compute_type=compute_type)

View File

@@ -124,7 +124,7 @@ def wrap_text(text, max_width, font='Arial', fontsize=60):
width, height = get_text_size(text) width, height = get_text_size(text)
if width <= max_width: if width <= max_width:
return text return text, height
logger.warning(f"wrapping text, max_width: {max_width}, text_width: {width}, text: {text}") logger.warning(f"wrapping text, max_width: {max_width}, text_width: {width}, text: {text}")
@@ -149,8 +149,9 @@ def wrap_text(text, max_width, font='Arial', fontsize=60):
if processed: if processed:
_wrapped_lines_ = [line.strip() for line in _wrapped_lines_] _wrapped_lines_ = [line.strip() for line in _wrapped_lines_]
result = '\n'.join(_wrapped_lines_).strip() result = '\n'.join(_wrapped_lines_).strip()
height = len(_wrapped_lines_) * height
logger.warning(f"wrapped text: {result}") logger.warning(f"wrapped text: {result}")
return result return result, height
_wrapped_lines_ = [] _wrapped_lines_ = []
chars = list(text) chars = list(text)
@@ -165,8 +166,9 @@ def wrap_text(text, max_width, font='Arial', fontsize=60):
_txt_ = '' _txt_ = ''
_wrapped_lines_.append(_txt_) _wrapped_lines_.append(_txt_)
result = '\n'.join(_wrapped_lines_).strip() result = '\n'.join(_wrapped_lines_).strip()
height = len(_wrapped_lines_) * height
logger.warning(f"wrapped text: {result}") logger.warning(f"wrapped text: {result}")
return result return result, height
def generate_video(video_path: str, def generate_video(video_path: str,
@@ -199,23 +201,15 @@ def generate_video(video_path: str,
logger.info(f"using font: {font_path}") logger.info(f"using font: {font_path}")
if params.subtitle_position == "top": def create_text_clip(subtitle_item):
position_height = video_height * 0.1 phrase = subtitle_item[1]
elif params.subtitle_position == "bottom":
position_height = video_height * 0.9
else:
position_height = "center"
def generator(txt, **kwargs):
max_width = video_width * 0.9 max_width = video_width * 0.9
# logger.debug(f"rendering text: {txt}") wrapped_txt, txt_height = wrap_text(phrase,
wrapped_txt = wrap_text(txt,
max_width=max_width, max_width=max_width,
font=font_path, font=font_path,
fontsize=params.font_size fontsize=params.font_size
) # 调整max_width以适应你的视频 )
_clip = TextClip(
clip = TextClip(
wrapped_txt, wrapped_txt,
font=font_path, font=font_path,
fontsize=params.font_size, fontsize=params.font_size,
@@ -225,15 +219,28 @@ def generate_video(video_path: str,
stroke_width=params.stroke_width, stroke_width=params.stroke_width,
print_cmd=False, print_cmd=False,
) )
return clip duration = subtitle_item[0][1] - subtitle_item[0][0]
_clip = _clip.set_start(subtitle_item[0][0])
_clip = _clip.set_end(subtitle_item[0][1])
_clip = _clip.set_duration(duration)
if params.subtitle_position == "bottom":
_clip = _clip.set_position(('center', video_height * 0.95 - _clip.h))
elif params.subtitle_position == "top":
_clip = _clip.set_position(('center', video_height * 0.1))
else:
_clip = _clip.set_position(('center', 'center'))
return _clip
video_clip = VideoFileClip(video_path) video_clip = VideoFileClip(video_path)
audio_clip = AudioFileClip(audio_path).volumex(params.voice_volume) audio_clip = AudioFileClip(audio_path).volumex(params.voice_volume)
if subtitle_path and os.path.exists(subtitle_path): if subtitle_path and os.path.exists(subtitle_path):
sub = SubtitlesClip(subtitles=subtitle_path, make_textclip=generator, encoding='utf-8') sub = SubtitlesClip(subtitles=subtitle_path, encoding='utf-8')
sub_clip = sub.set_position(lambda _t: ('center', position_height)) text_clips = []
video_clip = CompositeVideoClip([video_clip, sub_clip]) for item in sub.subtitles:
clip = create_text_clip(subtitle_item=item)
text_clips.append(clip)
video_clip = CompositeVideoClip([video_clip, *text_clips])
bgm_file = get_bgm_file(bgm_type=params.bgm_type, bgm_file=params.bgm_file) bgm_file = get_bgm_file(bgm_type=params.bgm_type, bgm_file=params.bgm_file)
if bgm_file: if bgm_file:
@@ -258,7 +265,7 @@ if __name__ == "__main__":
txt_zh = "测试长字段这是您的旅行技巧指南帮助您进行预算友好的冒险" txt_zh = "测试长字段这是您的旅行技巧指南帮助您进行预算友好的冒险"
font = utils.resource_dir() + "/fonts/STHeitiMedium.ttc" font = utils.resource_dir() + "/fonts/STHeitiMedium.ttc"
for txt in [txt_en, txt_zh]: for txt in [txt_en, txt_zh]:
t = wrap_text(text=txt, max_width=1000, font=font, fontsize=60) t, h = wrap_text(text=txt, max_width=1000, font=font, fontsize=60)
print(t) print(t)
task_id = "aa563149-a7ea-49c2-b39f-8c32cc225baf" task_id = "aa563149-a7ea-49c2-b39f-8c32cc225baf"

BIN
docs/wechat-01.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View File

@@ -38,7 +38,7 @@ hide_streamlit_style = """
<style>#root > div:nth-child(1) > div > div > div > div > section > div {padding-top: 0rem;}</style> <style>#root > div:nth-child(1) > div > div > div > div > section > div {padding-top: 0rem;}</style>
""" """
st.markdown(hide_streamlit_style, unsafe_allow_html=True) st.markdown(hide_streamlit_style, unsafe_allow_html=True)
st.title("MoneyPrinterTurbo") st.title(f"MoneyPrinterTurbo v{config.project_version}")
font_dir = os.path.join(root_dir, "resource", "fonts") font_dir = os.path.join(root_dir, "resource", "fonts")
song_dir = os.path.join(root_dir, "resource", "songs") song_dir = os.path.join(root_dir, "resource", "songs")

View File

@@ -58,6 +58,6 @@
"Model Name": "Model Name", "Model Name": "Model Name",
"Please Enter the LLM API Key": "Please Enter the **LLM API Key**", "Please Enter the LLM API Key": "Please Enter the **LLM API Key**",
"Please Enter the Pexels API Key": "Please Enter the **Pexels API Key**", "Please Enter the Pexels API Key": "Please Enter the **Pexels API Key**",
"Get Help": "If you need help, or have any questions, you can join discord for help: https://harryai.cc/moneyprinterturbo" "Get Help": "If you need help, or have any questions, you can join discord for help: https://harryai.cc"
} }
} }

View File

@@ -59,6 +59,6 @@
"Model Name": "Model Name", "Model Name": "Model Name",
"Please Enter the LLM API Key": "Please Enter the **LLM API Key**", "Please Enter the LLM API Key": "Please Enter the **LLM API Key**",
"Please Enter the Pexels API Key": "Please Enter the **Pexels API Key**", "Please Enter the Pexels API Key": "Please Enter the **Pexels API Key**",
"Get Help": "If you need help, or have any questions, you can join discord for help: https://harryai.cc/moneyprinterturbo" "Get Help": "If you need help, or have any questions, you can join discord for help: https://harryai.cc"
} }
} }

View File

@@ -59,6 +59,6 @@
"Model Name": "模型名称 (:blue[需要到大模型提供商的后台确认被授权的模型名称])", "Model Name": "模型名称 (:blue[需要到大模型提供商的后台确认被授权的模型名称])",
"Please Enter the LLM API Key": "请先填写大模型 **API Key**", "Please Enter the LLM API Key": "请先填写大模型 **API Key**",
"Please Enter the Pexels API Key": "请先填写 **Pexels API Key**", "Please Enter the Pexels API Key": "请先填写 **Pexels API Key**",
"Get Help": "有任何问题或建议,可以加入 **微信群** 求助或讨论https://harryai.cc/moneyprinterturbo" "Get Help": "有任何问题或建议,可以加入 **微信群** 求助或讨论https://harryai.cc"
} }
} }