| import os |
| import sys |
| from contextlib import asynccontextmanager |
| from pathlib import Path |
|
|
| |
| |
| |
| |
| |
| |
| if getattr(sys, "frozen", False): |
| _plugin_dir = os.path.join( |
| os.getenv("APPDATA") or str(Path.home()), "VideoMemo", "python-packages" |
| ) |
| os.makedirs(_plugin_dir, exist_ok=True) |
| if _plugin_dir not in sys.path: |
| sys.path.insert(0, _plugin_dir) |
|
|
| import uvicorn |
| from fastapi import FastAPI |
| from starlette.middleware.cors import CORSMiddleware |
| from starlette.middleware.gzip import GZipMiddleware |
| from starlette.staticfiles import StaticFiles |
| from dotenv import load_dotenv |
|
|
| from app.db.init_db import init_db |
| from app.db.provider_dao import seed_default_providers |
| from app.exceptions.exception_handlers import register_exception_handlers |
| |
| |
| from app.utils.logger import get_logger |
| from app.utils.path_helper import get_runtime_dir |
| from app import create_app |
| from app.services.transcriber_config_manager import TranscriberConfigManager |
| from app.services.scheduler import get_scheduler |
| from events import register_handler |
| from ffmpeg_helper import ensure_ffmpeg_or_raise |
|
|
| logger = get_logger(__name__) |
| load_dotenv() |
|
|
| |
| static_path = os.getenv('STATIC', '/static') |
|
|
| |
| |
| |
| static_dir = get_runtime_dir("static") |
| uploads_dir = get_runtime_dir("uploads") |
|
|
| @asynccontextmanager |
| async def lifespan(app: FastAPI): |
| |
| |
| try: |
| logger.info("[startup 1/5] register_handler() — 注册事件处理器") |
| register_handler() |
|
|
| logger.info("[startup 2/5] init_db() — 初始化 SQLite 数据库") |
| init_db() |
|
|
| logger.info("[startup 3/5] TranscriberConfigManager — 读取转写器配置") |
| |
| |
| _cfg = TranscriberConfigManager().get_config() |
| logger.info( |
| f" 当前转写器: type={_cfg['transcriber_type']}, " |
| f"model_size={_cfg['whisper_model_size']}" |
| ) |
|
|
| logger.info("[startup 4/5] seed_default_providers() — 初始化默认 LLM 供应商") |
| seed_default_providers() |
|
|
| logger.info("[startup 5/5] 启动完成,等待请求") |
| get_scheduler().start() |
| except Exception: |
| logger.exception("[startup FAILED] 后端启动期异常,详见堆栈;容器会退出并由 restart 策略决定是否重试") |
| raise |
|
|
| yield |
|
|
| get_scheduler().stop() |
|
|
| app = create_app(lifespan=lifespan) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| CORS_ORIGIN_REGEX = ( |
| r"^chrome-extension://[a-z]+$" |
| r"|^moz-extension://.+$" |
| r"|^http://(localhost|127\.0\.0\.1)(:\d+)?$" |
| r"|^tauri://localhost$" |
| r"|^https?://tauri\.localhost$" |
| |
| r"|^https://([a-z0-9-]+\.)*pages\.dev$" |
| ) |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origin_regex=CORS_ORIGIN_REGEX, |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
| app.add_middleware(GZipMiddleware, minimum_size=1000) |
| register_exception_handlers(app) |
| app.mount(static_path, StaticFiles(directory=static_dir), name="static") |
| app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| if __name__ == "__main__": |
| port = int(os.getenv("BACKEND_PORT", 8483)) |
| host = os.getenv("BACKEND_HOST", "0.0.0.0") |
| logger.info(f"Starting server on {host}:{port}") |
| uvicorn.run(app, host=host, port=port, reload=False) |