首次提交
This commit is contained in:
BIN
config/2026-08.lnk
Normal file
BIN
config/2026-08.lnk
Normal file
Binary file not shown.
0
config/__init__.py
Normal file
0
config/__init__.py
Normal file
BIN
config/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
config/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/config_utils.cpython-311.pyc
Normal file
BIN
config/__pycache__/config_utils.cpython-311.pyc
Normal file
Binary file not shown.
113
config/config.json
Normal file
113
config/config.json
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"tencentcloud": {
|
||||
"ocr": {
|
||||
"secret_id": "",
|
||||
"secret_key": "",
|
||||
"max_attempts": 4,
|
||||
"timeout": 30,
|
||||
"dispatch_log_every": 1000,
|
||||
"endpoints": [
|
||||
]
|
||||
},
|
||||
"cos": {
|
||||
"secret_id": "",
|
||||
"secret_key": "",
|
||||
"region": "",
|
||||
"bucket": ""
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"model_size": "small",
|
||||
"device": "cpu",
|
||||
"compute_type": "float16",
|
||||
"device_index": "auto",
|
||||
"cpu_threads": 2
|
||||
},
|
||||
"splitframe": {
|
||||
"interval_seconds": 0.33,
|
||||
"ffmpeg_threads": 3,
|
||||
"use_gpu": true,
|
||||
"gpu_skip_codecs": [
|
||||
"av1"
|
||||
],
|
||||
"gpu_reserve_last_for_whisper": false,
|
||||
"gpu_skip_indices": [],
|
||||
"gpu_auto_select": true,
|
||||
"gpu_busy_mem_mb": 4096,
|
||||
"crop_bottom_ratio": null,
|
||||
"download": true,
|
||||
"root_path": "./video",
|
||||
"upload_path": "",
|
||||
"delete_video": true,
|
||||
"coscli_path": "./coscli-linux-amd64",
|
||||
"cosconfig_path": ".cos.yaml",
|
||||
"ffmpeg_timeout": 3600
|
||||
},
|
||||
"subtitle": {
|
||||
"min_confidence": 50,
|
||||
"similarity_threshold": 0.7,
|
||||
"ocr_workers": 20,
|
||||
"require_chinese": true,
|
||||
"redundant_bands": 12,
|
||||
"redundant_merge_ratio": 0.97,
|
||||
"no_composite": false,
|
||||
"min_duration_seconds": 0,
|
||||
"merge_workers": 4,
|
||||
"cv_detect": true,
|
||||
"cv_use_region": false,
|
||||
"max_tiles_per_composite": 24,
|
||||
"ocr_max_image_side": 4000,
|
||||
"filter_static": true,
|
||||
"static_frac": 0.25,
|
||||
"filter_lowconf": true,
|
||||
"lowconf_thr": 80,
|
||||
"lowconf_filter_frac": 0.6,
|
||||
"lowconf_min_det": 200,
|
||||
"strip_static_prefix": false,
|
||||
"filter_watermark_pos": true,
|
||||
"wm_dominant_frac": 0.25,
|
||||
"wm_min_count": 20,
|
||||
"split_far_boxes": true,
|
||||
"max_duration_seconds": 0,
|
||||
"long_video_list": "./超长待重跑.txt",
|
||||
"ocr_no_drop": true,
|
||||
"ocr_max_passes": 5,
|
||||
"ocr_retry_sleep": 2,
|
||||
"ocr_fail_list": "./OCR失败待重跑.txt",
|
||||
"max_global_ocr": 0,
|
||||
"no_subtitle_reasons": [
|
||||
"字幕太少",
|
||||
"冗余文字过多",
|
||||
"冗余文字太多",
|
||||
"无字幕区域",
|
||||
"非字幕内容",
|
||||
"置信度太低",
|
||||
"未检测",
|
||||
"特殊字体",
|
||||
"视频过短",
|
||||
"视频过长"
|
||||
]
|
||||
},
|
||||
"dispatch": {
|
||||
"enabled": true,
|
||||
"source": "cos",
|
||||
"run_long_video": false,
|
||||
"auto_fallback_long_video": false,
|
||||
"global_dedup_on_startup": false,
|
||||
"machine_id": "",
|
||||
"jobs_root": "",
|
||||
"shard_size": 2000,
|
||||
"keep_alive": true,
|
||||
"idle_poll_seconds": 60,
|
||||
"claim_shuffle": true,
|
||||
"claim_match": "",
|
||||
"heartbeat_seconds": 300,
|
||||
"daily_backup_path": "",
|
||||
"monitor_root": "",
|
||||
"long_list_backup_path": "",
|
||||
"long_list_clear_after_upload": true,
|
||||
"long_list_min_batch": 1000,
|
||||
"daily_backup_hour": 10,
|
||||
"stop_flag_key": ""
|
||||
}
|
||||
}
|
||||
131
config/config_utils.py
Normal file
131
config/config_utils.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# 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()
|
||||
Reference in New Issue
Block a user