Files
BiliNote/backend/main.py
huangjianwu b8f359e7e7 feat(extension): 浏览器插件 P1 MVP
新建 BillNote_extension/ 工作空间(基于 vitesse-webext 骨架,Vue 3 + Vite + UnoCSS + MV3)。

P1 MVP 范围:
- popup:自动读当前 tab URL,识别 Bilibili / YouTube / 抖音 / 快手;提交 /generate_note 后轮询 /task_status;展示 markdown,复制 + 下载 .md
- options:后端地址输入与连通性测试;从 /get_all_providers + /get_models_by_provider 拉供应商/模型列表;默认画质、截图/跳转、笔记风格
- chrome.storage.local 持久化设置与最近 30 个任务,popup 重开恢复进行中任务
- markdown 里的 /static/screenshots 路径在渲染前重写为绝对地址

后端:CORS 改用 regex,新增允许 chrome-extension:// 与 moz-extension:// 源(同时保留 localhost / 127.0.0.1 / tauri.localhost)。无新增 backend endpoint。

P2-P4(content script 悬浮按钮、cookie 直通、side panel、思维导图、RAG 问答)保留 stub 文件,不在本次范围。

去掉 vitesse-webext 自带的 simple-git-hooks postinstall 配置——它会在仓库根装 pre-commit 钩子去跑 pnpm lint-staged,但仓库根没有 package.json,会破坏所有提交流。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:40:15 +08:00

86 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
from contextlib import asynccontextmanager
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.db.model_dao import init_model_table
# from app.db.provider_dao import init_provider_table
from app.utils.logger import get_logger
from app import create_app
from app.services.transcriber_config_manager import TranscriberConfigManager
from events import register_handler
from ffmpeg_helper import ensure_ffmpeg_or_raise
logger = get_logger(__name__)
load_dotenv()
# 读取 .env 中的路径
static_path = os.getenv('STATIC', '/static')
out_dir = os.getenv('OUT_DIR', './static/screenshots')
# 自动创建本地目录static 和 static/screenshots
static_dir = "static"
uploads_dir = "uploads"
if not os.path.exists(static_dir):
os.makedirs(static_dir)
if not os.path.exists(uploads_dir):
os.makedirs(uploads_dir)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
@asynccontextmanager
async def lifespan(app: FastAPI):
register_handler()
init_db()
# 转写器不再在启动时强制初始化,而是在首次生成笔记时按需创建
# 如果配置了不可用的类型(如 mlx-whisper 未安装),会在使用时报错而非静默回退
_cfg = TranscriberConfigManager().get_config()
logger.info(f"当前转写器配置: type={_cfg['transcriber_type']}, model_size={_cfg['whisper_model_size']}")
seed_default_providers()
yield
app = create_app(lifespan=lifespan)
# 允许的源:本地 web 端 + Tauri 桌面端 + 浏览器扩展chrome/edge/firefox
# 用 regex 是因为 chrome-extension://<id> 的 id 在每次开发版加载时不固定
CORS_ORIGIN_REGEX = (
r"^chrome-extension://[a-z]+$"
r"|^moz-extension://.+$"
r"|^http://(localhost|127\.0\.0\.1)(:\d+)?$"
r"|^http://tauri\.localhost$"
)
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)