374 lines
18 KiB
Python
374 lines
18 KiB
Python
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
|
||
|
||
APP_NAME: str = "VideoGen API"
|
||
APP_VERSION: str = "1.0.0"
|
||
DEBUG: bool = False
|
||
SECRET_KEY: str = "change-me"
|
||
|
||
DATABASE_URL: str = "sqlite+aiosqlite:///./videogen.db"
|
||
|
||
REDIS_URL: str = "" # Leave empty to disable Redis (rate limiting, captcha)
|
||
|
||
JWT_ALGORITHM: str = "HS256"
|
||
JWT_EXPIRE_MINUTES: int = 1440
|
||
JWT_EXPIRE_REMEMBER_MINUTES: int = 10080
|
||
|
||
SEEDANCE_API_KEY: str = ""
|
||
SEEDANCE_API_BASE: str = "https://ark.cn-beijing.volces.com/api/v3"
|
||
SEEDANCE_CALLBACK_URL: str = ""
|
||
|
||
LLM_API_BASE: str = "https://api.openai.com/v1"
|
||
LLM_API_KEY: str = ""
|
||
LLM_MODEL: str = "gpt-4o"
|
||
LLM_MOCK: bool = True
|
||
LLM_MEDIA_AS_BASE64: bool = True
|
||
|
||
ENCRYPTION_KEY: str = "changeme-32bytes-base64-key-here!!"
|
||
|
||
# SMS settings
|
||
# 兼容旧字段:SMS_API_URL/SMS_API_KEY/SMS_SIGN_NAME/SMS_TEMPLATE_CODE 保留,
|
||
# 新接入默认使用火山引擎短信 SDK。
|
||
SMS_API_URL: str = ""
|
||
SMS_API_KEY: str = ""
|
||
SMS_SIGN_NAME: str = "VideoGen"
|
||
SMS_TEMPLATE_CODE: str = "SMS_001"
|
||
SMS_MOCK: bool = True
|
||
|
||
# 火山引擎配置。
|
||
VOLC_ACCESS_KEY_ID: str = ""
|
||
VOLC_SECRET_ACCESS_KEY: str = ""
|
||
|
||
# 火山引擎短信配置。
|
||
# SmsAccount=消息组ID,TemplateID=模板ID,Sign=短信签名内容。
|
||
VOLC_SMS_ACCESS_KEY_ID: str = ""
|
||
VOLC_SMS_SECRET_ACCESS_KEY: str = ""
|
||
VOLC_SMS_ACCOUNT: str = ""
|
||
VOLC_SMS_TEMPLATE_ID: str = ""
|
||
VOLC_SMS_SIGN: str = ""
|
||
SMS_CODE_LENGTH: int = 4
|
||
SMS_CODE_TTL_SECONDS: int = 300
|
||
SMS_SEND_INTERVAL_SECONDS: int = 60
|
||
SMS_DAILY_LIMIT: int = 20
|
||
|
||
# Payment settings
|
||
WECHAT_MCH_ID: str = ""
|
||
WECHAT_API_KEY: str = ""
|
||
WECHAT_CERT_PATH: str = ""
|
||
ALIPAY_APP_ID: str = ""
|
||
ALIPAY_PRIVATE_KEY: str = ""
|
||
ALIPAY_PUBLIC_KEY: str = ""
|
||
ALIPAY_NOTIFY_URL: str = ""
|
||
PAYMENT_MOCK: bool = False # Default off; use admin panel to enable for testing
|
||
|
||
STORAGE_TYPE: str = "local"
|
||
STORAGE_LOCAL_PATH: str = "./storage/generate/videos"
|
||
STORAGE_IMAGE_LOCAL_PATH: str = "./storage/generate/images"
|
||
STORAGE_VIDEO_COVER_LOCAL_PATH: str = "./storage/generate/covers"
|
||
UPLOAD_LOCAL_PATH: str = "./storage/uploads"
|
||
|
||
# 首页素材行业装修配置。
|
||
# 文件实际挂载仍走 /uploads,目录在 UPLOAD_LOCAL_PATH/home_materials 下。
|
||
HOME_MATERIAL_MAX_IMAGE_MB: int = 20
|
||
HOME_MATERIAL_MAX_VIDEO_MB: int = 300
|
||
HOME_MATERIAL_MAX_WATERMARK_MB: int = 10
|
||
HOME_MATERIAL_MAX_VIDEO_DURATION_SECONDS: int = 300
|
||
HOME_MATERIAL_IMAGE_WATERMARK_CONCURRENCY: int = 4
|
||
HOME_MATERIAL_VIDEO_WATERMARK_CONCURRENCY: int = 2
|
||
HOME_MATERIAL_FFMPEG_TIMEOUT_SECONDS: int = 600
|
||
HOME_MATERIAL_PROCESSING_STALE_MINUTES: int = 30
|
||
# 重复文字水印配置。
|
||
# 字体必须使用开源可商用字体文件,推荐 Noto Sans CJK SC / Source Han Sans SC,且前端预览层和后端实际生成均由后端使用该字体渲染。
|
||
# 不允许自动 fallback 到系统字体,避免预览不一致和字体授权风险。
|
||
HOME_MATERIAL_TEXT_WATERMARK_FONT: str = "./resource/font/NotoSansCJKsc-Regular.otf"
|
||
HOME_MATERIAL_TEXT_WATERMARK_FONT_LICENSE: str = "SIL Open Font License 1.1"
|
||
HOME_MATERIAL_TEXT_WATERMARK_MAX_TEXT_LENGTH: int = 64
|
||
HOME_MATERIAL_TEXT_WATERMARK_MAX_FONT_SIZE: int = 160
|
||
HOME_MATERIAL_TEXT_WATERMARK_MAX_GAP: int = 2000
|
||
HOME_MATERIAL_TEXT_WATERMARK_PREVIEW_MAX_WIDTH: int = 8192
|
||
HOME_MATERIAL_TEXT_WATERMARK_PREVIEW_MAX_HEIGHT: int = 8192
|
||
|
||
# 本地视频封面截帧配置。
|
||
# 说明:
|
||
# - FFMPEG_BIN 为空时自动从系统 PATH 查找 ffmpeg / ffmpeg.exe。
|
||
# - VIDEO_COVER_TIMEOUT_SECONDS 必须较短,避免 ffmpeg 异常卡住下载 worker。
|
||
FFMPEG_BIN: str = ""
|
||
|
||
# 视频超分配置。
|
||
# 本地处理器始终复用 FFMPEG_BIN,不允许由管理后台覆盖可执行文件路径。
|
||
VOLC_API_KEY: str = ""
|
||
VOLC_MEDIAKIT_API_BASE: str = "https://mediakit.cn-beijing.volces.com"
|
||
VIDEO_UPSCALE_LOCAL_QUEUE: str = "gen_video_upscale_local"
|
||
VIDEO_UPSCALE_REMOTE_QUEUE: str = "gen_video_upscale_remote"
|
||
VIDEO_UPSCALE_LOCAL_TIMEOUT_SECONDS: int = 3600
|
||
VIDEO_UPSCALE_MAX_ATTEMPTS: int = 3
|
||
VIDEO_UPSCALE_REMOTE_POLL_INTERVAL_SECONDS: int = 30
|
||
VIDEO_UPSCALE_REMOTE_POLL_TIMEOUT_SECONDS: int = 7200
|
||
VIDEO_UPSCALE_REMOTE_REQUEST_TIMEOUT_SECONDS: int = 30
|
||
VIDEO_UPSCALE_REMOTE_RESULT_DOWNLOAD_TIMEOUT_SECONDS: int = 600
|
||
VIDEO_UPSCALE_REMOTE_URL_PROBE_THRESHOLD_SECONDS: int = 600
|
||
VIDEO_UPSCALE_REMOTE_URL_PROBE_CONNECT_TIMEOUT_SECONDS: int = 3
|
||
VIDEO_UPSCALE_REMOTE_URL_PROBE_READ_TIMEOUT_SECONDS: int = 5
|
||
VIDEO_UPSCALE_LOCAL_SIGNED_URL_EXPIRE_SECONDS: int = 7200
|
||
VIDEO_UPSCALE_TASK_LEASE_SECONDS: int = 30 * 60
|
||
VIDEO_UPSCALE_RETRY_BACKOFF_SECONDS: int = 60
|
||
VIDEO_UPSCALE_RECOVERY_BATCH_SIZE: int = 50
|
||
VIDEO_UPSCALE_RECOVERY_LOCK_KEY: str = "vg:celery:video_upscale_recovery_lock"
|
||
VIDEO_UPSCALE_EXECUTION_LOCK_KEY_PREFIX: str = "vg:lock:upscale:execute"
|
||
VIDEO_UPSCALE_EXECUTION_LOCK_TTL_SECONDS: int = 30 * 60
|
||
VIDEO_UPSCALE_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:video_upscale:active"
|
||
VIDEO_UPSCALE_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:video_upscale:active_index"
|
||
VIDEO_UPSCALE_STAGE_HANDOFF_DELAY_SECONDS: int = 2
|
||
|
||
VIDEO_COVER_SEEK_TIME: str = "00:00:01"
|
||
VIDEO_COVER_FALLBACK_SEEK_TIME: str = "00:00:00"
|
||
VIDEO_COVER_WIDTH: int = 720
|
||
VIDEO_COVER_TIMEOUT_SECONDS: int = 15
|
||
VIDEO_COVER_FORMAT: str = "jpg"
|
||
|
||
CAPTCHA_ENABLED: bool = True
|
||
|
||
# 银行交易查询接口配置
|
||
BANK_API_BASE: str = ""
|
||
BANK_API_KEY: str = ""
|
||
|
||
BASE_URL: str = ""
|
||
|
||
CORS_ORIGINS: list[str] = ["*"]
|
||
|
||
RATE_LIMIT_ENABLED: bool = True
|
||
|
||
# ChatAPI async generation pipeline settings
|
||
CELERY_BROKER_URL: str = ""
|
||
CELERY_RESULT_BACKEND: str = ""
|
||
# Celery result backend 仅保留近期排障状态;业务恢复以数据库状态和业务日志为准。
|
||
CELERY_RESULT_EXPIRES_SECONDS: int = 7200
|
||
# Celery async 兼容配置。
|
||
# single_loop:每个 Celery 子进程一个专用 event loop,推荐线上/本地统一使用。
|
||
# direct:旧版线程本地 loop 降级模式,建议配合 CELERY_DB_USE_NULLPOOL=true。
|
||
CELERY_ASYNC_RUNNER_MODE: str = "single_loop"
|
||
CELERY_DB_USE_NULLPOOL: bool = False
|
||
CELERY_STARTUP_RECOVERY_ENABLED: bool = True
|
||
CELERY_STARTUP_RECOVERY_DELAY_SECONDS: int = 30
|
||
CELERY_RUNTIME_STARTUP_BARRIER_KEY: str = "vg:celery:recovery:startup_barrier"
|
||
CELERY_RUNTIME_STARTUP_BARRIER_TTL_SECONDS: int = 120
|
||
CELERY_RUNTIME_GLOBAL_RECOVERY_LOCK_KEY: str = "vg:celery:recovery:global"
|
||
# Celery Runtime V2 Worker identity。
|
||
# worker_instance_id 只由 worker_name + host_boot_id + 主进程启动 token 构成,
|
||
# 与 prefork 子进程 PID、threads 线程数和 --concurrency 完全解耦。
|
||
CELERY_RUNTIME_SCHEMA_VERSION: int = 2
|
||
CELERY_RUNTIME_WORKER_INSTANCE_PREFIX: str = "vg:celery:v2:worker_instance"
|
||
CELERY_RUNTIME_WORKER_NAME_INSTANCE_ZSET_PREFIX: str = "vg:celery:v2:worker_name_instances"
|
||
CELERY_RUNTIME_WORKER_TASK_SET_PREFIX: str = "vg:celery:v2:worker_tasks"
|
||
CELERY_RUNTIME_LOCATION_HASH_KEY: str = "vg:celery:runtime:locations"
|
||
CELERY_RUNTIME_WORKER_HEARTBEAT_INTERVAL_SECONDS: int = 30
|
||
CELERY_RUNTIME_WORKER_HEARTBEAT_TTL_SECONDS: int = 90
|
||
CELERY_RUNTIME_WORKER_TASK_SET_TTL_SECONDS: int = 24 * 60 * 60
|
||
CELERY_RUNTIME_WORKER_STALE_GRACE_SECONDS: int = 120
|
||
CELERY_RUNTIME_WORKER_STALE_SCAN_INTERVAL_SECONDS: int = 120
|
||
CELERY_RUNTIME_WORKER_HEARTBEAT_FAILURE_LOG_THRESHOLD: int = 3
|
||
CELERY_RUNTIME_RECONCILE_INTERVAL_SECONDS: int = 300
|
||
CELERY_RUNTIME_GC_INTERVAL_SECONDS: int = 600
|
||
CELERY_RUNTIME_GC_BATCH_SIZE: int = 500
|
||
|
||
CHATAPI_REQUEST_TIMEOUT_SECONDS: int = 600
|
||
CHATAPI_VIDEO_FPS: float = 0.5
|
||
CHATAPI_ASYNC_MAX_RETRIES: int = 3
|
||
CHATAPI_ASYNC_RETRY_BACKOFF_SECONDS: int = 30
|
||
CHATAPI_ASYNC_POLL_INTERVAL_SECONDS: int = 30
|
||
CHATAPI_ASYNC_IMAGE_DEADLINE_MINUTES: int = 30
|
||
# 视频异步生成不再使用 30 分钟最终超时;前 10 分钟高频轮询,之后降频,24 小时最后判定失败才退款。
|
||
CHATAPI_ASYNC_VIDEO_FINAL_DEADLINE_HOURS: int = 24
|
||
CHATAPI_ASYNC_VIDEO_HIGH_FREQ_MINUTES: int = 10
|
||
CHATAPI_ASYNC_VIDEO_HIGH_FREQ_POLL_SECONDS: int = 30
|
||
CHATAPI_ASYNC_VIDEO_BACKOFF_INITIAL_SECONDS: int = 60
|
||
CHATAPI_ASYNC_VIDEO_BACKOFF_MULTIPLIER: int = 2
|
||
CHATAPI_ASYNC_VIDEO_BACKOFF_MAX_SECONDS: int = 3600
|
||
CHATAPI_ASYNC_VIDEO_DIRECT_COUNTDOWN_MAX_SECONDS: int = 300
|
||
|
||
# Distributed provider concurrency limits. 0 means disabled/no-op.
|
||
ARK_CHAT_PROMPT_MAX_CONCURRENCY: int = 20
|
||
ARK_IMAGE_CREATE_MAX_CONCURRENCY: int = 10
|
||
ARK_VIDEO_CREATE_MAX_CONCURRENCY: int = 10
|
||
ARK_IMAGE_POLL_MAX_CONCURRENCY: int = 50
|
||
ARK_VIDEO_POLL_MAX_CONCURRENCY: int = 50
|
||
RESULT_DOWNLOAD_MAX_CONCURRENCY: int = 10
|
||
PROVIDER_LIMIT_WAIT_TIMEOUT_SECONDS: float = 30.0
|
||
PROVIDER_LIMIT_TOKEN_TTL_SECONDS: int = 600
|
||
|
||
CELERY_DB_POOL_SIZE: int = 1
|
||
CELERY_DB_MAX_OVERFLOW: int = 2
|
||
CELERY_DB_POOL_TIMEOUT: int = 60
|
||
CELERY_DB_POOL_RECYCLE: int = 1800
|
||
|
||
# Celery 图片/视频下载容灾配置。
|
||
# Redis broker 下 priority=0 最高,priority=9 最低。
|
||
DOWNLOAD_TASK_PRIORITY_NORMAL: int = 5
|
||
DOWNLOAD_TASK_PRIORITY_RECOVER: int = 0
|
||
DOWNLOAD_TASK_MAX_ATTEMPTS: int = 3
|
||
DOWNLOAD_TASK_RETRY_BACKOFF_SECONDS: int = 30
|
||
DOWNLOAD_TASK_LEASE_SECONDS: int = 10 * 60
|
||
DOWNLOAD_TASK_QUEUE_TIMEOUT_SECONDS: int = 5 * 60
|
||
DOWNLOAD_RECOVERY_BATCH_SIZE: int = 20
|
||
DOWNLOAD_RECOVERY_INTERVAL_SECONDS: int = 60
|
||
DOWNLOAD_RETRY_COUNTDOWN_EXTRA_SECONDS: int = 1
|
||
DOWNLOAD_NON_RETRYABLE_LOCAL_ERRORS: bool = True
|
||
DOWNLOAD_EVENT_VERBOSE_ENABLED: bool = True
|
||
MEDIA_TOKEN_SNAPSHOT_ENABLED: bool = True
|
||
DOWNLOAD_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:download:active"
|
||
DOWNLOAD_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:download:active_index"
|
||
GENERATION_CREATE_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:generation_create:active"
|
||
GENERATION_CREATE_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:generation_create:active_index"
|
||
|
||
# Celery 生成链路 / provider poll 容灾配置。
|
||
# 说明:
|
||
# - 启动容灾保留,但恢复扫描独立投递到 CELERY_RECOVERY_QUEUE。
|
||
# - worker_ready 每个 worker 都会尝试抢启动恢复锁,只有抢到锁的 worker 投递恢复协调任务。
|
||
# - poll active 使用独立 Redis key,避免影响稳定的下载 active 注册表。
|
||
GENERATION_RECOVERY_BATCH_SIZE: int = 20
|
||
GENERATION_RECOVERY_MAX_ROUNDS: int = 1
|
||
GENERATION_CREATE_RECOVERY_INTERVAL_SECONDS: int = 60
|
||
GENERATION_CREATE_QUEUE_TIMEOUT_SECONDS: int = 5 * 60
|
||
MODULE_ASYNC_RECOVERY_INTERVAL_SECONDS: int = 60
|
||
POLL_RECOVERY_BATCH_SIZE: int = 20
|
||
POLL_TASK_LEASE_SECONDS: int = 5 * 60
|
||
POLL_TASK_QUEUE_TIMEOUT_SECONDS: int = 2 * 60
|
||
POLL_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:poll:active"
|
||
POLL_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:poll:active_index"
|
||
CELERY_STARTUP_RECOVERY_LOCK_KEY: str = "vg:celery:startup_recovery_lock"
|
||
CELERY_STARTUP_RECOVERY_LOCK_TTL_SECONDS: int = 120
|
||
CELERY_RECOVERY_QUEUE: str = "gen_recovery"
|
||
CELERY_RECOVERY_TASK_LOCK_TTL_SECONDS: int = 10 * 60
|
||
CELERY_RECOVERY_SOFT_TIME_LIMIT_SECONDS: int = 300
|
||
CELERY_RECOVERY_TIME_LIMIT_SECONDS: int = 420
|
||
CELERY_RECOVERY_STARTUP_TASK_LOCK_KEY: str = "vg:celery:startup_recovery_task_lock"
|
||
GENERATION_RECOVERY_LOCK_KEY: str = "vg:celery:generation_recovery_lock"
|
||
DOWNLOAD_RECOVERY_LOCK_KEY: str = "vg:celery:download_recovery_lock"
|
||
MODULE_ASYNC_RECOVERY_LOCK_KEY: str = "vg:celery:module_async_recovery_lock"
|
||
SHOT_SPLIT_RECOVERY_LOCK_KEY: str = "vg:celery:shot_split_recovery_lock"
|
||
SHOT_ANALYSIS_RECOVERY_LOCK_KEY: str = "vg:celery:shot_analysis_recovery_lock"
|
||
|
||
# 视频到期轮询调度。
|
||
# Celery Beat 每分钟投递轻量 dispatcher 到 gen_recovery;dispatcher 只扫描 next_poll_at 到期的视频任务。
|
||
POLL_DUE_DISPATCH_ENABLED: bool = True
|
||
POLL_DUE_DISPATCH_INTERVAL_SECONDS: int = 60
|
||
POLL_DUE_DISPATCH_BATCH_SIZE: int = 100
|
||
POLL_DUE_DISPATCH_LOCK_KEY: str = "vg:celery:poll_due_dispatch_lock"
|
||
POLL_DUE_DISPATCH_LOCK_TTL_SECONDS: int = 55
|
||
|
||
# Redis execution locks. These locks are fail-closed: when Redis is
|
||
# unavailable, the current Celery task retries and does not fall back to an
|
||
# unlocked database-only execution path.
|
||
GENERATION_CREATE_LOCK_KEY_PREFIX: str = "vg:lock:generation:create"
|
||
GENERATION_POLL_LOCK_KEY_PREFIX: str = "vg:lock:generation:poll"
|
||
GENERATION_DOWNLOAD_LOCK_KEY_PREFIX: str = "vg:lock:generation:download"
|
||
GENERATION_CREATE_LOCK_TTL_SECONDS: int = 10 * 60
|
||
GENERATION_POLL_LOCK_TTL_SECONDS: int = 5 * 60
|
||
GENERATION_DOWNLOAD_LOCK_TTL_SECONDS: int = 10 * 60
|
||
REDIS_EXECUTION_LOCK_RENEW_INTERVAL_SECONDS: int = 30
|
||
|
||
# PostgreSQL short row-lock wait and whole ordinary download timeout.
|
||
# The lock timeout is transaction-local (SET LOCAL), not a global DB setting.
|
||
GENERATION_DB_LOCK_TIMEOUT_SECONDS: int = 5
|
||
GENERATION_DOWNLOAD_TOTAL_TIMEOUT_SECONDS: int = 8 * 60
|
||
|
||
# 模块异步任务容灾配置。
|
||
# 覆盖 ModuleGenerationStep 提词任务、shot 原视频/片段分析、shot ffmpeg 切割 active 注册。
|
||
# 恢复扫描走 CELERY_RECOVERY_QUEUE,真实业务任务回到原始队列。
|
||
MODULE_ASYNC_RECOVERY_BATCH_SIZE: int = 20
|
||
MODULE_ASYNC_QUEUE_TIMEOUT_SECONDS: int = 5 * 60
|
||
MODULE_ASYNC_LEASE_SECONDS: int = 10 * 60
|
||
MODULE_ASYNC_LOCK_TTL_SECONDS: int = 10 * 60
|
||
MODULE_ASYNC_REQUEUE_DELAY_SECONDS: int = 10
|
||
MODULE_ASYNC_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:module_async:active"
|
||
MODULE_ASYNC_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:module_async:active_index"
|
||
MODULE_ASYNC_LOCK_KEY_PREFIX: str = "vg:celery:module_async:lock"
|
||
|
||
RESOURCE_SIGN_SECRET: str = "resource-signature-secret-key-for-API-authentication"
|
||
RESOURCE_SIGN_EXPIRE_SECONDS: int = 60
|
||
RESOURCE_SIGN_ARG_EXPIRE: str = "exp"
|
||
RESOURCE_SIGN_ARG_SIGNATURE: str = "sign"
|
||
|
||
# 爆款开头复刻默认配置。素材校验由前端完成,后端只接收已有上传接口返回的链接。
|
||
HOT_OPENING_DEFAULT_VIDEO_DURATION: int = 4
|
||
HOT_OPENING_DEFAULT_VIDEO_RATIO: str = "9:16"
|
||
HOT_OPENING_DEFAULT_VIDEO_RESOLUTION: str = "480p"
|
||
HOT_OPENING_DEFAULT_TARGET_PLATFORM: str = "抖音"
|
||
|
||
# 拆镜复刻配置。
|
||
# 原始上传视频和拆镜片段都属于 uploads 素材域;只有 generate 生成结果走 token 验签。
|
||
SHOT_ANALYSIS_TIMEOUT_SECONDS: int = 3600
|
||
SHOT_ANALYSIS_SOFT_TIME_LIMIT_SECONDS: int = 3720
|
||
SHOT_ANALYSIS_TIME_LIMIT_SECONDS: int = 3900
|
||
SHOT_ANALYSIS_QUEUE: str = "gen_shot_analysis"
|
||
SHOT_ANALYSIS_LOCK_TTL_SECONDS: int = 180
|
||
SHOT_ANALYSIS_LEASE_SECONDS: int = 180
|
||
SHOT_ANALYSIS_HEARTBEAT_INTERVAL_SECONDS: int = 30
|
||
SHOT_ANALYSIS_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:shot_analysis:active"
|
||
SHOT_ANALYSIS_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:shot_analysis:active_index"
|
||
SHOT_ANALYSIS_LOCK_KEY_PREFIX: str = "vg:lock:shot_analysis"
|
||
SHOT_ANALYSIS_TEMPERATURE: float = 0.1
|
||
SHOT_ANALYSIS_MAX_TOKENS: int = 10000
|
||
SHOT_ANALYSIS_VIDEO_FPS: float = 1.0
|
||
SHOT_ANALYSIS_MAX_LOCAL_VIDEO_MB: int = 45
|
||
|
||
SHOT_SEGMENT_LOCAL_PATH: str = "./storage/uploads/shot_segments"
|
||
SHOT_SEGMENT_URL_PREFIX: str = "/uploads/shot_segments"
|
||
SHOT_SPLIT_MIN_SECONDS: float = 2
|
||
SHOT_SPLIT_MAX_SECONDS: float = 15
|
||
SHOT_SPLIT_END_TOLERANCE_SECONDS: float = 0.5
|
||
SHOT_DURATION_TOLERANCE_SECONDS: float = 1.0
|
||
SHOT_FFMPEG_TIMEOUT_SECONDS: int = 120
|
||
SHOT_FFPROBE_TIMEOUT_SECONDS: int = 20
|
||
FFPROBE_BIN: str = ""
|
||
|
||
# 拆镜切片使用独立队列,避免 FFmpeg 占用用户结果下载 worker。
|
||
SHOT_SPLIT_QUEUE: str = "gen_shot_split"
|
||
SHOT_SPLIT_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:shot_split:active"
|
||
SHOT_SPLIT_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:shot_split:active_index"
|
||
SHOT_SPLIT_MAX_CONCURRENT: int = 1
|
||
SHOT_SPLIT_MAX_RETRY_COUNT: int = 3
|
||
SHOT_SPLIT_RETRY_BACKOFF_SECONDS: int = 30
|
||
SHOT_SPLIT_LEASE_SECONDS: int = 10 * 60
|
||
SHOT_SPLIT_PENDING_TIMEOUT_SECONDS: int = 5 * 60
|
||
SHOT_SPLIT_RECOVERY_BATCH_SIZE: int = 20
|
||
SHOT_SPLIT_LOCK_KEY_PREFIX: str = "vg:shot_replicate:split:lock"
|
||
SHOT_SPLIT_SEMAPHORE_KEY_PREFIX: str = "vg:shot_replicate:split:semaphore"
|
||
|
||
# 私域素材 Celery Runtime。轮询与远程删除使用对象级原子锁,Redis 不可用时停止外部调用。
|
||
PRIVATE_PORTRAIT_POLL_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:private_portrait_poll:active"
|
||
PRIVATE_PORTRAIT_POLL_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:private_portrait_poll:active_index"
|
||
PRIVATE_PORTRAIT_POLL_LOCK_KEY_PREFIX: str = "vg:lock:private_portrait:poll"
|
||
PRIVATE_PORTRAIT_DELETE_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:private_portrait_delete:active"
|
||
PRIVATE_PORTRAIT_DELETE_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:private_portrait_delete:active_index"
|
||
PRIVATE_PORTRAIT_DELETE_LOCK_KEY_PREFIX: str = "vg:lock:private_portrait:delete"
|
||
PRIVATE_PORTRAIT_RUNTIME_LOCK_TTL_SECONDS: int = 180
|
||
PRIVATE_PORTRAIT_RUNTIME_HEARTBEAT_SECONDS: int = 30
|
||
PRIVATE_PORTRAIT_DISPATCH_LOCK_KEY: str = "vg:celery:private_portrait:dispatch_lock"
|
||
PRIVATE_PORTRAIT_DELETE_RECOVERY_LOCK_KEY: str = "vg:celery:private_portrait:delete_recovery_lock"
|
||
|
||
# V3 虚拟素材库 Celery Runtime(与前台私域素材库独立隔离,避免任务集合 key 冲突和相互影响)
|
||
VP_V3_POLL_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:vp_v3_poll:active"
|
||
VP_V3_POLL_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:vp_v3_poll:active_index"
|
||
VP_V3_POLL_LOCK_KEY_PREFIX: str = "vg:lock:vp_v3:poll"
|
||
VP_V3_DELETE_ACTIVE_REDIS_HASH_KEY: str = "vg:celery:vp_v3_delete:active"
|
||
VP_V3_DELETE_ACTIVE_REDIS_ZSET_KEY: str = "vg:celery:vp_v3_delete:active_index"
|
||
VP_V3_DELETE_LOCK_KEY_PREFIX: str = "vg:lock:vp_v3:delete"
|
||
VP_V3_RUNTIME_LOCK_TTL_SECONDS: int = 180
|
||
VP_V3_RUNTIME_HEARTBEAT_SECONDS: int = 30
|
||
VP_V3_DISPATCH_LOCK_KEY: str = "vg:celery:vp_v3:dispatch_lock"
|
||
VP_V3_DELETE_RECOVERY_LOCK_KEY: str = "vg:celery:vp_v3:delete_recovery_lock"
|
||
VP_V3_ASSET_POLL_BATCH_SIZE: int = 50
|
||
VP_V3_REMOTE_DELETE_RECOVERY_BATCH_SIZE: int = 50
|
||
|
||
SHOT_REPLICATE_DEFAULT_VIDEO_DURATION: int = 4
|
||
SHOT_REPLICATE_DEFAULT_VIDEO_RATIO: str = "9:16"
|
||
SHOT_REPLICATE_DEFAULT_VIDEO_RESOLUTION: str = "480p"
|
||
SHOT_REPLICATE_DEFAULT_TARGET_PLATFORM: str = "抖音"
|
||
|
||
|
||
settings = Settings()
|