Files
wehub-resource-sync 41b710f9c7
CI / Frontend checks (push) Failing after 0s
CI / Backend tests (push) Failing after 1s
I18n Documentation Sync / sync-docs (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:28:40 +08:00

199 lines
6.8 KiB
Python

"""
统一路径管理工具
解决项目中路径构建不一致的问题
"""
import os
from pathlib import Path
from typing import Optional
DESKTOP_TRUE_VALUES = {"1", "true", "yes", "on"}
def is_desktop_mode() -> bool:
"""判断当前是否为桌面运行模式"""
return (
os.getenv("AUTOCLIP_DESKTOP_MODE", "").lower() in DESKTOP_TRUE_VALUES
or os.getenv("AUTOCLIP_MODE", "").lower() == "desktop"
)
def get_project_root() -> Path:
"""
获取项目根目录
从backend目录向上查找,直到找到包含frontend和backend的目录
"""
current_path = Path(__file__).parent # backend/core/
# 向上查找项目根目录
while current_path.parent != current_path: # 未到达根目录
if (current_path.parent / "frontend").exists() and (current_path.parent / "backend").exists():
return current_path.parent
current_path = current_path.parent
# 如果没找到,使用默认路径
return Path(__file__).parent.parent.parent
def get_data_directory() -> Path:
"""获取数据目录"""
configured_data_dir = os.getenv("AUTOCLIP_DATA_DIR")
if configured_data_dir:
data_dir = Path(configured_data_dir).expanduser()
elif is_desktop_mode():
app_dir = os.getenv("AUTOCLIP_APP_DIR", "~/Library/Application Support/AutoClip")
data_dir = Path(app_dir).expanduser()
else:
# 统一使用项目根目录下的data目录,与config.py保持一致
data_dir = get_project_root() / "data"
data_dir.mkdir(parents=True, exist_ok=True)
return data_dir
def get_projects_directory() -> Path:
"""获取项目目录"""
projects_dir = get_data_directory() / "projects"
projects_dir.mkdir(parents=True, exist_ok=True)
return projects_dir
def get_output_directory() -> Path:
"""获取输出目录"""
output_dir = get_data_directory() / "output"
output_dir.mkdir(parents=True, exist_ok=True)
return output_dir
def get_project_directory(project_id: str) -> Path:
"""获取项目目录"""
project_dir = get_projects_directory() / project_id
project_dir.mkdir(parents=True, exist_ok=True)
return project_dir
def get_project_raw_directory(project_id: str) -> Path:
"""获取项目原始文件目录"""
raw_dir = get_project_directory(project_id) / "raw"
raw_dir.mkdir(parents=True, exist_ok=True)
return raw_dir
def get_project_output_directory(project_id: str) -> Path:
"""获取项目输出目录"""
output_dir = get_project_directory(project_id) / "output"
output_dir.mkdir(parents=True, exist_ok=True)
return output_dir
def get_clips_directory() -> Path:
"""获取切片目录"""
clips_dir = get_output_directory() / "clips"
clips_dir.mkdir(parents=True, exist_ok=True)
return clips_dir
def get_collections_directory() -> Path:
"""获取合集目录"""
collections_dir = get_output_directory() / "collections"
collections_dir.mkdir(parents=True, exist_ok=True)
return collections_dir
def get_metadata_directory() -> Path:
"""获取元数据目录"""
metadata_dir = get_output_directory() / "metadata"
metadata_dir.mkdir(parents=True, exist_ok=True)
return metadata_dir
def get_settings_file_path() -> Path:
"""获取设置文件路径"""
return get_data_directory() / "settings.json"
def get_uploads_directory() -> Path:
"""获取上传目录"""
uploads_dir = get_data_directory() / "uploads"
uploads_dir.mkdir(parents=True, exist_ok=True)
return uploads_dir
def get_temp_directory() -> Path:
"""获取临时目录"""
temp_dir = get_data_directory() / "temp"
temp_dir.mkdir(parents=True, exist_ok=True)
return temp_dir
def ensure_directory_exists(path: Path) -> Path:
"""确保目录存在"""
path.mkdir(parents=True, exist_ok=True)
return path
def get_video_file_path(project_id: str, filename: str) -> Path:
"""获取项目视频文件路径"""
return get_project_raw_directory(project_id) / filename
def get_srt_file_path(project_id: str, filename: str) -> Path:
"""获取项目SRT文件路径"""
return get_project_raw_directory(project_id) / filename
def get_clip_file_path(clip_id: str, title: str) -> Path:
"""获取切片文件路径"""
# 清理文件名,移除特殊字符
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip()
safe_title = safe_title.replace(' ', '_')
return get_clips_directory() / f"{clip_id}_{safe_title}.mp4"
def get_collection_file_path(collection_id: str, title: str) -> Path:
"""获取合集文件路径"""
# 清理文件名,移除特殊字符
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip()
safe_title = safe_title.replace(' ', '_')
return get_collections_directory() / f"{collection_id}_{safe_title}.mp4"
def get_metadata_file_path(project_id: str) -> Path:
"""获取项目元数据文件路径"""
return get_metadata_directory() / f"{project_id}_metadata.json"
def get_log_file_path() -> Path:
"""获取日志文件路径"""
configured_log_file = os.getenv("LOG_FILE")
if configured_log_file:
log_file = Path(configured_log_file).expanduser()
log_file.parent.mkdir(parents=True, exist_ok=True)
return log_file
if is_desktop_mode() or os.getenv("AUTOCLIP_DATA_DIR"):
logs_dir = get_data_directory() / "logs"
logs_dir.mkdir(parents=True, exist_ok=True)
return logs_dir / "backend.log"
return get_project_root() / "backend.log"
def get_cache_directory() -> Path:
"""获取缓存目录"""
cache_dir = get_data_directory() / "cache"
cache_dir.mkdir(parents=True, exist_ok=True)
return cache_dir
def get_backup_directory() -> Path:
"""获取备份目录"""
backup_dir = get_data_directory() / "backups"
backup_dir.mkdir(parents=True, exist_ok=True)
return backup_dir
def cleanup_temp_files(max_age_hours: int = 24):
"""清理临时文件"""
import time
temp_dir = get_temp_directory()
current_time = time.time()
for file_path in temp_dir.iterdir():
if file_path.is_file():
file_age = current_time - file_path.stat().st_mtime
if file_age > (max_age_hours * 3600):
try:
file_path.unlink()
except Exception as e:
print(f"清理临时文件失败: {file_path}, 错误: {e}")
def validate_file_path(file_path: Path) -> bool:
"""验证文件路径是否安全"""
try:
# 检查路径是否在允许的目录内
allowed_dirs = [
get_data_directory(),
get_output_directory(),
get_project_root()
]
file_path = file_path.resolve()
return any(file_path.is_relative_to(allowed_dir) for allowed_dir in allowed_dirs)
except Exception:
return False