mirror of
https://github.com/harry0703/MoneyPrinterTurbo.git
synced 2026-02-21 08:27:22 +08:00
Merge branch 'main' of https://github.com/harry0703/MoneyPrinterTurbo into main
This commit is contained in:
commit
90f0f560b2
20
README.md
20
README.md
@ -66,6 +66,9 @@
|
||||
- [ ] 支持更多的语音合成服务商,比如 OpenAI TTS, Azure TTS
|
||||
- [ ] 自动上传到YouTube平台
|
||||
|
||||
## 交流讨论 💬
|
||||
<img src="docs/wechat-01.jpg" width="300">
|
||||
|
||||
## 视频演示 📺
|
||||
|
||||
### 竖屏 9:16
|
||||
@ -102,8 +105,17 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## 配置要求 📦
|
||||
- 建议最低 CPU 4核或以上,内存 8G 或以上,显卡非必须
|
||||
- Windows 10 或 MacOS 11.0 以上系统
|
||||
|
||||
## 安装部署 📥
|
||||
|
||||
> 不想部署的可以直接下载安装包,解压直接使用
|
||||
- **Windows** 版本下载地址
|
||||
- 百度网盘: https://pan.baidu.com/s/1BB3SGtAFTytzFLS5t2d8Gg?pwd=5bry
|
||||
|
||||
### 前提条件
|
||||
- 尽量不要使用 **中文路径**,避免出现一些无法预料的问题
|
||||
- 请确保你的 **网络** 是正常的,VPN需要打开`全局流量`模式
|
||||
|
||||
@ -375,14 +387,6 @@ pip install Pillow==8.4.0
|
||||
|
||||
- 可以提交 [issue](https://github.com/harry0703/MoneyPrinterTurbo/issues)
|
||||
或者 [pull request](https://github.com/harry0703/MoneyPrinterTurbo/pulls)。
|
||||
- 也可以关注我的 **抖音** 或 **视频号**:`网旭哈瑞.AI`
|
||||
- 我会在上面发布一些 **使用教程** 和 **纯技术** 分享。
|
||||
- 如果有更新和优化,我也会在上面 **及时通知**。
|
||||
- 有问题也可以在上面 **留言**,我会 **尽快回复**。
|
||||
|
||||
| 抖音 | | 视频号 |
|
||||
|:---------------------------------------:|:------------:|:-------------------------------------------:|
|
||||
| <img src="docs/douyin.jpg" width="180"> | | <img src="docs/shipinghao.jpg" width="200"> |
|
||||
|
||||
## 参考项目 📚
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ listen_port = _cfg.get("listen_port", 8080)
|
||||
project_name = _cfg.get("project_name", "MoneyPrinterTurbo")
|
||||
project_description = _cfg.get("project_description",
|
||||
"<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
|
||||
|
||||
imagemagick_path = app.get("imagemagick_path", "")
|
||||
|
||||
@ -5,6 +5,8 @@ from typing import List
|
||||
from loguru import logger
|
||||
from openai import OpenAI
|
||||
from openai import AzureOpenAI
|
||||
from openai.types.chat import ChatCompletion
|
||||
|
||||
from app.config import config
|
||||
|
||||
|
||||
@ -154,7 +156,15 @@ def _generate_response(prompt: str) -> str:
|
||||
messages=[{"role": "user", "content": prompt}]
|
||||
)
|
||||
if response:
|
||||
content = response.choices[0].message.content
|
||||
if isinstance(response, ChatCompletion):
|
||||
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", "")
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import json
|
||||
import os.path
|
||||
import re
|
||||
|
||||
from faster_whisper import WhisperModel
|
||||
@ -17,8 +18,13 @@ model = None
|
||||
def create(audio_file, subtitle_file: str = ""):
|
||||
global model
|
||||
if not model:
|
||||
logger.info(f"loading model: {model_size}, device: {device}, compute_type: {compute_type}")
|
||||
model = WhisperModel(model_size_or_path=model_size,
|
||||
model_path = f"{utils.root_dir()}/models/whisper-{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,
|
||||
compute_type=compute_type)
|
||||
|
||||
|
||||
@ -124,7 +124,7 @@ def wrap_text(text, max_width, font='Arial', fontsize=60):
|
||||
|
||||
width, height = get_text_size(text)
|
||||
if width <= max_width:
|
||||
return text
|
||||
return text, height
|
||||
|
||||
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:
|
||||
_wrapped_lines_ = [line.strip() for line in _wrapped_lines_]
|
||||
result = '\n'.join(_wrapped_lines_).strip()
|
||||
height = len(_wrapped_lines_) * height
|
||||
logger.warning(f"wrapped text: {result}")
|
||||
return result
|
||||
return result, height
|
||||
|
||||
_wrapped_lines_ = []
|
||||
chars = list(text)
|
||||
@ -165,8 +166,9 @@ def wrap_text(text, max_width, font='Arial', fontsize=60):
|
||||
_txt_ = ''
|
||||
_wrapped_lines_.append(_txt_)
|
||||
result = '\n'.join(_wrapped_lines_).strip()
|
||||
height = len(_wrapped_lines_) * height
|
||||
logger.warning(f"wrapped text: {result}")
|
||||
return result
|
||||
return result, height
|
||||
|
||||
|
||||
def generate_video(video_path: str,
|
||||
@ -199,23 +201,15 @@ def generate_video(video_path: str,
|
||||
|
||||
logger.info(f"using font: {font_path}")
|
||||
|
||||
if params.subtitle_position == "top":
|
||||
position_height = video_height * 0.1
|
||||
elif params.subtitle_position == "bottom":
|
||||
position_height = video_height * 0.9
|
||||
else:
|
||||
position_height = "center"
|
||||
|
||||
def generator(txt, **kwargs):
|
||||
def create_text_clip(subtitle_item):
|
||||
phrase = subtitle_item[1]
|
||||
max_width = video_width * 0.9
|
||||
# logger.debug(f"rendering text: {txt}")
|
||||
wrapped_txt = wrap_text(txt,
|
||||
max_width=max_width,
|
||||
font=font_path,
|
||||
fontsize=params.font_size
|
||||
) # 调整max_width以适应你的视频
|
||||
|
||||
clip = TextClip(
|
||||
wrapped_txt, txt_height = wrap_text(phrase,
|
||||
max_width=max_width,
|
||||
font=font_path,
|
||||
fontsize=params.font_size
|
||||
)
|
||||
_clip = TextClip(
|
||||
wrapped_txt,
|
||||
font=font_path,
|
||||
fontsize=params.font_size,
|
||||
@ -225,15 +219,28 @@ def generate_video(video_path: str,
|
||||
stroke_width=params.stroke_width,
|
||||
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)
|
||||
audio_clip = AudioFileClip(audio_path).volumex(params.voice_volume)
|
||||
|
||||
if subtitle_path and os.path.exists(subtitle_path):
|
||||
sub = SubtitlesClip(subtitles=subtitle_path, make_textclip=generator, encoding='utf-8')
|
||||
sub_clip = sub.set_position(lambda _t: ('center', position_height))
|
||||
video_clip = CompositeVideoClip([video_clip, sub_clip])
|
||||
sub = SubtitlesClip(subtitles=subtitle_path, encoding='utf-8')
|
||||
text_clips = []
|
||||
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)
|
||||
if bgm_file:
|
||||
@ -258,7 +265,7 @@ if __name__ == "__main__":
|
||||
txt_zh = "测试长字段这是您的旅行技巧指南帮助您进行预算友好的冒险"
|
||||
font = utils.resource_dir() + "/fonts/STHeitiMedium.ttc"
|
||||
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)
|
||||
|
||||
task_id = "aa563149-a7ea-49c2-b39f-8c32cc225baf"
|
||||
|
||||
BIN
docs/wechat-01.jpg
Normal file
BIN
docs/wechat-01.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
@ -38,7 +38,7 @@ hide_streamlit_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.title("MoneyPrinterTurbo")
|
||||
st.title(f"MoneyPrinterTurbo v{config.project_version}")
|
||||
|
||||
font_dir = os.path.join(root_dir, "resource", "fonts")
|
||||
song_dir = os.path.join(root_dir, "resource", "songs")
|
||||
|
||||
@ -58,6 +58,6 @@
|
||||
"Model Name": "Model Name",
|
||||
"Please Enter the LLM API Key": "Please Enter the **LLM 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"
|
||||
}
|
||||
}
|
||||
@ -59,6 +59,6 @@
|
||||
"Model Name": "Model Name",
|
||||
"Please Enter the LLM API Key": "Please Enter the **LLM 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"
|
||||
}
|
||||
}
|
||||
@ -59,6 +59,6 @@
|
||||
"Model Name": "模型名称 (:blue[需要到大模型提供商的后台确认被授权的模型名称])",
|
||||
"Please Enter the LLM API Key": "请先填写大模型 **API Key**",
|
||||
"Please Enter the Pexels API Key": "请先填写 **Pexels API Key**",
|
||||
"Get Help": "有任何问题或建议,可以加入 **微信群** 求助或讨论:https://harryai.cc/moneyprinterturbo"
|
||||
"Get Help": "有任何问题或建议,可以加入 **微信群** 求助或讨论:https://harryai.cc"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user