# Emby 多域名接口 emby_ext_domains
> UHD 开源的 Emby 扩展服务：为 Emby 服务端补上 ServerDomains 多域名接口，客户端登录后自动获取全部线路。含接口定义、Emby token 鉴权顺序、Nginx 部署要点与客户端对接建议。

> [!NOTE]
> `emby_ext_domains` 是 UHD 开源的一个 Emby 生态扩展服务：给 Emby 服务端补上一个「可用域名列表」接口，客户端登录后就能自动获取该服务端的全部线路域名，不必再让用户手动改服务器地址。仓库地址：[github.com/uhdnow/emby_ext_domains](https://github.com/uhdnow/emby_ext_domains)，MIT 许可，Go 实现。

## 它解决什么问题

Emby 协议里没有「一个服务端有多个域名」的概念——客户端只认登录时填的那一个地址。但真实运营中域名是会变的：新增边缘节点、线路故障切换、老域名不可用，都需要用户手动去客户端里改地址，成本很高。

这个扩展接口把这件事收归服务端：

* 服务端维护一份域名列表，随时可增删
* 客户端登录后调用一次接口，就拿到完整线路列表
* 用户在客户端里直接切换线路，不需要重新输入地址、也不需要重新登录

目前已有多款三方 Emby 播放器实现了这个接口，也有不少 Emby 服务端基于它对外提供多域名订阅与同步服务。

## 已支持该接口的三方客户端

SenPlayer、VidHub、Forward、Hills、AfuseKt、Flow、TVxEmby、CapyPlayer、LinPlayer。

> [!TIP]
> 客户端的具体入口叫法不一，常见的是「线路」「域名订阅」「服务器地址同步」之类的开关。UHD 用户可先看[客户端推荐](/usage/client)挑选客户端，再按[配置客户端](/usage/client-setup)完成登录。

## 接口定义

一个接口，挂在 Emby 的同源路径下：

```http
GET /emby/System/Ext/ServerDomains
```

成功响应（200）：

```json
{
  "data": [
    { "name": "Server 1", "url": "https://server1.example.com" },
    { "name": "Server 2", "url": "https://server2.example.com" }
  ],
  "ok": true
}
```

| 字段 | 类型 | 说明 |
| :-- | :-- | :-- |
| `ok` | boolean | 请求是否成功，客户端应以此判断 |
| `data[].name` | string | 线路显示名称，展示给用户 |
| `data[].url` | string | 线路完整地址，含协议与端口 |

鉴权失败返回 **401**：

```json
{ "error": "Invalid token", "ok": false }
```

`error` 有两种取值：`Token not found`（请求里没带 token）和 `Invalid token`（token 校验没过）。

## 鉴权：复用 Emby 自己的 token

接口不引入新的凭证体系，直接复用调用方已有的 Emby token。服务端按以下顺序取第一个非空值：

| 顺序 | 位置 | 键名 |
| :-- | :-- | :-- |
| 1 | Query | `X-Emby-Token` |
| 2 | Header | `X-Emby-Token` |
| 3 | Query | `api_key` |
| 4 | Header | `X-Emby-Authorization` 中的 `Token="..."` |
| 5 | Cookie | `Authorization` |
| 6 | Header | `Authorization` |
| 7 | Query | `token` |
| 8 | Cookie | `token` |
| 9 | Header | `token` |

覆盖这么多位置，是为了让不同框架写出来的客户端都能直接对接——无论它习惯把 token 放在查询参数、请求头还是 Cookie 里。

取到 token 后，服务端拿它去问 Emby 本体：

```http
GET {emby_server_url}/emby/System/Info?X-Emby-Token={token}
```

Emby 返回 200 即认为 token 有效，其余情况一律视为无效。参考实现的校验超时是 **3 秒**，且不缓存校验结果——每次请求都会回源验证一次。

## 部署参考实现

![Nginx 同域分流示意图：精确匹配 /emby/System/Ext/ServerDomains 转发到本地 52143 的 emby_ext_domains，其余路径交给 Emby 本体](/media/b2aab62a-3463-479d-b872-ae2613967960/deploy.webp)

配置文件 `config.yaml`：

```yaml
emby:
  server_url: "https://your-emby-server.com"  # Emby 本体地址
server:
  port: 52143                                  # 本服务监听端口
domains:
  - name: "Server 1"
    url: "https://server1.example.com"
  - name: "Server 2"
    url: "https://server2.example.com"
```

仓库带了 Dockerfile 和 docker-compose.yml，直接起：

```bash
docker compose up -d
```

关键一步是反向代理：这个接口必须和 Emby **同域同路径**对外，客户端才会用同一个 base URL 请求到它。在 Emby 的 Nginx 配置里加一条精确匹配：

```nginx
location = /emby/System/Ext/ServerDomains {
    proxy_pass http://127.0.0.1:52143;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass_request_headers on;   # token 相关的头必须透传
    proxy_connect_timeout 5s;
    proxy_read_timeout 10s;
    proxy_send_timeout 10s;
}
```

> [!IMPORTANT]
> `proxy_pass_request_headers on` 不能省。token 可能藏在 `X-Emby-Authorization` 或 Cookie 里，代理层一旦丢头，接口就只会返回 `Token not found`。

部署时还有两点要注意：

* 本服务必须能访问到配置里的 Emby 地址，否则所有 token 都校验不过
* 域名列表在启动时从配置文件读入，改完需要重启服务生效

## UHD 的实现

UHD 生产环境没有直接跑这份参考实现，而是按同一套接口重新实现了一份，对外行为与本文描述保持对齐。所以：

* 对客户端开发者：按本文的接口对接即可，不需要为 UHD 单独适配
* 对 UHD 用户：线路列表由服务端维护并下发，客户端支持的话就能直接看到全部可用线路

## 给客户端开发者的建议

* 登录成功后再调用，此时才有可用 token
* 以 `ok` 字段判断成功与否，不要只看 HTTP 状态码
* 接口不存在（404）或超时是常态——大量 Emby 服务端并没有部署这个扩展，此时应静默沿用用户当前填写的地址，不要报错打断使用
* 拿到列表后本地缓存，不必每次进入 App 都请求
* 不要把 token 写进日志、崩溃上报或截图
