为什么要折腾这一步

我的短剧流水线分三段:IPloom 存剧本和提示词,文本交给 DeepSeek,画面交给 Muse,生产管理放在 ArcReel。DeepSeek 早就通过 Magpie(本机的模型网关,OpenAI 兼容)接好了,Muse 一直是单独开网页用。

目标很简单:所有模型都收进 Magpie 统一调度,下游工具只认一个地址。

先排除两条路

1. 现成的 magpie-muse-web 插件。 它确实存在,但只做文本进、文本出。它自己的 README 也写了:要图片和视频,去用 muse2api 加对应插件。

2. 社区的两个 Muse Code 插件。

  • magpie-muse-bridge:走官方 Muse Code SDK 和 muse serve,接口是 Anthropic Messages。作者写明只用假上游测过,没用真实订阅账号测过。
  • magpie-muse-direct:伪装成 Muse Code CLI,用公开的 OAuth client id 和伪造的请求头直连 Meta 接口。README 自己警告:大概率违反条款,账号或订阅可能被限制甚至封禁。

这两个都只管 Muse Code 的对话和工具调用,没有视频任务的提交、轮询、取回接口,满足不了 ArcReel 生视频的需求。我两个都没装。bridge 留作以后把 Muse 当 Agent 用时再评估,direct 不建议长期用。

还要澄清一个常见误解:按 Meta 文档,Muse Spark 能接收视频输入,但输出是文本,它本身不生成视频。生视频走的是 muse.ai 网页端的能力。

最终架构

链路就一条:muse.ai → muse2api(Docker)→ Magpie → 下游工具(封面图即架构图)。

  • muse2api:用容器内的 Chromium 驱动 muse.ai 网页,维持登录态,对外提供 OpenAI 兼容的对话、图片、视频接口。端口 18610,模型 muse-spark、muse-image、muse-video。
  • magpie-muse2api-plugin:Magpie 插件,provider id 是 muse2api,默认上游 http://127.0.0.1:18610/v1。
  • 插件会过滤掉媒体模型,Magpie 里只出现 muse2api/muse-spark。图片和视频不走 Magpie,直接调 muse2api 自己的异步任务接口。

部署:从原生改成 Docker

第一版是原生跑的:Python 虚拟环境加 macOS 的 LaunchAgent,监听 127.0.0.1:18610。能用,但我想要隔离、好迁移,于是改成 Docker。

compose 在上游基础上改了几处:只绑本机回环、restart 改为 unless-stopped、密钥放 .env、浏览器 profile 单独挂卷。

name: muse2api
services:
  muse2api:
    build:
      context: ./source-<commit>
      dockerfile: Dockerfile.local
    image: local/muse2api:<commit>
    container_name: muse2api
    restart: unless-stopped
    ports:
      - "127.0.0.1:18610:18610"   # 上游是 "18610:18610",会暴露到局域网
    env_file: .env                # MUSE2API_KEY=<你的 API Key>
    volumes:
      - ./data:/app/data
      - profiles:/root/muse2api-profiles
    shm_size: 2gb                 # Chromium 需要
volumes:
  profiles:

上游镜像基于 python:3.11-slim-bookworm,装 Chromium、中文字体、curl、git,Python 依赖是 FastAPI、uvicorn、pydantic、requests、websocket-client。

切换脚本保证不中断:先停原生服务,再 compose up,检查 /v1/models 里有 muse-video、Chromium 起来了,成功就禁用原生的 LaunchAgent,失败就回滚。地址没变,Magpie 那边一行配置都不用改。

接入 Magpie

magpie plugin add <插件目录>
magpie plugin login muse2api
# 选 2:连接已有实例
# Base URL: http://localhost:18610/v1
# Key: <你的 API Key>

插件登录有 4 种方式:自动部署、连接已有实例、连接并导入账号、只填 key。我已经有实例,选 2。

我当时也问过:这个 API Key 从哪来? 它不是找 Meta 申请的,是你自己在 muse2api 的 .env 里设置的 MUSE2API_KEY,只用来保护本机这个服务。

导入 Muse 账号

muse.ai 的 4 个核心 Cookie 都是 httpOnly,网页脚本读不到。muse2api 给了两种办法:浏览器扩展(MV3,权限是 cookies、storage、activeTab),或者走 CDP 的取 Cookie 脚本。

我不想另开一个专用浏览器重新登录,就用扩展复用已登录的 Chrome:

  1. 复制一份官方扩展,把 host_permissions 收窄到只允许 muse.ai 和 127.0.0.1,上游地址写死成本机实例。
  2. 打开 chrome://extensions,开启开发者模式,加载已解压的扩展。
  3. 打开已登录的 muse.ai,粘贴 API Key,点「读取并导入」。

结果:账号池 1 个账号,状态是已启用、临近到期,但可以用。muse2api 自带续期机制,会定期刷新。

实测

文本,已通过。 向 Magpie 的 /v1/chat/completions 发请求,模型选 muse2api/muse-spark,让它原样回一个随机串:

curl http://localhost:3425/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"muse2api/muse-spark",
       "messages":[{"role":"user","content":"只回复:<随机串>"}]}'

返回 HTTP 200,内容一致。整条链路 Muse → muse2api → Magpie 通了。

图片,进行中。 走的是 muse2api 的异步接口:

# 提交
curl -X POST http://localhost:18610/v1/images/tasks \
  -H "Authorization: Bearer <你的 API Key>" \
  -H "Content-Type: application/json" \
  -d '{"model":"muse-image","prompt":"一只红色玩具帆船","aspect_ratio":"16:9","response_format":"url"}'
# 轮询
curl http://localhost:18610/v1/images/tasks/<task_id> -H "Authorization: Bearer <你的 API Key>"

任务状态有 queued、processing、completed、failed 四种。Muse 本身不提供百分比,进度只是几个观察到的节点。我这次跑到 processing 40%,写这篇时还没出最终结果。

视频,还没测。 计划用上一步的图做首帧,生成 4 秒 16:9 视频,接口是 POST /v1/videos,再轮询 GET /v1/videos/{task_id}。

踩坑记录

  1. 只看模型列表,漏了现成插件。 第一次检查只看了 Magpie 的模型列表,没看插件目录,以为没人做过。查了才发现插件有,只是只支持文本。
  2. Debian 源 502。 第一次构建镜像,deb.debian.org 在 libxfixes3、libgl1 上返回 502,大约 182 个包、220MB 下载。解决:写一个 Dockerfile.local,把源换成 HTTPS 的 USTC 镜像,apt 加重试,再用一个小脚本并行下载 .deb,带重试和大小校验。之后构建通过,Chromium 156。
  3. 管理页 401。 管理页的更新和查询一直 401,原因是管理页没存对 key。用上游较新的提交重建镜像(1.6.0 → 1.6.7,新 Dockerfile 多复制了一个 proxy.py),之后管理接口 200,检查更新显示已是最新。
  4. Chrome 不让自动填 key。 想用 AppleScript 往页面注入 JS 填 key,但 Chrome 默认关闭了「允许 Apple 事件中的 JavaScript」。改成把 key 放进剪贴板,在管理页和扩展里手动粘贴。
  5. ArcReel 容器访问宿主机。 ArcReel 跑在 Docker 里,访问宿主机服务要用 host.docker.internal:18610,我实测能通(200)。ArcReel 支持声明式的自定义供应商(提交 → 轮询),正好对得上 muse2api 的任务接口,但我还没配完。

限制和风险

  • 这是非官方的逆向接口。 muse2api 靠浏览器自动化驱动 muse.ai 网页,不是 Meta 提供的 API。网页一改版就可能失效,也可能违反 Muse 的服务条款。账号被限制或封禁的风险由你自己承担。建议用不重要的号,不要连主力邮箱和支付。
  • 登录态就是账号。 导入的 Cookie 等于账号的完整权限。扩展要收窄权限,实例只绑 127.0.0.1,.env 不进 Git。
  • Magpie 只管文本。 图片和视频要直接调 muse2api,下游工具需要支持「提交 + 轮询」的异步模式。
  • 没有精确进度。 长任务只能轮询状态,超时要设宽:上游默认图片 240 秒、视频 600 秒、对话 300 秒。
  • 账号状态要盯。 我的账号导入时就显示临近到期,续期失败时链路会直接断。

下一步

等图片任务出结果,跑一遍图生视频,再在 ArcReel 里用声明式供应商把 muse-image 和 muse-video 接进去。跑通后再写一篇。


说明:本文按我自己的操作记录整理。文本链路已实测通过;图片只跑到 processing 40%,视频和 ArcReel 接入还没验证,不代表能用。所有 key、地址都已替换成占位符。