路径跨平台兼容优化 - 修改相对路径,兼容Windows/Linux/Mac
This commit is contained in:
16
config.py
16
config.py
@@ -123,14 +123,21 @@ DB_CONNECTION_STRING = os.getenv("DB_CONNECTION_STRING", f"sqlite:///{BASE_DIR}/
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
# 优先检测系统字体,防止乱码
|
# 优先检测系统字体,防止乱码
|
||||||
SYSTEM_FONTS = [
|
SYSTEM_FONTS = [
|
||||||
str(FONTS_DIR / "SmileySans-Oblique.otf"),
|
# 项目内置字体 (跨平台通用,优先使用)
|
||||||
|
str(FONTS_DIR / "NotoSansSC-Regular.otf"),
|
||||||
str(FONTS_DIR / "HarmonyOS-Sans-SC-Regular.ttf"),
|
str(FONTS_DIR / "HarmonyOS-Sans-SC-Regular.ttf"),
|
||||||
str(FONTS_DIR / "HarmonyOS-Sans-SC-Bold.ttf"),
|
str(FONTS_DIR / "HarmonyOS-Sans-SC-Bold.ttf"),
|
||||||
str(FONTS_DIR / "NotoSansSC-Regular.otf"),
|
|
||||||
str(FONTS_DIR / "NotoSansSC-Bold.otf"),
|
str(FONTS_DIR / "NotoSansSC-Bold.otf"),
|
||||||
|
str(FONTS_DIR / "SmileySans-Oblique.otf"),
|
||||||
|
# Linux 系统字体
|
||||||
|
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
|
||||||
|
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
||||||
|
# macOS 系统字体
|
||||||
"/System/Library/Fonts/PingFang.ttc",
|
"/System/Library/Fonts/PingFang.ttc",
|
||||||
"/System/Library/Fonts/STHeiti Medium.ttc",
|
"/System/Library/Fonts/STHeiti Medium.ttc",
|
||||||
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
|
# Windows 系统字体
|
||||||
|
"C:/Windows/Fonts/msyh.ttc",
|
||||||
|
"C:/Windows/Fonts/simhei.ttf",
|
||||||
]
|
]
|
||||||
|
|
||||||
DEFAULT_FONT = str(FONTS_DIR / "NotoSansSC-Regular.otf")
|
DEFAULT_FONT = str(FONTS_DIR / "NotoSansSC-Regular.otf")
|
||||||
@@ -141,7 +148,8 @@ def pick_font():
|
|||||||
for f in SYSTEM_FONTS:
|
for f in SYSTEM_FONTS:
|
||||||
if os.path.exists(f) and os.path.getsize(f) > 1000:
|
if os.path.exists(f) and os.path.getsize(f) > 1000:
|
||||||
return f
|
return f
|
||||||
return "/System/Library/Fonts/PingFang.ttc"
|
# 极端情况回退到 Arial (所有平台都有)
|
||||||
|
return "Arial"
|
||||||
|
|
||||||
DEFAULT_FONT = pick_font()
|
DEFAULT_FONT = pick_font()
|
||||||
DEFAULT_FONT_BOLD = DEFAULT_FONT
|
DEFAULT_FONT_BOLD = DEFAULT_FONT
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ def get_demo_data() -> tuple:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 原始图片路径
|
# 原始图片路径
|
||||||
base_image_dir = Path("/Volumes/Tony/video-flow/素材/发夹/原始稿")
|
base_image_dir = config.BASE_DIR / "素材" / "发夹" / "原始稿"
|
||||||
original_images = [
|
original_images = [
|
||||||
str(base_image_dir / "主图1.png"),
|
str(base_image_dir / "主图1.png"),
|
||||||
str(base_image_dir / "主图2.png"),
|
str(base_image_dir / "主图2.png"),
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ class VideoComposer:
|
|||||||
style=style if isinstance(style, str) else "subtitle",
|
style=style if isinstance(style, str) else "subtitle",
|
||||||
custom_style={
|
custom_style={
|
||||||
**(custom_style or {}),
|
**(custom_style or {}),
|
||||||
"font_name": "/System/Library/Fonts/PingFang.ttc",
|
"font_name": config.DEFAULT_FONT,
|
||||||
},
|
},
|
||||||
cache=False
|
cache=False
|
||||||
)
|
)
|
||||||
@@ -156,7 +156,7 @@ class VideoComposer:
|
|||||||
text=text,
|
text=text,
|
||||||
style=style if isinstance(style, str) else "subtitle",
|
style=style if isinstance(style, str) else "subtitle",
|
||||||
custom_style={
|
custom_style={
|
||||||
"font_name": "/System/Library/Fonts/PingFang.ttc",
|
"font_name": config.DEFAULT_FONT,
|
||||||
},
|
},
|
||||||
cache=False
|
cache=False
|
||||||
)
|
)
|
||||||
@@ -662,7 +662,7 @@ def quick_compose(
|
|||||||
|
|
||||||
def example_hairclip_video():
|
def example_hairclip_video():
|
||||||
"""示例:发夹商品视频合成"""
|
"""示例:发夹商品视频合成"""
|
||||||
素材目录 = Path("/Volumes/Tony/video-flow/素材/发夹/合成图拆分镜")
|
素材目录 = config.BASE_DIR / "素材" / "发夹" / "合成图拆分镜"
|
||||||
|
|
||||||
video_paths = [
|
video_paths = [
|
||||||
str(素材目录 / "视频-分镜1.mp4"),
|
str(素材目录 / "视频-分镜1.mp4"),
|
||||||
@@ -706,7 +706,7 @@ def example_hairclip_video():
|
|||||||
output = quick_compose(
|
output = quick_compose(
|
||||||
video_folder=str(素材目录),
|
video_folder=str(素材目录),
|
||||||
script=script,
|
script=script,
|
||||||
output_path="/Volumes/Tony/video-flow/output/发夹_合成视频.mp4",
|
output_path=str(config.OUTPUT_DIR / "发夹_合成视频.mp4"),
|
||||||
voice_type="sweet_female"
|
voice_type="sweet_female"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ def generate_voiceover_volcengine_ws(
|
|||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
使用火山 WebSocket Binary Demo 生成 TTS 音频
|
使用火山 WebSocket Binary Demo 生成 TTS 音频
|
||||||
依赖目录:/Volumes/Tony/video-flow/volcengine_binary_demo/.venv/bin/python
|
依赖目录:{PROJECT_ROOT}/volcengine_binary_demo/.venv/bin/python
|
||||||
"""
|
"""
|
||||||
if not text or not text.strip():
|
if not text or not text.strip():
|
||||||
logger.warning("Empty text provided for TTS (ws)")
|
logger.warning("Empty text provided for TTS (ws)")
|
||||||
@@ -481,8 +481,10 @@ def generate_voiceover_volcengine_ws(
|
|||||||
|
|
||||||
voice_id = VOLC_TTS_VOICES.get(voice_type, voice_type)
|
voice_id = VOLC_TTS_VOICES.get(voice_type, voice_type)
|
||||||
|
|
||||||
venv_python = Path("/Volumes/Tony/video-flow/volcengine_binary_demo/.venv/bin/python")
|
# 跨平台路径:使用项目根目录相对路径
|
||||||
demo_script = Path("/Volumes/Tony/video-flow/volcengine_binary_demo/examples/volcengine/binary.py")
|
volc_demo_dir = config.BASE_DIR / "volcengine_binary_demo"
|
||||||
|
venv_python = volc_demo_dir / ".venv" / "bin" / "python"
|
||||||
|
demo_script = volc_demo_dir / "examples" / "volcengine" / "binary.py"
|
||||||
|
|
||||||
if not venv_python.exists() or not demo_script.exists():
|
if not venv_python.exists() or not demo_script.exists():
|
||||||
logger.error("Volcengine WS demo or venv not found. Please install under volcengine_binary_demo/.venv")
|
logger.error("Volcengine WS demo or venv not found. Please install under volcengine_binary_demo/.venv")
|
||||||
@@ -505,7 +507,7 @@ def generate_voiceover_volcengine_ws(
|
|||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
cmd,
|
cmd,
|
||||||
cwd="/Volumes/Tony/video-flow/volcengine_binary_demo",
|
cwd=str(volc_demo_dir),
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
@@ -515,7 +517,7 @@ def generate_voiceover_volcengine_ws(
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
# demo 保存在 cwd 下 voice_type.mp3
|
# demo 保存在 cwd 下 voice_type.mp3
|
||||||
demo_out = Path("/Volumes/Tony/video-flow/volcengine_binary_demo") / f"{voice_id}.mp3"
|
demo_out = volc_demo_dir / f"{voice_id}.mp3"
|
||||||
if not demo_out.exists():
|
if not demo_out.exists():
|
||||||
logger.error("Volc WS TTS output not found")
|
logger.error("Volc WS TTS output not found")
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -18,20 +18,25 @@ logger = logging.getLogger(__name__)
|
|||||||
FFMPEG_PATH = str(config.BASE_DIR / "bin" / "ffmpeg") if (config.BASE_DIR / "bin" / "ffmpeg").exists() else "ffmpeg"
|
FFMPEG_PATH = str(config.BASE_DIR / "bin" / "ffmpeg") if (config.BASE_DIR / "bin" / "ffmpeg").exists() else "ffmpeg"
|
||||||
FFPROBE_PATH = str(config.BASE_DIR / "bin" / "ffprobe") if (config.BASE_DIR / "bin" / "ffprobe").exists() else "ffprobe"
|
FFPROBE_PATH = str(config.BASE_DIR / "bin" / "ffprobe") if (config.BASE_DIR / "bin" / "ffprobe").exists() else "ffprobe"
|
||||||
|
|
||||||
# 字体路径优先使用项目自带中文字体,其次使用 Linux 系统字体,最后再回退到 macOS 路径
|
# 字体路径:优先使用项目内置字体,然后按平台回退到系统字体
|
||||||
DEFAULT_FONT_PATHS = [
|
DEFAULT_FONT_PATHS = [
|
||||||
# 优先使用 Linux 系统级中文字体 (服务器环境最稳健)
|
# 优先使用项目内置字体 (跨平台通用)
|
||||||
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
|
str(config.FONTS_DIR / "NotoSansSC-Regular.otf"),
|
||||||
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
|
||||||
|
|
||||||
# 项目内字体 (注意:需确保文件不是 LFS 指针)
|
|
||||||
str(config.FONTS_DIR / "HarmonyOS-Sans-SC-Regular.ttf"),
|
str(config.FONTS_DIR / "HarmonyOS-Sans-SC-Regular.ttf"),
|
||||||
str(config.FONTS_DIR / "AlibabaPuHuiTi-Regular.ttf"),
|
str(config.FONTS_DIR / "AlibabaPuHuiTi-Regular.ttf"),
|
||||||
|
|
||||||
# macOS 字体(仅本地调试生效)
|
# Linux 系统字体
|
||||||
|
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
|
||||||
|
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
||||||
|
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
|
||||||
|
|
||||||
|
# macOS 系统字体
|
||||||
"/System/Library/Fonts/PingFang.ttc",
|
"/System/Library/Fonts/PingFang.ttc",
|
||||||
"/System/Library/Fonts/STHeiti Medium.ttc",
|
"/System/Library/Fonts/STHeiti Medium.ttc",
|
||||||
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
|
|
||||||
|
# Windows 系统字体
|
||||||
|
"C:/Windows/Fonts/msyh.ttc",
|
||||||
|
"C:/Windows/Fonts/simhei.ttf",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -495,11 +500,7 @@ def add_multiple_subtitles(
|
|||||||
return output_path
|
return output_path
|
||||||
|
|
||||||
default_style = default_style or {}
|
default_style = default_style or {}
|
||||||
# 强制使用完整字体(先用项目内 NotoSansSC,如果不存在则回退 Droid)
|
# 使用统一的字体查找逻辑(跨平台兼容)
|
||||||
font = "/root/video-flow/assets/fonts/NotoSansSC-Regular.otf"
|
|
||||||
if not (os.path.exists(font) and os.path.getsize(font) > 1024 * 100): # 至少100KB以上认为有效
|
|
||||||
font = "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf"
|
|
||||||
if not (os.path.exists(font) and os.path.getsize(font) > 1024 * 100):
|
|
||||||
font = _get_font_path()
|
font = _get_font_path()
|
||||||
|
|
||||||
print(f"[SubDebug] Using font for subtitles: {font}", flush=True)
|
print(f"[SubDebug] Using font for subtitles: {font}", flush=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user