一个面向视频转换的多工具 Web 项目。主页提供统一工具入口,每个工具保持独立工作台,同时复用整体导航、缓存和输出目录。
/#/ 提供工具选择入口,目前包含视频转 GIF、BV 音频片段提取、视频总结和讲课视频转练习题。/#/gif 支持上传本地视频,或输入 Bilibili BV 号 / bilibili.com/video/BV... 地址下载视频;URL 会自动提取 BV 与 p 参数,多分 P 视频可选择具体分 P。/#/audio 支持输入 BV 号 / Bilibili URL、选择分 P、先下载视频、设置开始和结束时间、试听片段,可选择音频增强,并导出 mp3、m4a 或 wav 音频片段。/#/summary 输入 BV 号 / Bilibili URL、选择分 P,自动拉取该视频完整的 CC 字幕,用大模型生成结构化总结:整体总结、带时间点的关键内容点(可点击跳转到对应时间)、金句/知识点提炼,支持一键复制 Markdown、下载完整字幕文件。长视频会分段总结后再合并,避免截断丢失内容。仅支持带 CC 字幕(人工或 AI 字幕)的视频。/data 持久化;Compose 默认映射到宿主机目录。docker build -t video2any .
docker run --rm -p 8000:8000 -v video2emoticon-data:/data video2any
打开 http://localhost:8000。
也可以使用 Compose:
mkdir -p data/uploads data/downloads data/outputs data/fonts data/exercises data/models data/admin cookies
docker compose pull
docker compose up
Compose 默认使用目录映射:
./data/uploads -> /data/uploads
./data/downloads -> /data/downloads
./data/outputs -> /data/outputs
./data/summaries -> /data/summaries
./data/exercises -> /data/exercises
./data/models -> /data/models
./data/fonts -> /data/fonts
./data/admin -> /data/admin
./cookies -> /data/cookies
字体文件可以通过页面上传,也可以直接放入 data/fonts/ 后在页面点击刷新。支持 .ttf、.otf、.ttc、.otc。
管理入口为 /#/_manage,首页和工具导航不显示入口,管理接口不出现在公开的 OpenAPI 文档中。后台可管理 Bilibili Cookie、刷新令牌和保活参数,以及大模型 API Key、接口地址、模型、超时与站点分享地址。
首次使用时,在服务器创建管理员;没有默认密码,也没有公开注册接口:
# Compose 部署(先启动包含后台功能的新镜像)
docker compose exec app python -m backend.app.admin_auth setup --username admin
# 本地开发,在项目根目录执行;DATA_DIR 须与后端进程一致
.venv/bin/python -m backend.app.admin_auth setup --username admin
命令会交互输入并确认密码,至少 12 个字符。Compose 部署后访问 http://localhost:8005/#/_manage,使用创建的账号登录。忘记密码时,将命令中的 setup 改为 reset;重置和后台修改密码都会使全部管理会话失效。
配置保存后生效,无需改 .env 或重建容器;已保存的配置优先于环境变量,重启后仍然保留。大模型新调用使用新的连接设置;正在进行的调用继续使用原连接。基础设施和本地识别模型的配置(数据目录、端口、ASR 后端等)仍由部署配置控制。
管理员密码采用带随机盐的 PBKDF2-SHA256 哈希,登录会话由服务器保存,8 小时过期,支持退出撤销。Cookie 使用 HttpOnly、SameSite=Strict,HTTPS 下启用 Secure;修改配置需要 CSRF 校验和同源校验,登录失败有频率限制。已保存的 Cookie、令牌、API Key 不会通过管理接口回显。隐藏入口用于减少曝光,实际访问权限由后端鉴权控制。
持久保留 data/admin/(管理员、会话及设置)和 cookies/(Bilibili 凭据),Compose 已挂载。密钥保存在服务端文件中,数据库和凭据文件权限为 0600,管理员目录为 0700。生产环境通过 HTTPS 访问;反向代理应保留原始 Host 并正确传递协议,使同源校验和 Secure Cookie 正常工作。
Bilibili 下载和字幕接口共用一份登录态。服务启动时检查 Cookie,之后默认每 24 小时检查一次;Bilibili 返回需要刷新时,自动执行 Web Cookie 续期。临时网络错误和风控响应保留凭据,默认 5 分钟后重试;账号确实失效时提示重新登录。
推荐在隐藏管理后台的「Bilibili 账号」页导入凭据:
SESSDATA,自动续期还需 bili_jct,建议保留 DedeUserID、buvid3、buvid4。https://www.bilibili.com,复制 ac_time_value,填入「刷新令牌」。Cookie 和令牌必须来自同一次登录,仅导出 Cookie 无法续期。重新登录后,在后台重新导入 Cookie 和对应的刷新令牌。更换登录态时,未同时提交新令牌会清除原令牌,避免混用;仅补充令牌可保留原 Cookie。移除账号后,旧环境变量不会重新恢复账号。
已有部署也兼容直接挂载 Netscape Cookie 文件:
mkdir -p cookies
# 将导出的 Bilibili cookies 保存为 cookies/bilibili.cookies.txt
docker compose up
也可以直接传浏览器请求里的原始 Cookie header:
docker run --rm \
-p 8000:8000 \
-v video2emoticon-data:/data \
-e BILIBILI_COOKIE_HEADER='SESSDATA=...; bili_jct=...; DedeUserID=...' \
video2any
还支持把 Netscape cookies 文件内容放到 BILIBILI_COOKIES 环境变量中。环境变量仅在 Cookie 文件不存在时初始化文件;已有文件优先,避免每次请求或重启用旧 Cookie 覆盖续期结果。原始 Cookie header 也只在文件中没有登录态时使用。在后台导入或移除账号后,以后台保存的登录态为准。
如果尚未通过后台管理账号,仍可用环境变量初始化刷新令牌和默认参数:
BILIBILI_REFRESH_TOKEN=同一次登录的ac_time_value
BILIBILI_KEEPALIVE_ENABLED=true
BILIBILI_CHECK_INTERVAL_SECONDS=86400
BILIBILI_RETRY_INTERVAL_SECONDS=300
docker compose up -d --force-recreate app
后台显示缓存状态,不触发上游请求;「检查并续期」才主动检查。healthy 表示检查成功且具备刷新令牌,missing_refresh_token 表示当前有效但无法续期,refresh_required 表示需要补充令牌,expired 表示需要重新导入凭据,retrying 表示临时失败。刷新返回 86095 时先重新获取刷新口令再重试,持续失败按短间隔重试。旧 /api/bilibili/session 接口也需要管理员登录。本地 .env 不会自动读取,但可以完全通过后台保存这些运行设置。
新 Cookie 和匹配的新令牌会一起原子保存到 Cookie 文件旁的 bilibili.cookies.session.json,再写回 bilibili.cookies.txt 并确认刷新;确认请求失败后会使用已保存的新登录态继续恢复。后台检查与业务请求通过线程锁和文件锁协调,多个进程不会同时轮换令牌。下载任务使用独立临时 Cookie 文件,完成后删除,避免 yt-dlp 将旧凭据写回主文件。Compose 中的 cookies/ 挂载需要可写并保留到下次部署。
服务会优先使用保存的续期结果,因此无需每次刷新后修改 .env。Bilibili 密码变更、手动退出或风控强制下线仍需要重新登录,自动续期无法恢复已被撤销的会话。
可在容器内手动检查一次(输出仅包含状态):
docker compose exec app python -m backend.app.bili_auth
HTTP 412 也可能由网络出口或请求频率触发,不会直接被当作账号失效。续期协议参考 bilibili-API-collect 和 defy997/bilibili-api;凭据保存与网络故障处理同时参考 bilibili-cli。不要把 Cookie 或会话文件提交到 GitHub;cookies/ 和 data/ 已加入 .gitignore。
后端:
python3 -m venv .venv
. .venv/bin/activate
pip install -r backend/requirements.txt
uvicorn backend.app.main:app --reload --host 0.0.0.0 --port 8000
前端:
cd frontend
npm install
npm run dev
前端开发服务默认代理 /api 到 http://127.0.0.1:8000。
前端使用 React + TypeScript + Vite,UI 组件库为 Radix UI Themes。全局主题在 frontend/src/main.tsx 配置,品牌色和组件样式在 frontend/src/ui.css;表单、按钮、弹窗和进度条统一使用 Radix 组件。UploadField 封装视频选择与拖拽上传,FieldSelect 封装下拉菜单,RangeInput 使用 Radix Slider 处理拖动与键盘操作。
.github/workflows/docker.yml 会在以下场景构建镜像:
main 或 masterv* tagworkflow_dispatch非 pull request 事件会推送到:
ghcr.io/<owner>/video2any:latest
ghcr.io/<owner>/video2any:<branch-or-tag>
ghcr.io/<owner>/video2any:sha-<commit>
部署时挂载 /data:
docker run -p 8000:8000 -v video2emoticon-data:/data ghcr.io/<owner>/video2any:latest
如果用 Compose 部署,按本仓库的 docker-compose.yml 会映射到当前目录下的 data/ 和 cookies/。
Bilibili、OPENAI_*、SUMMARY_MAX_INPUT_CHARS 和 SITE_URL 均可在管理后台保存;以下环境变量保留为部署默认值或旧部署的初始化来源。后台已保存的字段优先于环境变量。
DATA_DIR:运行时数据目录,默认 /data。FRONTEND_DIST:前端静态文件目录,镜像内默认 /app/frontend/dist。FONT_FILE:FFmpeg drawtext 使用的字体文件路径。/data/fonts:页面可扫描的字体目录,用于文字层字体选择和预览。CORS_ORIGINS:开发时允许的跨域来源,默认 *。BILIBILI_COOKIES_FILE:Netscape 格式 Bilibili cookies 文件路径,推荐挂载到 /data/cookies/bilibili.cookies.txt。BILIBILI_COOKIE_HEADER:浏览器请求里的原始 Cookie header,例如 SESSDATA=...; bili_jct=...。BILIBILI_COOKIES:Netscape cookies 文件内容,适合通过部署平台 Secret 注入。BILIBILI_REFRESH_TOKEN:Web 登录刷新令牌,对应同一登录的 localStorage ac_time_value;自动续期所需,刷新后优先使用持久化的新令牌。BILIBILI_KEEPALIVE_ENABLED:自动检查和续期,默认 true;设为 false 可关闭后台及请求前的检查。BILIBILI_CHECK_INTERVAL_SECONDS:登录态检查间隔,默认 86400(24 小时),最小 3600;启动及业务请求共用检查结果。BILIBILI_RETRY_INTERVAL_SECONDS:网络或续期临时失败后的重试间隔,默认 300,最小 60。OPENAI_API_KEY:大模型 API Key 的可选初始值;可直接在后台填写,未配置有效密钥时总结页会返回 503。OPENAI_BASE_URL:大模型 OpenAI 兼容接口地址,默认 https://api.deepseek.com。OPENAI_MODEL:模型名,默认 deepseek-chat。OPENAI_TIMEOUT:大模型响应读取等待超时(秒),默认 300;连接和连接池等待为 10 秒,发送请求为 30 秒。SDK 对超时、连接错误及部分 HTTP 错误最多自动重试 2 次,整个调用耗时可能超过此设置。OPENAI_REASONING_EFFORT:可选思考深度。未配置时,glm-5.3 系列自动使用 low;其他模型保持服务商默认值。可按模型能力设为 low、high、max。OPENAI_THINKING_TYPE:可选 enabled / disabled,空值保留服务商默认。仅用于支持该参数的模型。GLM-5.3 系列不能关闭思考,设置 disabled 时会提示改用 low 或切换模型。OPENAI_REASONING_MAX_TOKENS:GLM-5.3 系列首次调用的输出 token 预算下限,默认 16384,为思考与正文共同预留空间;截断修正时最多提高到该值的两倍。预算是上限,实际使用量以响应 usage 为准。OPENAI_JSON_MODE:练习题流程默认请求 json_object 输出;设为 false 可关闭。服务商明确拒绝 JSON 模式时自动回退,同一模型后续请求复用回退结果。SUMMARY_MAX_INPUT_CHARS:总结分段时每段的字幕字符预算,默认 9000(越长越完整,但更慢更费 token)。出题及复核输出比普通视频总结长,默认给模型 300 秒的响应读取等待时间。可在后台「大模型连接」将响应等待时间调整为 300 秒,保存后生效。尚未通过后台保存时,也可将 .env 的 OPENAI_TIMEOUT=60 改成 OPENAI_TIMEOUT=300,并执行 docker compose up -d --force-recreate app 应用环境变量;单纯 restart 不会更新环境变量。
LLM 日志包含本次调用标识、读取超时、最大重试次数、总耗时和错误类型:APITimeoutError 表示超时,status=429 表示限流或额度问题,status=5xx 表示上游服务错误。LLM output validation failed; regenerating once 表示返回的 JSON / 题目结果不符合要求后重新生成,与网络重试不同;正常出题后的答案复核也会再调用一次模型。
finish_reason=length 表示输出达到 token 上限,练习题流程不会接受该次结果;修正请求携带上次返回内容,提高 token 预算并要求压缩描述、重新输出完整 JSON,最多修正一次。日志还会显示 reasoning_chars、completion_tokens、reasoning_effort 和 thinking,便于区分思考耗时、正文输出与截断。知识点提炼失败后点击重试会复用已完成的转写。
GLM-5.3-Flash 只能启用思考,可以用 low 降低深度;GLM-4.7 系列支持非思考模式,具体以模型说明和接口参数为准。若希望使用 GLM-4.7-Flash 的非思考模式,可在包含这些配置的新版本中设置:
OPENAI_BASE_URL=https://open.bigmodel.cn/api/paas/v4
OPENAI_MODEL=glm-4.7-flash
OPENAI_THINKING_TYPE=disabled
OPENAI_REASONING_EFFORT=
也可在管理后台设置上述服务地址、模型和思考模式,保留现有智谱 API Key 后保存,无需重新创建容器。如果使用环境变量,执行 docker compose up -d --force-recreate app。这些配置需要包含对应代码的新镜像支持。
镜像内置:
Bilibili 下载取决于部署环境网络可达性和 cookie 有效性。部分视频如果需要会员、地区权限或 cookie 过期,yt-dlp 可能无法下载,后端会把失败原因返回给前端。
/#/exercises 支持上传讲课视频,或输入 B 站 BV / URL 并选择单个分 P。下载或上传完成后,在服务端通过 .env 的 ASR_BACKEND 选择 SenseVoice-Small 或 Qwen3-ASR-0.6B 识别中文语音,再使用现有 OPENAI_* 配置提炼知识点。默认 SenseVoice-Small,保留 Whisper 兼容模式。页面只显示服务端配置的模型。修改 .env 后重新创建容器并刷新页面,新识别任务使用新模型。
试题地址形如 /#/exercises/papers/er-ci-fang-cheng,根据题目关联的知识点转为拼音;同名试题使用 -2、-3 等序号区分。首页显示最近生成的试题,出题工作台提供可翻页的全部记录;点击标题即可回看、复制地址和打印。每个页面保留题目及生成时知识点的独立快照,课程修改、重新出题或原视频缓存过期不会改变已经保存的页面。已有题目批次会在服务启动时自动补入记录,重复保存相同标题和选题会复用页面地址。
练习卷与答案解析按相同顺序连续编号,使用 A4 黑白排版;练习卷保留答题空间,打印时隐藏导航、页面地址与操作按钮。网页支持 Markdown 与 LaTeX,包括分式、根号、上下标、积分、求和、矩阵与分段函数。使用 $...$ / $$...$$,不支持自定义宏或 LaTeX 绘图。候选题只预览和勾选,不提供题目编辑器;可以重新生成独立批次,旧题目及其知识点版本会保留。
长视频按完整转写分段提炼,不截掉后半段。只分析语音文字,不自动识别 PPT 或板书画面;可手动补充。题数少于所选知识点时,从选定范围均匀取点。出题跨知识点合并,每批最多 5 道,并逐批复核答案和解析;普通 10 道题共 4 次大模型调用(不计校验修正或接口重试),原文过长时会自动拆成更小批次。本次出题任务的后续批次和复核共用对话 history,携带此前返回的完整题目、答案及解析;复核直接读取上一条 assistant 消息,避免再次拼接题目正文。跨批重复题、题型顺序或知识点分配不符会触发一次修正。history 会随请求作为输入发送,输入计费以服务商规则为准。后台日志显示计划批次数、调用次数及每次请求的 history 消息数。仍建议在打印前查看候选题。
转写和出题为后台任务,页面显示阶段及进度,刷新后恢复最近课程;页面地址中的 lesson 参数也可以重新打开该课程。错误可重试,同一模型已完成的转写会复用。切换模型后,点击“使用新模型重新识别”,会重新转写并提炼知识点,更新内容版本,保留旧题目批次;会替换当前转写及手动修改,需原视频文件存在。任务使用提交时的模型,运行中不可切换。切换识别失败时保留旧内容;识别成功但知识点提炼失败时,重试复用新模型的转写。课程文字、知识点、题目批次与试题页面保存在 /data/exercises/exercises.db。课程关联的上传视频及 B 站视频随课程保留,服务重启或超过 24 小时不访问均不会自动清理;其他原视频缓存仍在 24 小时未使用后清理,处理中的原视频也不会被清理。服务重启会把未完成任务标记为中断,可手动重试。
部署时同时持久挂载 /data/uploads、/data/downloads 和 /data/exercises(Compose 已配置)。视频元数据中的文件路径按当前数据目录解析,兼容旧的绝对路径记录。升级不会恢复此前已被清理的视频文件;这类视频需要重新上传。
环境变量(Compose 已传入):
ASR_BACKEND:sensevoice(默认)、qwen3 或 whisper,所有新识别任务采用此配置;页面不能覆盖服务端选择。ASR_MODEL_SOURCE:新模型下载源,默认 modelscope,也支持 huggingface。SENSEVOICE_MODEL:默认 iic/SenseVoiceSmall,可传本地模型目录。QWEN_ASR_MODEL:默认 Qwen/Qwen3-ASR-0.6B-hf,可传本地模型目录。使用官方 Transformers 原生 -hf 权重(同为 0.6B),本地目录也需对应此版本。ASR_VAD_MODEL:默认 iic/speech_fsmn_vad_zh-cn-16k-common-pytorch,两个中文模型共用,可传本地目录。ASR_MODEL_DIR:新模型缓存根目录,默认 DATA_DIR/models;ModelScope、Hugging Face 缓存分开保存。ASR_DEVICE:新模型推理设备,默认 cpu;本地支持 PyTorch 的设备如 cuda:0、mps。默认 Docker 镜像安装 CPU 版 PyTorch,GPU 部署需自行安装匹配运行时。ASR_CPU_THREADS:新模型 CPU 线程数,默认 4,所有转写串行执行,每次只保留一个识别模型以控制内存。ASR_SEGMENT_SECONDS:语音段最长秒数,默认 25,范围 5–30。SenseVoice / Qwen 时间戳来自 VAD 的原视频语音段位置,适合内容回看,不是逐字强制对齐。WHISPER_MODEL:兼容模式模型,默认 small;也可传本地 CTranslate2 模型目录。HF_ENDPOINT:Hugging Face 下载地址,默认 https://hf-mirror.com;可设为 https://huggingface.co 使用官方源。不影响 ModelScope 下载。HF_HUB_DISABLE_XET:默认 1,关闭 Xet/CAS 文件重建,避免镜像下载仍访问 cas-server.xethub.hf.co 而报 401。需要在 Python 进程启动前设置;官方网络下可显式设为 0。WHISPER_CPU_THREADS / WHISPER_MODEL_DIR:兼容模式线程数 / 缓存目录,默认 4 / DATA_DIR/models。EXERCISE_MAX_INPUT_CHARS:知识点提炼每段原文预算,默认 9000。两个中文模型均使用本地推理,无需外部转写 API。Qwen 在 CPU 上通常比 SenseVoice 慢,也需要更多内存;首次使用下载对应识别模型和小型 VAD 模型,后续复用持久缓存。长视频先用 FFmpeg 提取 16 kHz 单声道音轨,再按 VAD 片段识别,保留原视频时间位置。知识点与出题需要有效的 OPENAI_API_KEY。
切换示例(已运行包含新模型依赖的镜像时):
ASR_BACKEND=qwen3
ASR_MODEL_SOURCE=modelscope
HF_HUB_DISABLE_XET=1
保存 .env 后执行 docker compose up -d --force-recreate app,然后刷新页面。切回 SenseVoice 将 ASR_BACKEND 改为 sensevoice。环境变量固定在任务提交时,服务重启前正在进行的任务会被标记为中断,可重试。
Docker 镜像包含新模型依赖、CPU PyTorch、Pandoc 和 Noto 中文字体。Compose 持久挂载 ./data/exercises 与 ./data/models。模型切换的代码变更需要使用包含本次更新的新镜像;本地从源码构建可运行:
docker build -t video2any:local .
# 将 docker-compose.yml 的 image 改为 video2any:local 后:
docker compose up -d --force-recreate app
若旧版本 Whisper 下载出现 File reconstruction error / CAS Client Error / 401 Unauthorized,可先在 .env 增加 HF_HUB_DISABLE_XET=1,并在 Compose 的 environment 中增加 HF_HUB_DISABLE_XET: ${HF_HUB_DISABLE_XET:-1},然后运行 docker compose up -d --force-recreate app 再重试。此变量由 Hugging Face 官方支持:环境变量说明。只修改 .env 而不传入容器不会生效;docker compose restart 不会应用新的环境变量。已有正常模型缓存无需删除。
若提示 Network is unreachable,检查容器到下载源的网络。新模型默认使用 ModelScope;可改为 ASR_MODEL_SOURCE=huggingface 并配置 HF_ENDPOINT,或者配置可达的代理。离线部署需预先准备识别模型和 VAD 模型完整目录,放到 ./data/models 下,并使用容器路径,例如:
ASR_BACKEND=sensevoice
SENSEVOICE_MODEL=/data/models/SenseVoiceSmall
QWEN_ASR_MODEL=/data/models/Qwen3-ASR-0.6B
ASR_VAD_MODEL=/data/models/fsmn-vad
HF_HUB_DISABLE_XET=1
本地开发建议 Python 3.12,安装 backend/requirements.txt。本地 .env 不会自动读取,启动时传入环境变量,例如 ASR_BACKEND=qwen3 HF_HUB_DISABLE_XET=1 uvicorn backend.app.main:app --reload。
兼容旧版 Word 导出接口还需安装 Pandoc 和 Noto Serif CJK SC 字体(Linux 可安装 pandoc fonts-noto-cjk,macOS 可使用 Homebrew 安装 Pandoc 并安装 Noto 中文字体)。请保持单个 Uvicorn worker;本模块的工作池及清理保护由单进程管理。
验证:
.venv/bin/pip install pytest httpx
.venv/bin/python -m pytest backend/tests
cd frontend
npm run build
Word 集成测试需要 Pandoc;也可在测试环境安装 pypandoc_binary,不影响生产依赖。
一个面向视频转换的多工具 Web 项目。主页提供统一工具入口,每个工具保持独立工作台,同时复用整体导航、缓存和输出目录。
/#/ 提供工具选择入口,目前包含视频转 GIF、BV 音频片段提取、视频总结和讲课视频转练习题。/#/gif 支持上传本地视频,或输入 Bilibili BV 号 / bilibili.com/video/BV... 地址下载视频;URL 会自动提取 BV 与 p 参数,多分 P 视频可选择具体分 P。/#/audio 支持输入 BV 号 / Bilibili URL、选择分 P、先下载视频、设置开始和结束时间、试听片段,可选择音频增强,并导出 mp3、m4a 或 wav 音频片段。/#/summary 输入 BV 号 / Bilibili URL、选择分 P,自动拉取该视频完整的 CC 字幕,用大模型生成结构化总结:整体总结、带时间点的关键内容点(可点击跳转到对应时间)、金句/知识点提炼,支持一键复制 Markdown、下载完整字幕文件。长视频会分段总结后再合并,避免截断丢失内容。仅支持带 CC 字幕(人工或 AI 字幕)的视频。/data 持久化;Compose 默认映射到宿主机目录。docker build -t video2any .
docker run --rm -p 8000:8000 -v video2emoticon-data:/data video2any
打开 http://localhost:8000。
也可以使用 Compose:
mkdir -p data/uploads data/downloads data/outputs data/fonts data/exercises data/models data/admin cookies
docker compose pull
docker compose up
Compose 默认使用目录映射:
./data/uploads -> /data/uploads
./data/downloads -> /data/downloads
./data/outputs -> /data/outputs
./data/summaries -> /data/summaries
./data/exercises -> /data/exercises
./data/models -> /data/models
./data/fonts -> /data/fonts
./data/admin -> /data/admin
./cookies -> /data/cookies
字体文件可以通过页面上传,也可以直接放入 data/fonts/ 后在页面点击刷新。支持 .ttf、.otf、.ttc、.otc。
管理入口为 /#/_manage,首页和工具导航不显示入口,管理接口不出现在公开的 OpenAPI 文档中。后台可管理 Bilibili Cookie、刷新令牌和保活参数,以及大模型 API Key、接口地址、模型、超时与站点分享地址。
首次使用时,在服务器创建管理员;没有默认密码,也没有公开注册接口:
# Compose 部署(先启动包含后台功能的新镜像)
docker compose exec app python -m backend.app.admin_auth setup --username admin
# 本地开发,在项目根目录执行;DATA_DIR 须与后端进程一致
.venv/bin/python -m backend.app.admin_auth setup --username admin
命令会交互输入并确认密码,至少 12 个字符。Compose 部署后访问 http://localhost:8005/#/_manage,使用创建的账号登录。忘记密码时,将命令中的 setup 改为 reset;重置和后台修改密码都会使全部管理会话失效。
配置保存后生效,无需改 .env 或重建容器;已保存的配置优先于环境变量,重启后仍然保留。大模型新调用使用新的连接设置;正在进行的调用继续使用原连接。基础设施和本地识别模型的配置(数据目录、端口、ASR 后端等)仍由部署配置控制。
管理员密码采用带随机盐的 PBKDF2-SHA256 哈希,登录会话由服务器保存,8 小时过期,支持退出撤销。Cookie 使用 HttpOnly、SameSite=Strict,HTTPS 下启用 Secure;修改配置需要 CSRF 校验和同源校验,登录失败有频率限制。已保存的 Cookie、令牌、API Key 不会通过管理接口回显。隐藏入口用于减少曝光,实际访问权限由后端鉴权控制。
持久保留 data/admin/(管理员、会话及设置)和 cookies/(Bilibili 凭据),Compose 已挂载。密钥保存在服务端文件中,数据库和凭据文件权限为 0600,管理员目录为 0700。生产环境通过 HTTPS 访问;反向代理应保留原始 Host 并正确传递协议,使同源校验和 Secure Cookie 正常工作。
Bilibili 下载和字幕接口共用一份登录态。服务启动时检查 Cookie,之后默认每 24 小时检查一次;Bilibili 返回需要刷新时,自动执行 Web Cookie 续期。临时网络错误和风控响应保留凭据,默认 5 分钟后重试;账号确实失效时提示重新登录。
推荐在隐藏管理后台的「Bilibili 账号」页导入凭据:
SESSDATA,自动续期还需 bili_jct,建议保留 DedeUserID、buvid3、buvid4。https://www.bilibili.com,复制 ac_time_value,填入「刷新令牌」。Cookie 和令牌必须来自同一次登录,仅导出 Cookie 无法续期。重新登录后,在后台重新导入 Cookie 和对应的刷新令牌。更换登录态时,未同时提交新令牌会清除原令牌,避免混用;仅补充令牌可保留原 Cookie。移除账号后,旧环境变量不会重新恢复账号。
已有部署也兼容直接挂载 Netscape Cookie 文件:
mkdir -p cookies
# 将导出的 Bilibili cookies 保存为 cookies/bilibili.cookies.txt
docker compose up
也可以直接传浏览器请求里的原始 Cookie header:
docker run --rm \
-p 8000:8000 \
-v video2emoticon-data:/data \
-e BILIBILI_COOKIE_HEADER='SESSDATA=...; bili_jct=...; DedeUserID=...' \
video2any
还支持把 Netscape cookies 文件内容放到 BILIBILI_COOKIES 环境变量中。环境变量仅在 Cookie 文件不存在时初始化文件;已有文件优先,避免每次请求或重启用旧 Cookie 覆盖续期结果。原始 Cookie header 也只在文件中没有登录态时使用。在后台导入或移除账号后,以后台保存的登录态为准。
如果尚未通过后台管理账号,仍可用环境变量初始化刷新令牌和默认参数:
BILIBILI_REFRESH_TOKEN=同一次登录的ac_time_value
BILIBILI_KEEPALIVE_ENABLED=true
BILIBILI_CHECK_INTERVAL_SECONDS=86400
BILIBILI_RETRY_INTERVAL_SECONDS=300
docker compose up -d --force-recreate app
后台显示缓存状态,不触发上游请求;「检查并续期」才主动检查。healthy 表示检查成功且具备刷新令牌,missing_refresh_token 表示当前有效但无法续期,refresh_required 表示需要补充令牌,expired 表示需要重新导入凭据,retrying 表示临时失败。刷新返回 86095 时先重新获取刷新口令再重试,持续失败按短间隔重试。旧 /api/bilibili/session 接口也需要管理员登录。本地 .env 不会自动读取,但可以完全通过后台保存这些运行设置。
新 Cookie 和匹配的新令牌会一起原子保存到 Cookie 文件旁的 bilibili.cookies.session.json,再写回 bilibili.cookies.txt 并确认刷新;确认请求失败后会使用已保存的新登录态继续恢复。后台检查与业务请求通过线程锁和文件锁协调,多个进程不会同时轮换令牌。下载任务使用独立临时 Cookie 文件,完成后删除,避免 yt-dlp 将旧凭据写回主文件。Compose 中的 cookies/ 挂载需要可写并保留到下次部署。
服务会优先使用保存的续期结果,因此无需每次刷新后修改 .env。Bilibili 密码变更、手动退出或风控强制下线仍需要重新登录,自动续期无法恢复已被撤销的会话。
可在容器内手动检查一次(输出仅包含状态):
docker compose exec app python -m backend.app.bili_auth
HTTP 412 也可能由网络出口或请求频率触发,不会直接被当作账号失效。续期协议参考 bilibili-API-collect 和 defy997/bilibili-api;凭据保存与网络故障处理同时参考 bilibili-cli。不要把 Cookie 或会话文件提交到 GitHub;cookies/ 和 data/ 已加入 .gitignore。
后端:
python3 -m venv .venv
. .venv/bin/activate
pip install -r backend/requirements.txt
uvicorn backend.app.main:app --reload --host 0.0.0.0 --port 8000
前端:
cd frontend
npm install
npm run dev
前端开发服务默认代理 /api 到 http://127.0.0.1:8000。
前端使用 React + TypeScript + Vite,UI 组件库为 Radix UI Themes。全局主题在 frontend/src/main.tsx 配置,品牌色和组件样式在 frontend/src/ui.css;表单、按钮、弹窗和进度条统一使用 Radix 组件。UploadField 封装视频选择与拖拽上传,FieldSelect 封装下拉菜单,RangeInput 使用 Radix Slider 处理拖动与键盘操作。
.github/workflows/docker.yml 会在以下场景构建镜像:
main 或 masterv* tagworkflow_dispatch非 pull request 事件会推送到:
ghcr.io/<owner>/video2any:latest
ghcr.io/<owner>/video2any:<branch-or-tag>
ghcr.io/<owner>/video2any:sha-<commit>
部署时挂载 /data:
docker run -p 8000:8000 -v video2emoticon-data:/data ghcr.io/<owner>/video2any:latest
如果用 Compose 部署,按本仓库的 docker-compose.yml 会映射到当前目录下的 data/ 和 cookies/。
Bilibili、OPENAI_*、SUMMARY_MAX_INPUT_CHARS 和 SITE_URL 均可在管理后台保存;以下环境变量保留为部署默认值或旧部署的初始化来源。后台已保存的字段优先于环境变量。
DATA_DIR:运行时数据目录,默认 /data。FRONTEND_DIST:前端静态文件目录,镜像内默认 /app/frontend/dist。FONT_FILE:FFmpeg drawtext 使用的字体文件路径。/data/fonts:页面可扫描的字体目录,用于文字层字体选择和预览。CORS_ORIGINS:开发时允许的跨域来源,默认 *。BILIBILI_COOKIES_FILE:Netscape 格式 Bilibili cookies 文件路径,推荐挂载到 /data/cookies/bilibili.cookies.txt。BILIBILI_COOKIE_HEADER:浏览器请求里的原始 Cookie header,例如 SESSDATA=...; bili_jct=...。BILIBILI_COOKIES:Netscape cookies 文件内容,适合通过部署平台 Secret 注入。BILIBILI_REFRESH_TOKEN:Web 登录刷新令牌,对应同一登录的 localStorage ac_time_value;自动续期所需,刷新后优先使用持久化的新令牌。BILIBILI_KEEPALIVE_ENABLED:自动检查和续期,默认 true;设为 false 可关闭后台及请求前的检查。BILIBILI_CHECK_INTERVAL_SECONDS:登录态检查间隔,默认 86400(24 小时),最小 3600;启动及业务请求共用检查结果。BILIBILI_RETRY_INTERVAL_SECONDS:网络或续期临时失败后的重试间隔,默认 300,最小 60。OPENAI_API_KEY:大模型 API Key 的可选初始值;可直接在后台填写,未配置有效密钥时总结页会返回 503。OPENAI_BASE_URL:大模型 OpenAI 兼容接口地址,默认 https://api.deepseek.com。OPENAI_MODEL:模型名,默认 deepseek-chat。OPENAI_TIMEOUT:大模型响应读取等待超时(秒),默认 300;连接和连接池等待为 10 秒,发送请求为 30 秒。SDK 对超时、连接错误及部分 HTTP 错误最多自动重试 2 次,整个调用耗时可能超过此设置。OPENAI_REASONING_EFFORT:可选思考深度。未配置时,glm-5.3 系列自动使用 low;其他模型保持服务商默认值。可按模型能力设为 low、high、max。OPENAI_THINKING_TYPE:可选 enabled / disabled,空值保留服务商默认。仅用于支持该参数的模型。GLM-5.3 系列不能关闭思考,设置 disabled 时会提示改用 low 或切换模型。OPENAI_REASONING_MAX_TOKENS:GLM-5.3 系列首次调用的输出 token 预算下限,默认 16384,为思考与正文共同预留空间;截断修正时最多提高到该值的两倍。预算是上限,实际使用量以响应 usage 为准。OPENAI_JSON_MODE:练习题流程默认请求 json_object 输出;设为 false 可关闭。服务商明确拒绝 JSON 模式时自动回退,同一模型后续请求复用回退结果。SUMMARY_MAX_INPUT_CHARS:总结分段时每段的字幕字符预算,默认 9000(越长越完整,但更慢更费 token)。出题及复核输出比普通视频总结长,默认给模型 300 秒的响应读取等待时间。可在后台「大模型连接」将响应等待时间调整为 300 秒,保存后生效。尚未通过后台保存时,也可将 .env 的 OPENAI_TIMEOUT=60 改成 OPENAI_TIMEOUT=300,并执行 docker compose up -d --force-recreate app 应用环境变量;单纯 restart 不会更新环境变量。
LLM 日志包含本次调用标识、读取超时、最大重试次数、总耗时和错误类型:APITimeoutError 表示超时,status=429 表示限流或额度问题,status=5xx 表示上游服务错误。LLM output validation failed; regenerating once 表示返回的 JSON / 题目结果不符合要求后重新生成,与网络重试不同;正常出题后的答案复核也会再调用一次模型。
finish_reason=length 表示输出达到 token 上限,练习题流程不会接受该次结果;修正请求携带上次返回内容,提高 token 预算并要求压缩描述、重新输出完整 JSON,最多修正一次。日志还会显示 reasoning_chars、completion_tokens、reasoning_effort 和 thinking,便于区分思考耗时、正文输出与截断。知识点提炼失败后点击重试会复用已完成的转写。
GLM-5.3-Flash 只能启用思考,可以用 low 降低深度;GLM-4.7 系列支持非思考模式,具体以模型说明和接口参数为准。若希望使用 GLM-4.7-Flash 的非思考模式,可在包含这些配置的新版本中设置:
OPENAI_BASE_URL=https://open.bigmodel.cn/api/paas/v4
OPENAI_MODEL=glm-4.7-flash
OPENAI_THINKING_TYPE=disabled
OPENAI_REASONING_EFFORT=
也可在管理后台设置上述服务地址、模型和思考模式,保留现有智谱 API Key 后保存,无需重新创建容器。如果使用环境变量,执行 docker compose up -d --force-recreate app。这些配置需要包含对应代码的新镜像支持。
镜像内置:
Bilibili 下载取决于部署环境网络可达性和 cookie 有效性。部分视频如果需要会员、地区权限或 cookie 过期,yt-dlp 可能无法下载,后端会把失败原因返回给前端。
/#/exercises 支持上传讲课视频,或输入 B 站 BV / URL 并选择单个分 P。下载或上传完成后,在服务端通过 .env 的 ASR_BACKEND 选择 SenseVoice-Small 或 Qwen3-ASR-0.6B 识别中文语音,再使用现有 OPENAI_* 配置提炼知识点。默认 SenseVoice-Small,保留 Whisper 兼容模式。页面只显示服务端配置的模型。修改 .env 后重新创建容器并刷新页面,新识别任务使用新模型。
试题地址形如 /#/exercises/papers/er-ci-fang-cheng,根据题目关联的知识点转为拼音;同名试题使用 -2、-3 等序号区分。首页显示最近生成的试题,出题工作台提供可翻页的全部记录;点击标题即可回看、复制地址和打印。每个页面保留题目及生成时知识点的独立快照,课程修改、重新出题或原视频缓存过期不会改变已经保存的页面。已有题目批次会在服务启动时自动补入记录,重复保存相同标题和选题会复用页面地址。
练习卷与答案解析按相同顺序连续编号,使用 A4 黑白排版;练习卷保留答题空间,打印时隐藏导航、页面地址与操作按钮。网页支持 Markdown 与 LaTeX,包括分式、根号、上下标、积分、求和、矩阵与分段函数。使用 $...$ / $$...$$,不支持自定义宏或 LaTeX 绘图。候选题只预览和勾选,不提供题目编辑器;可以重新生成独立批次,旧题目及其知识点版本会保留。
长视频按完整转写分段提炼,不截掉后半段。只分析语音文字,不自动识别 PPT 或板书画面;可手动补充。题数少于所选知识点时,从选定范围均匀取点。出题跨知识点合并,每批最多 5 道,并逐批复核答案和解析;普通 10 道题共 4 次大模型调用(不计校验修正或接口重试),原文过长时会自动拆成更小批次。本次出题任务的后续批次和复核共用对话 history,携带此前返回的完整题目、答案及解析;复核直接读取上一条 assistant 消息,避免再次拼接题目正文。跨批重复题、题型顺序或知识点分配不符会触发一次修正。history 会随请求作为输入发送,输入计费以服务商规则为准。后台日志显示计划批次数、调用次数及每次请求的 history 消息数。仍建议在打印前查看候选题。
转写和出题为后台任务,页面显示阶段及进度,刷新后恢复最近课程;页面地址中的 lesson 参数也可以重新打开该课程。错误可重试,同一模型已完成的转写会复用。切换模型后,点击“使用新模型重新识别”,会重新转写并提炼知识点,更新内容版本,保留旧题目批次;会替换当前转写及手动修改,需原视频文件存在。任务使用提交时的模型,运行中不可切换。切换识别失败时保留旧内容;识别成功但知识点提炼失败时,重试复用新模型的转写。课程文字、知识点、题目批次与试题页面保存在 /data/exercises/exercises.db。课程关联的上传视频及 B 站视频随课程保留,服务重启或超过 24 小时不访问均不会自动清理;其他原视频缓存仍在 24 小时未使用后清理,处理中的原视频也不会被清理。服务重启会把未完成任务标记为中断,可手动重试。
部署时同时持久挂载 /data/uploads、/data/downloads 和 /data/exercises(Compose 已配置)。视频元数据中的文件路径按当前数据目录解析,兼容旧的绝对路径记录。升级不会恢复此前已被清理的视频文件;这类视频需要重新上传。
环境变量(Compose 已传入):
ASR_BACKEND:sensevoice(默认)、qwen3 或 whisper,所有新识别任务采用此配置;页面不能覆盖服务端选择。ASR_MODEL_SOURCE:新模型下载源,默认 modelscope,也支持 huggingface。SENSEVOICE_MODEL:默认 iic/SenseVoiceSmall,可传本地模型目录。QWEN_ASR_MODEL:默认 Qwen/Qwen3-ASR-0.6B-hf,可传本地模型目录。使用官方 Transformers 原生 -hf 权重(同为 0.6B),本地目录也需对应此版本。ASR_VAD_MODEL:默认 iic/speech_fsmn_vad_zh-cn-16k-common-pytorch,两个中文模型共用,可传本地目录。ASR_MODEL_DIR:新模型缓存根目录,默认 DATA_DIR/models;ModelScope、Hugging Face 缓存分开保存。ASR_DEVICE:新模型推理设备,默认 cpu;本地支持 PyTorch 的设备如 cuda:0、mps。默认 Docker 镜像安装 CPU 版 PyTorch,GPU 部署需自行安装匹配运行时。ASR_CPU_THREADS:新模型 CPU 线程数,默认 4,所有转写串行执行,每次只保留一个识别模型以控制内存。ASR_SEGMENT_SECONDS:语音段最长秒数,默认 25,范围 5–30。SenseVoice / Qwen 时间戳来自 VAD 的原视频语音段位置,适合内容回看,不是逐字强制对齐。WHISPER_MODEL:兼容模式模型,默认 small;也可传本地 CTranslate2 模型目录。HF_ENDPOINT:Hugging Face 下载地址,默认 https://hf-mirror.com;可设为 https://huggingface.co 使用官方源。不影响 ModelScope 下载。HF_HUB_DISABLE_XET:默认 1,关闭 Xet/CAS 文件重建,避免镜像下载仍访问 cas-server.xethub.hf.co 而报 401。需要在 Python 进程启动前设置;官方网络下可显式设为 0。WHISPER_CPU_THREADS / WHISPER_MODEL_DIR:兼容模式线程数 / 缓存目录,默认 4 / DATA_DIR/models。EXERCISE_MAX_INPUT_CHARS:知识点提炼每段原文预算,默认 9000。两个中文模型均使用本地推理,无需外部转写 API。Qwen 在 CPU 上通常比 SenseVoice 慢,也需要更多内存;首次使用下载对应识别模型和小型 VAD 模型,后续复用持久缓存。长视频先用 FFmpeg 提取 16 kHz 单声道音轨,再按 VAD 片段识别,保留原视频时间位置。知识点与出题需要有效的 OPENAI_API_KEY。
切换示例(已运行包含新模型依赖的镜像时):
ASR_BACKEND=qwen3
ASR_MODEL_SOURCE=modelscope
HF_HUB_DISABLE_XET=1
保存 .env 后执行 docker compose up -d --force-recreate app,然后刷新页面。切回 SenseVoice 将 ASR_BACKEND 改为 sensevoice。环境变量固定在任务提交时,服务重启前正在进行的任务会被标记为中断,可重试。
Docker 镜像包含新模型依赖、CPU PyTorch、Pandoc 和 Noto 中文字体。Compose 持久挂载 ./data/exercises 与 ./data/models。模型切换的代码变更需要使用包含本次更新的新镜像;本地从源码构建可运行:
docker build -t video2any:local .
# 将 docker-compose.yml 的 image 改为 video2any:local 后:
docker compose up -d --force-recreate app
若旧版本 Whisper 下载出现 File reconstruction error / CAS Client Error / 401 Unauthorized,可先在 .env 增加 HF_HUB_DISABLE_XET=1,并在 Compose 的 environment 中增加 HF_HUB_DISABLE_XET: ${HF_HUB_DISABLE_XET:-1},然后运行 docker compose up -d --force-recreate app 再重试。此变量由 Hugging Face 官方支持:环境变量说明。只修改 .env 而不传入容器不会生效;docker compose restart 不会应用新的环境变量。已有正常模型缓存无需删除。
若提示 Network is unreachable,检查容器到下载源的网络。新模型默认使用 ModelScope;可改为 ASR_MODEL_SOURCE=huggingface 并配置 HF_ENDPOINT,或者配置可达的代理。离线部署需预先准备识别模型和 VAD 模型完整目录,放到 ./data/models 下,并使用容器路径,例如:
ASR_BACKEND=sensevoice
SENSEVOICE_MODEL=/data/models/SenseVoiceSmall
QWEN_ASR_MODEL=/data/models/Qwen3-ASR-0.6B
ASR_VAD_MODEL=/data/models/fsmn-vad
HF_HUB_DISABLE_XET=1
本地开发建议 Python 3.12,安装 backend/requirements.txt。本地 .env 不会自动读取,启动时传入环境变量,例如 ASR_BACKEND=qwen3 HF_HUB_DISABLE_XET=1 uvicorn backend.app.main:app --reload。
兼容旧版 Word 导出接口还需安装 Pandoc 和 Noto Serif CJK SC 字体(Linux 可安装 pandoc fonts-noto-cjk,macOS 可使用 Homebrew 安装 Pandoc 并安装 Noto 中文字体)。请保持单个 Uvicorn worker;本模块的工作池及清理保护由单进程管理。
验证:
.venv/bin/pip install pytest httpx
.venv/bin/python -m pytest backend/tests
cd frontend
npm run build
Word 集成测试需要 Pandoc;也可在测试环境安装 pypandoc_binary,不影响生产依赖。