项目重命名: ReelForge => Pixelle-Video

This commit is contained in:
puke
2025-10-31 10:23:38 +08:00
parent dfdba73b74
commit 136514e466
60 changed files with 384 additions and 383 deletions

View File

@@ -0,0 +1,38 @@
"""
Pixelle-Video Configuration System
Unified configuration management with Pydantic validation.
Usage:
from pixelle_video.config import config_manager
# Access config (type-safe)
api_key = config_manager.config.llm.api_key
# Update config
config_manager.update({"llm": {"api_key": "xxx"}})
config_manager.save()
# Validate
if config_manager.validate():
print("Config is valid!")
"""
from .schema import PixelleVideoConfig, LLMConfig, ComfyUIConfig, TTSSubConfig, ImageSubConfig
from .manager import ConfigManager
from .loader import load_config_dict, save_config_dict
# Global singleton instance
config_manager = ConfigManager()
__all__ = [
"PixelleVideoConfig",
"LLMConfig",
"ComfyUIConfig",
"TTSSubConfig",
"ImageSubConfig",
"ConfigManager",
"config_manager",
"load_config_dict",
"save_config_dict",
]

View File

@@ -0,0 +1,53 @@
"""
Configuration loader - Pure YAML
Handles loading and saving configuration from/to YAML files.
"""
from pathlib import Path
import yaml
from loguru import logger
def load_config_dict(config_path: str = "config.yaml") -> dict:
"""
Load configuration from YAML file
Args:
config_path: Path to config file
Returns:
Configuration dictionary
"""
config_file = Path(config_path)
if not config_file.exists():
logger.warning(f"Config file not found: {config_path}")
logger.info("Using default configuration")
return {}
try:
with open(config_file, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f) or {}
logger.info(f"Configuration loaded from {config_path}")
return data
except Exception as e:
logger.error(f"Failed to load config: {e}")
return {}
def save_config_dict(config: dict, config_path: str = "config.yaml"):
"""
Save configuration to YAML file
Args:
config: Configuration dictionary
config_path: Path to config file
"""
try:
with open(config_path, 'w', encoding='utf-8') as f:
yaml.dump(config, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
logger.info(f"Configuration saved to {config_path}")
except Exception as e:
logger.error(f"Failed to save config: {e}")
raise

View File

@@ -0,0 +1,124 @@
"""
Configuration Manager - Singleton pattern
Provides unified access to configuration with automatic validation.
"""
from pathlib import Path
from typing import Any, Optional
from loguru import logger
from .schema import PixelleVideoConfig
from .loader import load_config_dict, save_config_dict
class ConfigManager:
"""
Configuration Manager (Singleton)
Provides unified access to configuration with automatic validation.
"""
_instance: Optional['ConfigManager'] = None
def __new__(cls, config_path: str = "config.yaml"):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, config_path: str = "config.yaml"):
# Only initialize once
if hasattr(self, '_initialized'):
return
self.config_path = Path(config_path)
self.config: PixelleVideoConfig = self._load()
self._initialized = True
def _load(self) -> PixelleVideoConfig:
"""Load configuration from file"""
data = load_config_dict(str(self.config_path))
return PixelleVideoConfig(**data)
def reload(self):
"""Reload configuration from file"""
self.config = self._load()
logger.info("Configuration reloaded")
def save(self):
"""Save current configuration to file"""
save_config_dict(self.config.to_dict(), str(self.config_path))
def update(self, updates: dict):
"""
Update configuration with new values
Args:
updates: Dictionary of updates (e.g., {"llm": {"api_key": "xxx"}})
"""
current = self.config.to_dict()
# Deep merge
def deep_merge(base: dict, updates: dict) -> dict:
for key, value in updates.items():
if key in base and isinstance(base[key], dict) and isinstance(value, dict):
deep_merge(base[key], value)
else:
base[key] = value
return base
merged = deep_merge(current, updates)
self.config = PixelleVideoConfig(**merged)
def get(self, key: str, default: Any = None) -> Any:
"""Dict-like access (for backward compatibility)"""
return self.config.to_dict().get(key, default)
def validate(self) -> bool:
"""Validate configuration completeness"""
return self.config.validate_required()
def get_llm_config(self) -> dict:
"""Get LLM configuration as dict"""
return {
"api_key": self.config.llm.api_key,
"base_url": self.config.llm.base_url,
"model": self.config.llm.model,
}
def set_llm_config(self, api_key: str, base_url: str, model: str):
"""Set LLM configuration"""
self.update({
"llm": {
"api_key": api_key,
"base_url": base_url,
"model": model,
}
})
def get_comfyui_config(self) -> dict:
"""Get ComfyUI configuration as dict"""
return {
"comfyui_url": self.config.comfyui.comfyui_url,
"runninghub_api_key": self.config.comfyui.runninghub_api_key,
"tts": {
"default_workflow": self.config.comfyui.tts.default_workflow,
},
"image": {
"default_workflow": self.config.comfyui.image.default_workflow,
"prompt_prefix": self.config.comfyui.image.prompt_prefix,
}
}
def set_comfyui_config(
self,
comfyui_url: Optional[str] = None,
runninghub_api_key: Optional[str] = None
):
"""Set ComfyUI global configuration"""
updates = {}
if comfyui_url is not None:
updates["comfyui_url"] = comfyui_url
if runninghub_api_key is not None:
updates["runninghub_api_key"] = runninghub_api_key
if updates:
self.update({"comfyui": updates})

View File

@@ -0,0 +1,59 @@
"""
Configuration schema with Pydantic models
Single source of truth for all configuration defaults and validation.
"""
from pydantic import BaseModel, Field
class LLMConfig(BaseModel):
"""LLM configuration"""
api_key: str = Field(default="", description="LLM API Key")
base_url: str = Field(default="", description="LLM API Base URL")
model: str = Field(default="", description="LLM Model Name")
class TTSSubConfig(BaseModel):
"""TTS-specific configuration (under comfyui.tts)"""
default_workflow: str = Field(default=None, description="Default TTS workflow (required, no fallback)")
class ImageSubConfig(BaseModel):
"""Image-specific configuration (under comfyui.image)"""
default_workflow: str = Field(default=None, description="Default image workflow (required, no fallback)")
prompt_prefix: str = Field(
default="Pure white background, minimalist illustration, matchstick figure style, black and white line drawing, simple clean lines",
description="Prompt prefix for all image generation"
)
class ComfyUIConfig(BaseModel):
"""ComfyUI configuration (includes global settings and service-specific configs)"""
comfyui_url: str = Field(default="http://127.0.0.1:8188", description="ComfyUI Server URL")
runninghub_api_key: str = Field(default="", description="RunningHub API Key (optional)")
tts: TTSSubConfig = Field(default_factory=TTSSubConfig, description="TTS-specific configuration")
image: ImageSubConfig = Field(default_factory=ImageSubConfig, description="Image-specific configuration")
class PixelleVideoConfig(BaseModel):
"""Pixelle-Video main configuration"""
project_name: str = Field(default="Pixelle-Video", description="Project name")
llm: LLMConfig = Field(default_factory=LLMConfig)
comfyui: ComfyUIConfig = Field(default_factory=ComfyUIConfig)
def is_llm_configured(self) -> bool:
"""Check if LLM is properly configured"""
return bool(
self.llm.api_key and self.llm.api_key.strip() and
self.llm.base_url and self.llm.base_url.strip() and
self.llm.model and self.llm.model.strip()
)
def validate_required(self) -> bool:
"""Validate required configuration"""
return self.is_llm_configured()
def to_dict(self) -> dict:
"""Convert to dictionary (for backward compatibility)"""
return self.model_dump()