Spaces:
Running
Running
fix: MFA 数据目录隔离
Browse files- src/mfa_runner.py +50 -3
src/mfa_runner.py
CHANGED
|
@@ -79,8 +79,13 @@ def _get_mfa_command() -> list:
|
|
| 79 |
return ["mfa"]
|
| 80 |
|
| 81 |
|
| 82 |
-
def _build_mfa_env() -> dict:
|
| 83 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
env = os.environ.copy()
|
| 85 |
|
| 86 |
if IS_WINDOWS:
|
|
@@ -93,6 +98,11 @@ def _build_mfa_env() -> dict:
|
|
| 93 |
]
|
| 94 |
env["PATH"] = ";".join(mfa_paths) + ";" + env.get("PATH", "")
|
| 95 |
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
# Linux: 设置 pkuseg 模型目录(云端使用持久化路径)
|
| 97 |
persistent_models = Path("/home/studio_service/models")
|
| 98 |
if persistent_models.exists():
|
|
@@ -164,6 +174,36 @@ def _clean_dict_empty_lines(dict_path: str) -> int:
|
|
| 164 |
return 0
|
| 165 |
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
def run_mfa_alignment(
|
| 168 |
corpus_dir: str,
|
| 169 |
output_dir: str,
|
|
@@ -197,6 +237,10 @@ def run_mfa_alignment(
|
|
| 197 |
if progress_callback:
|
| 198 |
progress_callback(msg)
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
# 检查环境
|
| 201 |
if not check_mfa_available():
|
| 202 |
platform_hint = "tools/mfa_engine 目录" if IS_WINDOWS else "pip install montreal-forced-aligner"
|
|
@@ -254,7 +298,7 @@ def run_mfa_alignment(
|
|
| 254 |
log(f"输出目录: {output_dir}")
|
| 255 |
|
| 256 |
try:
|
| 257 |
-
env = _build_mfa_env()
|
| 258 |
|
| 259 |
result = subprocess.run(
|
| 260 |
cmd,
|
|
@@ -295,6 +339,9 @@ def run_mfa_alignment(
|
|
| 295 |
shutil.rmtree(temp_dir)
|
| 296 |
except Exception:
|
| 297 |
pass
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
|
| 300 |
def run_mfa_validate(
|
|
|
|
| 79 |
return ["mfa"]
|
| 80 |
|
| 81 |
|
| 82 |
+
def _build_mfa_env(mfa_root: Optional[Path] = None) -> dict:
|
| 83 |
+
"""
|
| 84 |
+
构造 MFA 专用环境变量
|
| 85 |
+
|
| 86 |
+
参数:
|
| 87 |
+
mfa_root: 会话独立的 MFA 数据目录(用于并发隔离)
|
| 88 |
+
"""
|
| 89 |
env = os.environ.copy()
|
| 90 |
|
| 91 |
if IS_WINDOWS:
|
|
|
|
| 98 |
]
|
| 99 |
env["PATH"] = ";".join(mfa_paths) + ";" + env.get("PATH", "")
|
| 100 |
else:
|
| 101 |
+
# Linux: 设置会话独立的 MFA_ROOT_DIR(解决并发数据库冲突)
|
| 102 |
+
if mfa_root:
|
| 103 |
+
env["MFA_ROOT_DIR"] = str(mfa_root)
|
| 104 |
+
logger.info(f"设置会话独立 MFA_ROOT_DIR: {mfa_root}")
|
| 105 |
+
|
| 106 |
# Linux: 设置 pkuseg 模型目录(云端使用持久化路径)
|
| 107 |
persistent_models = Path("/home/studio_service/models")
|
| 108 |
if persistent_models.exists():
|
|
|
|
| 174 |
return 0
|
| 175 |
|
| 176 |
|
| 177 |
+
def _create_isolated_mfa_root(session_id: str) -> Path:
|
| 178 |
+
"""
|
| 179 |
+
为每个会话创建独立的 MFA_ROOT_DIR,避免多用户并发时数据库冲突
|
| 180 |
+
|
| 181 |
+
MFA 使用 MFA_ROOT_DIR 环境变量指定数据目录,包含:
|
| 182 |
+
- pretrained_models/: 预训练模型缓存
|
| 183 |
+
- 各种 .db 文件: SQLite 数据库
|
| 184 |
+
|
| 185 |
+
通过为每个会话创建独立目录,完全隔离并发用户
|
| 186 |
+
"""
|
| 187 |
+
import tempfile
|
| 188 |
+
|
| 189 |
+
# 在系统临时目录下创建会话专属的 MFA 根目录
|
| 190 |
+
mfa_root = Path(tempfile.gettempdir()) / f"mfa_session_{session_id}"
|
| 191 |
+
mfa_root.mkdir(parents=True, exist_ok=True)
|
| 192 |
+
|
| 193 |
+
logger.info(f"创建会话独立 MFA 目录: {mfa_root}")
|
| 194 |
+
return mfa_root
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def _cleanup_isolated_mfa_root(mfa_root: Path):
|
| 198 |
+
"""清理会话独立的 MFA 目录"""
|
| 199 |
+
if mfa_root and mfa_root.exists() and "mfa_session_" in str(mfa_root):
|
| 200 |
+
try:
|
| 201 |
+
shutil.rmtree(mfa_root)
|
| 202 |
+
logger.info(f"已清理会话 MFA 目录: {mfa_root}")
|
| 203 |
+
except Exception as e:
|
| 204 |
+
logger.warning(f"清理会话 MFA 目录失败: {e}")
|
| 205 |
+
|
| 206 |
+
|
| 207 |
def run_mfa_alignment(
|
| 208 |
corpus_dir: str,
|
| 209 |
output_dir: str,
|
|
|
|
| 237 |
if progress_callback:
|
| 238 |
progress_callback(msg)
|
| 239 |
|
| 240 |
+
# 为本次会话创建独立的 MFA 数据目录(并发安全)
|
| 241 |
+
session_id = uuid.uuid4().hex[:8]
|
| 242 |
+
isolated_mfa_root = _create_isolated_mfa_root(session_id) if not IS_WINDOWS else None
|
| 243 |
+
|
| 244 |
# 检查环境
|
| 245 |
if not check_mfa_available():
|
| 246 |
platform_hint = "tools/mfa_engine 目录" if IS_WINDOWS else "pip install montreal-forced-aligner"
|
|
|
|
| 298 |
log(f"输出目录: {output_dir}")
|
| 299 |
|
| 300 |
try:
|
| 301 |
+
env = _build_mfa_env(isolated_mfa_root)
|
| 302 |
|
| 303 |
result = subprocess.run(
|
| 304 |
cmd,
|
|
|
|
| 339 |
shutil.rmtree(temp_dir)
|
| 340 |
except Exception:
|
| 341 |
pass
|
| 342 |
+
# 清理会话独立的 MFA 数据目录
|
| 343 |
+
if isolated_mfa_root:
|
| 344 |
+
_cleanup_isolated_mfa_root(isolated_mfa_root)
|
| 345 |
|
| 346 |
|
| 347 |
def run_mfa_validate(
|