optimize UI and user experience

This commit is contained in:
harry
2024-04-09 19:46:56 +08:00
parent 68c7ea13c7
commit 0b07202d42
9 changed files with 167 additions and 81 deletions

View File

@@ -1,6 +1,6 @@
import os
import socket
import tomli
import toml
from loguru import logger
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
@@ -16,17 +16,17 @@ if not os.path.isfile(config_file):
logger.info(f"load config from file: {config_file}")
try:
with open(config_file, mode="rb") as fp:
_cfg = tomli.load(fp)
_cfg = toml.load(config_file)
except Exception as e:
logger.warning(f"load config failed: {str(e)}, try to load as utf-8-sig")
with open(config_file, mode="r", encoding='utf-8-sig') as fp:
_cfg_content = fp.read()
_cfg = tomli.loads(_cfg_content)
_cfg = toml.loads(_cfg_content)
app = _cfg.get("app", {})
whisper = _cfg.get("whisper", {})
pexels = _cfg.get("pexels", {})
ui = _cfg.get("ui", {})
hostname = socket.gethostname()
@@ -47,9 +47,18 @@ ffmpeg_path = app.get("ffmpeg_path", "")
if ffmpeg_path and os.path.isfile(ffmpeg_path):
os.environ["IMAGEIO_FFMPEG_EXE"] = ffmpeg_path
# __cfg = {
# "hostname": hostname,
# "listen_host": listen_host,
# "listen_port": listen_port,
# }
# logger.info(__cfg)
def save_config():
with open(config_file, "w", encoding="utf-8") as f:
_cfg["app"] = app
_cfg["whisper"] = whisper
_cfg["pexels"] = pexels
f.write(toml.dumps(_cfg))

View File

@@ -11,13 +11,14 @@ from app.models.schema import VideoAspect, VideoConcatMode, MaterialInfo
from app.utils import utils
requested_count = 0
pexels_api_keys = config.app.get("pexels_api_keys")
if not pexels_api_keys:
raise ValueError(
f"\n\n##### pexels_api_keys is not set #####\n\nPlease set it in the config.toml file: {config.config_file}\n\n{utils.to_json(config.app)}")
def round_robin_api_key():
pexels_api_keys = config.app.get("pexels_api_keys")
if not pexels_api_keys:
raise ValueError(
f"\n\n##### pexels_api_keys is not set #####\n\nPlease set it in the config.toml file: {config.config_file}\n\n{utils.to_json(config.app)}")
# if only one key is provided, return it
if isinstance(pexels_api_keys, str):
return pexels_api_keys

View File

@@ -1,3 +1,4 @@
import locale
import os
import platform
import threading
@@ -174,3 +175,25 @@ def split_string_by_punctuations(s):
def md5(text):
import hashlib
return hashlib.md5(text.encode('utf-8')).hexdigest()
def get_system_locale():
try:
loc = locale.getdefaultlocale()
# zh_CN, zh_TW return zh
# en_US, en_GB return en
language_code = loc[0].split("_")[0]
return language_code
except Exception as e:
return "en"
def load_locales(i18n_dir):
_locales = {}
for root, dirs, files in os.walk(i18n_dir):
for file in files:
if file.endswith(".json"):
lang = file.split(".")[0]
with open(os.path.join(root, file), "r", encoding="utf-8") as f:
_locales[lang] = json.loads(f.read())
return _locales

View File

@@ -1,4 +1,2 @@
set CURRENT_DIR=%CD%
set PYTHONPATH=%CURRENT_DIR%
rem set HF_ENDPOINT=https://hf-mirror.com
streamlit run .\webui\Main.py
streamlit run .\webui\Main.py --browser.gatherUsageStats=False --server.enableCORS=True

View File

@@ -1,7 +1,3 @@
CURRENT_DIR=$(pwd)
echo "***** Current directory: $CURRENT_DIR *****"
export PYTHONPATH="${CURRENT_DIR}:$PYTHONPATH"
# If you could not download the model from the official site, you can use the mirror site.
# Just remove the comment of the following line .
# 如果你无法从官方网站下载模型,你可以使用镜像网站。

View File

@@ -9,15 +9,12 @@ if root_dir not in sys.path:
print(sys.path)
print("")
import json
import locale
import streamlit as st
import os
from uuid import uuid4
import platform
import streamlit.components.v1 as components
import toml
from loguru import logger
st.set_page_config(page_title="MoneyPrinterTurbo",
@@ -35,6 +32,7 @@ st.set_page_config(page_title="MoneyPrinterTurbo",
from app.models.schema import VideoParams, VideoAspect, VideoConcatMode
from app.services import task as tm, llm, voice
from app.utils import utils
from app.config import config
hide_streamlit_style = """
<style>#root > div:nth-child(1) > div > div > div > div > section > div {padding-top: 0rem;}</style>
@@ -46,33 +44,7 @@ font_dir = os.path.join(root_dir, "resource", "fonts")
song_dir = os.path.join(root_dir, "resource", "songs")
i18n_dir = os.path.join(root_dir, "webui", "i18n")
config_file = os.path.join(root_dir, "webui", ".streamlit", "webui.toml")
def load_config() -> dict:
try:
return toml.load(config_file)
except Exception as e:
return {}
cfg = load_config()
def save_config():
with open(config_file, "w", encoding="utf-8") as f:
f.write(toml.dumps(cfg))
def get_system_locale():
try:
loc = locale.getdefaultlocale()
# zh_CN, zh_TW return zh
# en_US, en_GB return en
language_code = loc[0].split("_")[0]
return language_code
except Exception as e:
return "en"
system_locale = utils.get_system_locale()
if 'video_subject' not in st.session_state:
st.session_state['video_subject'] = ''
@@ -81,7 +53,7 @@ if 'video_script' not in st.session_state:
if 'video_terms' not in st.session_state:
st.session_state['video_terms'] = ''
if 'ui_language' not in st.session_state:
st.session_state['ui_language'] = cfg.get("ui_language", get_system_locale())
st.session_state['ui_language'] = config.ui.get("language", system_locale)
def get_all_fonts():
@@ -163,19 +135,7 @@ def init_log():
init_log()
def load_locales():
locales = {}
for root, dirs, files in os.walk(i18n_dir):
for file in files:
if file.endswith(".json"):
lang = file.split(".")[0]
with open(os.path.join(root, file), "r", encoding="utf-8") as f:
locales[lang] = json.loads(f.read())
return locales
locales = load_locales()
locales = utils.load_locales(i18n_dir)
def tr(key):
@@ -183,20 +143,70 @@ def tr(key):
return loc.get("Translation", {}).get(key, key)
display_languages = []
selected_index = 0
for i, code in enumerate(locales.keys()):
display_languages.append(f"{code} - {locales[code].get('Language')}")
if code == st.session_state['ui_language']:
selected_index = i
with st.expander(tr("Basic Settings"), expanded=True):
config_panels = st.columns(3)
left_config_panel = config_panels[0]
middle_config_panel = config_panels[1]
right_config_panel = config_panels[2]
with left_config_panel:
display_languages = []
selected_index = 0
for i, code in enumerate(locales.keys()):
display_languages.append(f"{code} - {locales[code].get('Language')}")
if code == st.session_state['ui_language']:
selected_index = i
selected_language = st.selectbox("Language", options=display_languages, label_visibility='collapsed',
index=selected_index)
if selected_language:
code = selected_language.split(" - ")[0].strip()
st.session_state['ui_language'] = code
cfg['ui_language'] = code
save_config()
selected_language = st.selectbox(tr("Language"), options=display_languages,
index=selected_index)
if selected_language:
code = selected_language.split(" - ")[0].strip()
st.session_state['ui_language'] = code
config.ui['language'] = code
config.save_config()
with middle_config_panel:
# openai
# moonshot (月之暗面)
# oneapi
# g4f
# azure
# qwen (通义千问)
# gemini
# ollama
llm_providers = ['OpenAI', 'Moonshot', 'Azure', 'Qwen', 'Gemini', 'Ollama', 'G4f', 'OneAPI']
saved_llm_provider = config.app.get("llm_provider", "OpenAI").lower()
saved_llm_provider_index = 0
for i, provider in enumerate(llm_providers):
if provider.lower() == saved_llm_provider:
saved_llm_provider_index = i
break
llm_provider = st.selectbox(tr("LLM Provider"), options=llm_providers, index=saved_llm_provider_index)
llm_provider = llm_provider.lower()
config.app["llm_provider"] = llm_provider
st.write(f"**{tr('LLM Provider')}:** {llm_provider}")
llm_api_key = config.app.get(f"{llm_provider}_api_key", "")
llm_base_url = config.app.get(f"{llm_provider}_base_url", "")
llm_model_name = config.app.get(f"{llm_provider}_model_name", "")
st_llm_api_key = st.text_input(tr("API Key"), value=llm_api_key, type="password")
st_llm_base_url = st.text_input(tr("Base Url"), value=llm_base_url)
st_llm_model_name = st.text_input(tr("Model Name"), value=llm_model_name)
if st_llm_api_key:
config.app[f"{llm_provider}_api_key"] = st_llm_api_key
if st_llm_base_url:
config.app[f"{llm_provider}_base_url"] = st_llm_base_url
if st_llm_model_name:
config.app[f"{llm_provider}_model_name"] = st_llm_model_name
config.save_config()
with right_config_panel:
pexels_api_key = config.app.get("pexels_api_keys", "")
pexels_api_key = st.text_input(tr("Pexels API Key"), value=pexels_api_key, type="password")
if pexels_api_key:
config.app["pexels_api_keys"] = pexels_api_key
config.save_config()
panel = st.columns(3)
left_panel = panel[0]
@@ -286,7 +296,7 @@ with middle_panel:
replace("Male", tr("Male")).
replace("Neural", "") for
voice in voices}
saved_voice_name = cfg.get("voice_name", "")
saved_voice_name = config.ui.get("voice_name", "")
saved_voice_name_index = 0
if saved_voice_name in friendly_names:
saved_voice_name_index = list(friendly_names.keys()).index(saved_voice_name)
@@ -302,8 +312,8 @@ with middle_panel:
voice_name = list(friendly_names.keys())[list(friendly_names.values()).index(selected_friendly_name)]
params.voice_name = voice_name
cfg['voice_name'] = voice_name
save_config()
config.ui['voice_name'] = voice_name
config.save_config()
params.voice_volume = st.selectbox(tr("Speech Volume"),
options=[0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 3.0, 4.0, 5.0], index=2)
@@ -334,7 +344,13 @@ with right_panel:
st.write(tr("Subtitle Settings"))
params.subtitle_enabled = st.checkbox(tr("Enable Subtitles"), value=True)
font_names = get_all_fonts()
params.font_name = st.selectbox(tr("Font"), font_names)
saved_font_name = config.ui.get("font_name", "")
saved_font_name_index = 0
if saved_font_name in font_names:
saved_font_name_index = font_names.index(saved_font_name)
params.font_name = st.selectbox(tr("Font"), font_names, index=saved_font_name_index)
config.ui['font_name'] = params.font_name
config.save_config()
subtitle_positions = [
(tr("Top"), "top"),
@@ -350,9 +366,14 @@ with right_panel:
font_cols = st.columns([0.3, 0.7])
with font_cols[0]:
params.text_fore_color = st.color_picker(tr("Font Color"), "#FFFFFF")
saved_text_fore_color = config.ui.get("text_fore_color", "#FFFFFF")
params.text_fore_color = st.color_picker(tr("Font Color"), saved_text_fore_color)
config.ui['text_fore_color'] = params.text_fore_color
with font_cols[1]:
params.font_size = st.slider(tr("Font Size"), 30, 100, 60)
saved_font_size = config.ui.get("font_size", 60)
params.font_size = st.slider(tr("Font Size"), 30, 100, saved_font_size)
config.ui['font_size'] = params.font_size
stroke_cols = st.columns([0.3, 0.7])
with stroke_cols[0]:
@@ -362,12 +383,23 @@ with right_panel:
start_button = st.button(tr("Generate Video"), use_container_width=True, type="primary")
if start_button:
config.save_config()
task_id = str(uuid4())
if not params.video_subject and not params.video_script:
st.error(tr("Video Script and Subject Cannot Both Be Empty"))
scroll_to_bottom()
st.stop()
if not config.app.get(f"{llm_provider}_api_key", ""):
st.error(tr("Please Enter the LLM API Key"))
scroll_to_bottom()
st.stop()
if not config.app.get("pexels_api_keys", ""):
st.error(tr("Please Enter the Pexels API Key"))
scroll_to_bottom()
st.stop()
log_container = st.empty()
log_records = []

View File

@@ -48,6 +48,15 @@
"Generating Video": "Video wird erstellt, bitte warten...",
"Start Generating Video": "Beginne mit der Generierung",
"Video Generation Completed": "Video erfolgreich generiert",
"You can download the generated video from the following links": "Sie können das generierte Video über die folgenden Links herunterladen"
"You can download the generated video from the following links": "Sie können das generierte Video über die folgenden Links herunterladen",
"Basic Settings": "**Grunde Instellungen**",
"Pexels API Key": "Pexels API Key (:red[Required] [Get API Key](https://www.pexels.com/api/))",
"Language": "Language",
"LLM Provider": "LLM Provider",
"API Key": "API Key (:red[Required])",
"Base Url": "Base Url",
"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**"
}
}

View File

@@ -48,6 +48,15 @@
"Generating Video": "Generating video, please wait...",
"Start Generating Video": "Start Generating Video",
"Video Generation Completed": "Video Generation Completed",
"You can download the generated video from the following links": "You can download the generated video from the following links"
"You can download the generated video from the following links": "You can download the generated video from the following links",
"Pexels API Key": "Pexels API Key (:red[Required] [Get API Key](https://www.pexels.com/api/))",
"Basic Settings": "**Basic Settings** (:blue[Click to expand])",
"Language": "Language",
"LLM Provider": "LLM Provider",
"API Key": "API Key (:red[Required])",
"Base Url": "Base Url",
"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**"
}
}

View File

@@ -48,6 +48,15 @@
"Generating Video": "正在生成视频,请稍候...",
"Start Generating Video": "开始生成视频",
"Video Generation Completed": "视频生成完成",
"You can download the generated video from the following links": "你可以从以下链接下载生成的视频"
"You can download the generated video from the following links": "你可以从以下链接下载生成的视频",
"Basic Settings": "**基础设置** (:blue[点击展开])",
"Language": "界面语言",
"Pexels API Key": "Pexels API Key (:red[必填] [点击获取](https://www.pexels.com/api/))",
"LLM Provider": "大模型提供商",
"API Key": "API Key (:red[必填,需要到大模型提供商的后台申请])",
"Base Url": "Base Url (可选)",
"Model Name": "模型名称 (:blue[需要到大模型提供商的后台确认被授权的模型名称])",
"Please Enter the LLM API Key": "请先填写大模型 **API Key**",
"Please Enter the Pexels API Key": "请先填写 **Pexels API Key**"
}
}