Files
audio2text/app/config.py
zikai 00e2a95fb7 Initial commit: audio2text 双语字幕生成服务
- 音频/视频转双语(英/中)SRT 字幕,Docker 容器化,CPU 开发/GPU 生产同一份代码
- faster-whisper ASR(词级时间戳) + 断句时间戳重算 + NLLB 翻译(模型不共驻)
- 分片上传(断点续传) + SQLite 持久化 + 主页/历史/日志页面
- 历史页文件名搜索;缓存定时清理(默认保留7天,可配置)
- 双 Dockerfile(cpu/gpu) + setup/start/stop 脚本
2026-07-06 06:54:19 +00:00

137 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""运行时配置:所有参数从 config.yaml 读取,对齐 server/config.py 的风格。
CPU dev / GPU prod 仅靠 device / model / compute_type 三项切换,代码完全不变。
"""
from __future__ import annotations
import os
from functools import lru_cache
from pathlib import Path
import yaml
from pydantic import BaseModel, Field, field_validator
PROJECT_ROOT = Path(__file__).resolve().parent.parent
DEFAULT_CONFIG_PATH = PROJECT_ROOT / "config.yaml"
class ServerConfig(BaseModel):
host: str = "0.0.0.0"
port: int = 8000
workers: int = 1
class StorageConfig(BaseModel):
upload_dir: str = "/data/uploads"
work_dir: str = "/data/.work"
output_dir: str = "/data/outputs"
chunk_bytes: int = 1024 * 1024
chunk_session_ttl_seconds: int = 300
# 缓存保留:超期任务产物(输出字幕 / 中间音频 / 保留的原始视频)连同 DB 记录一并清理
cache_retention_days: int = 7
# 定时清理间隔(小时):启动时跑一次,之后按此间隔循环
cache_cleanup_interval_hours: int = 24
class ProcessingConfig(BaseModel):
delete_original_after_extract: bool = True
keep_audio: bool = False
class AsrConfig(BaseModel):
model: str = "small"
device: str = "cpu" # cpu | cuda
compute_type: str = "int8" # cpu: int8gpu: float16
language: str = "en"
word_timestamps: bool = True
vad_filter: bool = True
class TranslationConfig(BaseModel):
model: str = "facebook/nllb-200-distilled-1.3B"
device: str = "cpu" # cpu | cuda
src_lang: str = "eng_Latn"
tgt_lang: str = "zho_Hans"
batch_size: int = 16
max_length: int = 256
class SegmentationConfig(BaseModel):
max_words_per_line: int = 14
max_duration_seconds: float = 7.0
min_duration_seconds: float = 1.0
max_chars_per_line: int = 42
class LoggingConfig(BaseModel):
"""日志配置:控制台 + 内存缓冲的最低级别,以及缓冲条数。
分层语义:
- debug详细ffmpeg 命令、模型加载/卸载、转写逐段、翻译逐批进度)
- info简略仅任务阶段转换"任务 N [transcribing 55%]"
- error详细错误完整 traceback由 logger.exception 自带)
"""
level: str = "info" # debug | info | warning | error
buffer_size: int = 2000 # /logs 页面内存缓冲条数
@field_validator("level", mode="before")
@classmethod
def _normalize_level(cls, v):
return str(v).lower() if v else "info"
class DocsConfig(BaseModel):
"""/docs Basic Auth 凭据(明文,对齐 server"""
enabled: bool = True
username: str = "admin"
password: str = ""
realm: str = "audio2text docs"
@field_validator("password", mode="before")
@classmethod
def _coerce(cls, v):
return "" if v is None else str(v)
class Settings(BaseModel):
server: ServerConfig = ServerConfig()
storage: StorageConfig = StorageConfig()
processing: ProcessingConfig = ProcessingConfig()
asr: AsrConfig = AsrConfig()
translation: TranslationConfig = TranslationConfig()
segmentation: SegmentationConfig = SegmentationConfig()
logging: LoggingConfig = LoggingConfig()
docs: DocsConfig = DocsConfig()
def upload_dir(self) -> Path:
return Path(self.storage.upload_dir)
def work_dir(self) -> Path:
return Path(self.storage.work_dir)
def output_dir(self) -> Path:
return Path(self.storage.output_dir)
def _load_yaml(path: Path) -> dict:
if not path.exists():
raise FileNotFoundError(
f"未找到配置文件 {path};请先 cp config.example.yaml config.yaml 并填值。"
)
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
@lru_cache(maxsize=1)
def get_settings() -> Settings:
path = Path(os.getenv("CONFIG_PATH", str(DEFAULT_CONFIG_PATH)))
return Settings.model_validate(_load_yaml(path))
def reload_settings() -> Settings:
"""清缓存并重新读取,供脚本与测试使用。"""
get_settings.cache_clear()
return get_settings()