Files
ViGent2/backend/app/services/assets_service.py
Kevin Wong 6c6fbae13a 更新
2026-02-03 17:12:30 +08:00

129 lines
3.4 KiB
Python

import json
import shutil
from pathlib import Path
from typing import Optional, List, Dict, Any
from loguru import logger
from app.core.config import settings
BGM_EXTENSIONS = {".wav", ".mp3", ".m4a", ".aac", ".flac", ".ogg", ".webm"}
def _style_file_path(style_type: str) -> Path:
return settings.ASSETS_DIR / "styles" / f"{style_type}.json"
def _load_style_file(style_type: str) -> List[Dict[str, Any]]:
style_path = _style_file_path(style_type)
if not style_path.exists():
return []
try:
with open(style_path, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, list):
return data
except Exception as e:
logger.error(f"Failed to load style file {style_path}: {e}")
return []
def list_styles(style_type: str) -> List[Dict[str, Any]]:
return _load_style_file(style_type)
def get_style(style_type: str, style_id: Optional[str]) -> Optional[Dict[str, Any]]:
if not style_id:
return None
for item in _load_style_file(style_type):
if item.get("id") == style_id:
return item
return None
def get_default_style(style_type: str) -> Optional[Dict[str, Any]]:
styles = _load_style_file(style_type)
if not styles:
return None
for item in styles:
if item.get("is_default"):
return item
return styles[0]
def list_bgm() -> List[Dict[str, Any]]:
bgm_root = settings.ASSETS_DIR / "bgm"
if not bgm_root.exists():
return []
items: List[Dict[str, Any]] = []
for path in bgm_root.rglob("*"):
if not path.is_file():
continue
if path.suffix.lower() not in BGM_EXTENSIONS:
continue
rel = path.relative_to(bgm_root).as_posix()
items.append({
"id": rel,
"name": path.stem,
"ext": path.suffix.lower().lstrip(".")
})
items.sort(key=lambda x: x.get("name", ""))
return items
def resolve_bgm_path(bgm_id: str) -> Optional[Path]:
if not bgm_id:
return None
bgm_root = settings.ASSETS_DIR / "bgm"
candidate = (bgm_root / bgm_id).resolve()
try:
candidate.relative_to(bgm_root.resolve())
except ValueError:
return None
if candidate.exists() and candidate.is_file():
return candidate
return None
def prepare_style_for_remotion(
style: Optional[Dict[str, Any]],
temp_dir: Path,
prefix: str
) -> Optional[Dict[str, Any]]:
if not style:
return None
prepared = dict(style)
font_file = prepared.get("font_file")
if not font_file:
return prepared
source_font = (settings.ASSETS_DIR / "fonts" / font_file).resolve()
try:
source_font.relative_to((settings.ASSETS_DIR / "fonts").resolve())
except ValueError:
logger.warning(f"Font path outside assets: {font_file}")
return prepared
if not source_font.exists():
logger.warning(f"Font file missing: {source_font}")
return prepared
temp_dir.mkdir(parents=True, exist_ok=True)
ext = source_font.suffix.lower()
target_name = f"{prefix}{ext}"
target_path = temp_dir / target_name
try:
shutil.copy(source_font, target_path)
prepared["font_file"] = target_name
if not prepared.get("font_family"):
prepared["font_family"] = prefix
except Exception as e:
logger.warning(f"Failed to copy font {source_font} -> {target_path}: {e}")
return prepared