# config_utils.py import json import os class ConfigManager: _instance = None _config = None def __new__(cls): if cls._instance is None: cls._instance = super(ConfigManager, cls).__new__(cls) cls._instance.load_config() return cls._instance def load_config(self, config_path="./config/config.json"): """加载配置文件""" try: if not os.path.exists(config_path): # 如果配置文件不存在,创建一个默认配置 default_config = { "tencentcloud": { "ocr": { "secret_id": "please_set_your_secret_id", "secret_key": "please_set_your_secret_key", "region": "ap-beijing", "endpoint": "ocr.tencentcloudapi.com" } } } with open(config_path, 'w', encoding='utf-8') as f: json.dump(default_config, f, indent=4, ensure_ascii=False) print(f"已创建默认配置文件: {config_path}") print("请编辑此文件并填写您的腾讯云OCR密钥") with open(config_path, 'r', encoding='utf-8') as f: self._config = json.load(f) except Exception as e: print(f"加载配置文件失败: {e}") self._config = {} def get(self, key, default=None): """获取配置值""" try: keys = key.split('.') value = self._config for k in keys: value = value[k] return value except (KeyError, TypeError): return default def get_tencent_ocr_config(self): """获取腾讯云OCR配置""" return self.get('tencentcloud.ocr', {}) def get_splitframe_config(self): """获取Whisper配置""" return self.get('splitframe', {}) def get_audio_config(self): """获取音频配置""" return self.get('audio', {}) def get_subtitle_config(self): """获取字幕配置""" return self.get('subtitle', {}) def get_cos_config(self): """获取COS配置""" return self.get('tencentcloud.cos', {}) def get_dispatch_config(self): """获取 COS 任务分发配置(常驻 worker / 认领锁 / 每日备份)。""" return self.get('dispatch', {}) def _prepend_local_ffmpeg_to_path(): """若项目根的 bin/ 下有自带 ffmpeg,则把 bin/ 提到 PATH 最前, 让所有 ffmpeg/ffprobe 调用(抽帧、音频、pydub 等)都优先用项目自带的, 不再依赖系统 PATH。""" root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) bin_dir = os.path.join(root, "bin") ff = os.path.join(bin_dir, "ffmpeg") if os.path.isfile(ff): try: os.chmod(ff, 0o755) ffprobe = os.path.join(bin_dir, "ffprobe") if os.path.isfile(ffprobe): os.chmod(ffprobe, 0o755) except OSError: pass os.environ["PATH"] = bin_dir + os.pathsep + os.environ.get("PATH", "") def _ensure_coscli_executable(): """给项目根的 coscli-linux-amd64 自动加执行权限(解压后常丢失 +x,导致无法调用)。""" if os.name == "nt": return root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) for name in ("coscli-linux-amd64", "coscli"): p = os.path.join(root, name) if os.path.isfile(p) and not os.access(p, os.X_OK): try: os.chmod(p, 0o755) except OSError: pass _prepend_local_ffmpeg_to_path() _ensure_coscli_executable() def _resolve_bin(name): """返回项目 bin/ 下的可执行文件绝对路径(若存在),否则返回名字本身(回退到 PATH)。""" root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) p = os.path.join(root, "bin", name) return p if os.path.isfile(p) else name def get_ffmpeg(): """ffmpeg 可执行路径:优先项目自带 bin/ffmpeg,否则 PATH 里的 ffmpeg。""" return _resolve_bin("ffmpeg") def get_ffprobe(): """ffprobe 可执行路径:优先项目自带 bin/ffprobe,否则 PATH 里的 ffprobe。""" return _resolve_bin("ffprobe") # 全局配置实例 config = ConfigManager()