首次提交

This commit is contained in:
2026-09-28 16:09:31 +08:00
commit 4a6839319a
71 changed files with 12144 additions and 0 deletions

BIN
config/2026-08.lnk Normal file

Binary file not shown.

0
config/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

113
config/config.json Normal file
View 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
View 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()