Skip to content

1.9.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 15:44
· 32 commits to main since this release

发布说明摘自 CHANGELOG.md 的 1.9.0 一节。

变更

  • Emby 版本行标题位改由聚合层供给(契约变更记录见 docs/emby-compat.md):
    版本行的标题位(MediaSources[].Name 与视频流 DisplayTitle)不再由 emby 层自己拼,
    改为直接读聚合层写好的 versionLabel,出口插件(FW/Rex)读同一个字段 —— 规则只实现一次。

    • 改的是什么:标题位格式不变(仍是 [体积] 站点标签 · 线路flag [· 变体标注] [· 项标注]),
      拼装从 emby 层下沉到聚合层;emby 层不留旧拼装兜底。聚合层同步给详情站条目补上 sourceName。
    • 影响哪些端点:POST /api/emby/Items/{ItemId}/PlaybackInfo、
      GET /api/emby/Users/{UserId}/Items/{ItemId}(详情)的 MediaSources。
    • 对客户端的影响:多源命中时标题多出源名前缀(形如 源名 站点标签 · 线路)——
      此前聚合层详情站条目不带 sourceName,这条「多源前置源名」实际从未生效;现生效,属新可见文本。
      单源场景标题不变。客户端无需改动(只当显示名读)。agg 详情缓存 key 版本号随之升级
      (aggdetail4 → aggdetail5)。
    • 决策见 ADR-0063(与
      ADR-0043 同一模式)。
  • Emby「接下来看」端点改为对外恒空(契约变更记录见 docs/emby-compat.md):
    GET /api/emby/Shows/NextUp 保留端点、不删路由、不加开关,但响应恒为
    200 {Items:[], TotalRecordCount:0}。

    • 改的是什么:客户端首页的「继续观看」(Items/Resume)与「接下来看」(Shows/NextUp)
      在常规顺序观看下常指向同一集、两行重复;现让 Shows/NextUp 不再出数据,藏掉那一行。
      算「该看哪一集」的实现留着但暂不调用(恢复时改回即可)。鉴权口径不变(无 token → 401)。
    • 影响哪些端点:仅 GET /api/emby/Shows/NextUp(含带 SeriesId 的请求)。
      Items/Resume、Items?Filters=IsPlayed 等不受影响。
    • 对客户端的影响:只认 Shows/NextUp 渲染「接下来看」那一行的客户端将不再显示该行;
      「继续观看」仍由 Resume 提供。客户端无需改动(它本就该容忍空列表)。
    • 决策见 ADR-0060(取代 ADR-0023 的
      Shows/NextUp 那一格)。
  • Emby 播放地址由绝对 URL 改为相对路径(AfuseKt 起播 404)
    (契约变更记录见 docs/emby-compat.md):

    • 改的是什么:DirectStreamUrl(PlaybackInfo)与 MediaSources[].Path(详情 / PlaybackInfo)
      此前给绝对 URL({proto}://{host}/api/emby/…),现改为相对路径
      (/videos/…?…&Static=true、/Items/…),对齐真机形态。连带去掉
      getItem / getPlaybackInfo / buildMediaSource 的 host / proto 参数与 protoOf 工具函数。
    • 影响哪些端点:POST /api/emby/Items/{ItemId}/PlaybackInfo、
      GET /api/emby/Users/{UserId}/Items/{ItemId}(详情)。
    • 对客户端的影响:AfuseKt/3.2.0 起播恢复。此前它把面板给的绝对地址当相对路径再拼在
      自己的 base(已含 /emby)之后,得到双重拼接的畸形地址 → 实例端口 404;改为相对后两种 base
      都能命中。走官方协议的客户端无需改动。
    • 决策见 ADR-0062;真机逐条见
      docs/emby-realdevice/11-items-playbackinfo.md(未复测)。

新增

  • Emby 收藏功能(契约变更记录见 docs/emby-compat.md):新增写端点
    POST|DELETE /api/emby/Users/{UserId}/FavoriteItems/{ItemId},并让读取类端点出真收藏数据。

    • 改的是什么:面板新增收藏存储 —— favorite 表(一行 = 账号 + 条目 + 收藏时的列表元数据快照,
      随 emby.db 持久化,属用户数据、不随缓存清理,SCHEMA_VERSION 4→5)。收藏动作时快照、读侧 0 上游请求。
    • 影响哪些端点:POST|DELETE …/FavoriteItems/{ItemId}(此前落 501 通配 → 现 200 + UserItemDataDto,
      IsFavorite 真值;无 token → 401,Id 认不出 / 反查失败 → 204 不写库);
      GET …/Items?Filters=IsFavorite(此前恒空 → 现读 favorite 表、快照重建出真实条目);
      UserData.IsFavorite 全链路(列表 / 详情 / 季集)由恒 false → 按收藏库查出的真值。
    • 对客户端的影响:客户端无需改动。收藏 / 取消收藏由「未实现」转为可用,首页「收藏」入口由空列表转为真实集合;
      可收藏条目类型对齐真机(电影 / 剧 / 季 / 集全认)。
    • 决策见 ADR-0058;真机逐条见
      docs/emby-realdevice/21-favorite-items.md(未复测,待端到端复核)。
  • 片源认证:插件认准的候选,面板直接采信:聚合层新增一条候选行级的认证标 ——
    片源插件在 search 返回的候选上写 vod_exact: true,表示「这一条已认准、就是目标作品」,
    面板见到就把它的分数直接记 1、不再判名字(其余流转一律不变:仍受分数线与条数上限约束,
    只因分最高排在最前、优先取详情)。这解决番号片的老问题:番号查询的名字相似度会被
    三四十字的长标题稀释、导致正确候选被名字闸门拒掉;而番号站自己按番号精确过滤过,
    「这条对不对」它比面板更清楚。契约在插件仓库
    media-bridge-plugins(docs/plugin-contract.md 第五节),
    本仓库只是消费方。missav 片源先行适配(番号精确过滤后的候选带 vod_exact)。
    决策见 ADR-0059。

  • Emby 认领旧版播放上报族 Users/{UserId}/PlayingItems/*(HamHub Android 进度一条未落)
    (契约变更记录见 docs/emby-compat.md):面板此前只实现新版族
    POST /api/emby/Sessions/Playing[/Progress|/Stopped];抓包发现 HamHub Android/1.0.0 走的是
    Emby 旧版族 POST|DELETE /api/emby/Users/{UserId}/PlayingItems/{ItemId}[/Progress]
    (ItemId 在路径、参数在 query、无 JSON body),此前全落 501 通配 → 播放进度一条没记、
    Resume / IsPlayed 拿不到真数据。现认领这一族的 3 条(开始 / 心跳 / 结束),
    把 path / query 拼成现有 recordPlayback 认的形状,落库口径与新版族完全一致(含 204 空体)。

    • 影响哪些端点:新增 POST …/PlayingItems/{ItemId}、POST …/PlayingItems/{ItemId}/Progress、
      DELETE …/PlayingItems/{ItemId}(此前落 501 通配)。
    • 对客户端的影响:走旧版族的客户端进度由「一条不记」转为正常入库,无需改动。
    • 已知限制:旧版族不带 RunTimeTicks ⇒ 面板拿不到时长,只能按位置记、「看完」可能判不出。
    • 真机逐条见 docs/emby-realdevice/22-users-userid-playingitems.md(未复测)。

修复

  • Emby 条目不再回空串 PremiereDate / Overview(客户端 FormatException: Invalid date format):
    插件没给首播日期时,条目 DTO 上此前会出现 "PremiereDate": "";客户端(如 Hills)对它做
    DateTime.parse(value),parse('') 直接抛 FormatException、整条响应解码失败(列表整个打不开)。
    真机拿不到就不含这个键(不是回空串)——现对齐:PremiereDate / Overview 改为拿不到就不挂键
    (口径同 DateCreated / DateModified)。数组类字段仍先铺 [],不变。

    • 影响哪些端点:一切补 baseItem 的条目 DTO —— Items 列表、Items/Latest、Items/Resume、
      详情、季 / 集、相似等。
    • 对客户端的影响:PremiereDate / Overview 由恒在(可能为空串)变为可能缺失;
      对空串做类型转换的客户端不再崩,其余客户端无感(无需改动)。Etag 哈希对 undefined / ''
      同化,值不变、无缓存抖动。
    • 决策见 ADR-0061(细化 ADR-0008);
      真机逐条见 docs/emby-realdevice/06-users-userid-items-latest.md(未复测)。
  • Emby 登录不认小写 pw(HamHub Android 登不上):POST /api/emby/Users/AuthenticateByName 的
    请求体取值此前只认固定拼写(Username/username、Pw/Password/password),客户端发全小写
    {"username":…,"pw":…} 时密码被读成空串 → 401 无效用户名或密码。请重试。。真机(.NET 反序列化)
    字段名大小写不敏感 —— 实测 OkEmby / itsmygo 发 Pw 与 pw 均登录成功。现对齐真机:Username / Pw
    (含兼容 Password)不再区分大小写。响应结构与错误文案不变,客户端无需改动。
    (契约变更记录见 docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/02-users-authenticatebyname.md)

  • Emby 登录不认 Authorization 头里的 appName:POST /api/emby/Users/AuthenticateByName
    取 appName 时此前只读 X-Emby-Authorization,客户端按官方文档把 Client="…" 放在 Authorization 里时
    被误判「缺 appName」→ 400 Value cannot be null. (Parameter 'appName')。真机两头都认 —— 实测
    OkEmby / nyamedia / 予初Emby:只发任一头 → 200 登录成功,两头都缺 → 400 登不上。现对齐真机:
    X-Emby-Authorization 缺失时回退读 Authorization。响应结构与错误文案不变,客户端无需改动。
    日志里的客户端标记([Client/版本])同步改为两个头都看(此前只认前者,缺了就退到 UA)。

  • Emby 登录不认授权头里不写引号的 Client=(部分客户端登不上):POST /api/emby/Users/AuthenticateByName
    解析 appName 时此前正则写死 Key="值"、只认带引号的写法,客户端发 Emby Client=SomeApp, …(省引号)
    就取不到 appName → 400。真机对引号可选 —— 实测 nyamedia 4.8.0.62:Emby Client="Filmly" 与
    Emby Client=Filmly 均 200 登录成功。现对齐真机:Client= / Device= / DeviceId= / Version= 的
    值引号可省(日志的客户端标记同一口径;从授权头取 Token= 的路径一并放宽)。响应结构与错误文案不变,客户端无需改动。
    (契约变更记录见 docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/02-users-authenticatebyname.md)

  • Emby 登录不认 query 里的 appName(Filmly / 网易爆米花登不上):POST /api/emby/Users/AuthenticateByName
    取 appName 时此前只看授权头。抓包发现 Filmly 把客户端名放在 query(?X-Emby-Client=网易爆米花 Android),
    授权头里只有 Device / DeviceId / Version、没有 Client=,于是被误判「缺 appName」→ 400
    Value cannot be null. (Parameter 'appName')。真机对同一形状的请求实测(nyamedia 4.8.0.62):头无 Client=

    • query 带 X-Emby-Client → 200 登录成功;头无 Client= + 无 query → 400。现对齐真机:头里取不到 Client=
      时回退读 query X-Emby-Client(参数名大小写不敏感)。响应结构与错误文案不变,客户端无需改动。
      (契约变更记录见 docs/emby-compat.md;真机逐条见
      docs/emby-realdevice/02-users-authenticatebyname.md)
  • Emby 路径大小写写错就落 501(AfuseKt 登不上):core/router.js 的路径匹配此前字面段大小写敏感,
    客户端把 /api/emby/Users/AuthenticateByName 写成全小写 authenticatebyname 就落 ANY /api/emby/*rest
    通配 → 501。真机(.NET 路由)大小写不敏感,故这类客户端在真机能登、在面板 501(实测 AfuseKt/3.2.0
    就把登录路径写成全小写)。现对齐真机:字面段大小写不敏感比对(:param / *wildcard 的取值照原样给)。
    影响面为全局(所有走该路由器的路径,含面板自身 /api/*):此前大小写写错会 404 / 501 的路径现在都能命中;
    方法与段数约束不变(段数不齐仍 404、方法不符仍 405)。此前正确大小写的请求行为不变,客户端无需改动。
    同轮删除冗余的 Videos 重复注册(现一条认下大小写两种),端点集合不变。
    (契约变更记录见 docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/02-users-authenticatebyname.md)

  • Emby 登录不认表单体(AfuseKt 登不上):core/http.js 的 readBody() 此前只认 JSON,
    客户端发 Content-Type: application/x-www-form-urlencoded、体为 Username=…&Pw=…&appName=…
    就解析失败 → 400(即便路径修好也登不上)。真机(.NET 模型绑定)JSON 与表单都读 —— 实测 AfuseKt/3.2.0
    的登录体就是表单。现对齐真机:readBody 按 content-type 分流,另认 application/x-www-form-urlencoded
    (未发 content-type 但形状像表单的也按表单解,否则照旧报 400)。响应结构、错误文案均不变,
    发 JSON 的客户端无需改动。(契约变更记录见 docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/02-users-authenticatebyname.md)

  • Filmly / 网易爆米花首页空白(Items 裸列表查询):客户端登录后打的
    GET /api/emby/Users/{UserId}/Items 不带 ParentId、也不带 SortBy/Filters/SearchTerm,
    只带 ExcludeItemTypes/StartIndex/Limit/Fields 这类通用参数 —— 此前落「其余查询 → 空」,
    于是首页一片空白(Items: []、TotalRecordCount: 0)。真机对照(予初Emby 4.9.5.0):这类查询
    真机回的是顶层库列表(26 个 CollectionFolder,与 GET /Users/{id}/Views 一字不差)——
    真机对「无 ParentId 且不递归」的 Items 默认回根节点的直接子级(要条目客户端须自带 Recursive=true)。
    现对齐真机:无 ParentId 的裸列表查询改为回顶层库列表(复用 Views 那支,条目形状、TotalRecordCount
    与 Views 一致);轮播推荐位(SortBy=IsFavoriteOrLiked,…)仍路由到 feed: 'random' 行,不受影响。
    不新增插件契约字段,插件无需改动;客户端无需改动(决策见
    ADR-0055,取代
    ADR-0054;契约变更记录见
    docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/05-users-userid-items.md)

  • Emby 端点带 ?api_key= 但无 X-Emby-Authorization 头时 500:取 token 时按名字读头,
    头缺失会拿到 undefined,undefined.match(...) 把整个请求打成 500
    ({"error":"Cannot read properties of undefined (reading 'match')"})—— 只带 api_key、
    不带授权头的客户端(实测就踩这条)在 Views / Items 等取 token 的端点上全中。现将取不到的
    头如实当「没有」,端点正常回数据或 401,不再 500。

  • 「装一个插件」卡的上传包选择框没有字段框样式:那个文件选择框此前只给里面的原生按钮写了样式,
    外圈既没有底色也没有描边、高度还比同行的输入框矮一截 —— 摆在卡片上像一颗孤零零的按钮。
    现按统一的字段框补齐(底色 / 描边 / 圆角 / 34px 高度,窄屏随其它输入框一起长到 40px),
    聚焦有描边与光晕,里面那颗按钮收进框内不再顶边。

  • 开机那一轮站点测速启动过早,撞上源还没起来:面板启动后排的那一轮固定「2 分钟后」开跑,
    而插件报的 ready 只是「入口加载完」,它托管的源实例(下包 / 起进程 / 绑端口)在其后串行启动 ——
    源还在 starting 时站点清单里缺这些实例的站点,好站被记成失败,还会被后续几轮按
    「上次失败就跳过」漏掉。现改为到点先看源起没起:还在起就每 30s 再看一眼,最多等到开机后
    10 分钟;等满了照跑(如实测、如实记,不再等)。判据只看源实例状态,不影响手动点的那一轮。
    (实现见 server/modules/agg/site-test.js)

  • 中继取块超时收尾漏成「未处理的 Promise 拒绝」:proxy 线路读满一块超时(readTimeout 先到)时
    调 res.body.cancel() 收尾,但没 await —— 此刻 res.arrayBuffer() 还占着流的锁,cancel() 回的是
    被拒的 Promise(非同步抛错),同步 try/catch 兜不住,于是每发超时都往日志刷一条
    TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is locked(服务本身不受影响,异常被全局拦截)。
    现改为 await,超时收尾不再漏告警。对外契约与中继取块行为不变,客户端无感。
    (实现见 server/modules/agg/stream.js)

  • Emby 拉流鉴权不认 query 里的 X-Emby-Token(客户端拉流 401):直连拉流端点
    (GET /api/emby/videos/{ItemId}/stream 等)取 token 时此前只认请求头与 query api_key,
    而客户端会把同一个 token 照着头名塞进 query(?X-Emby-Token=…,大小写照发),于是认不出
    → 401 token 校验不过:没带 token。现 service.tokenFrom() 的 query 兜底同时认 api_key 与
    X-Emby-Token
    。影响端点:一切靠 tokenFrom() 鉴权的读取 / 拉流端点;对客户端的影响:此前只带
    query token 的拉流由 401 转为正常,其余无感。
    (契约变更记录见 docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/12-direct-stream.md 的 12-5,未复测)

变更

  • Emby 补一条端点 GET /api/emby/System/Ping(连通性探针)(真机实测见
    docs/emby-compat.md):新客户端 Lenna/1.0.16 登录时会先打它探「服务器活着吗」,
    此前落到 501;现照真机回 200 text/plain、正文常量 Emby Server,并豁免 token
    (真机 OkEmby / nyamedia 免鉴权、itsmygo 要 token —— 对齐多数)。客户端无需改动。

  • Emby 补一条端点 GET /api/emby/System/Info(完整服务器信息,取诚实子集):新客户端
    Filmly/2.12.11-439 登录后打它取服务器信息,此前落到 501 通配;现认领该端点、回 200 JSON。
    响应只给面板真有的 17 个字段 —— ServerName / Version / Id / OperatingSystem / LocalAddress /
    LocalAddresses(空) / RemoteAddresses(空) / CompletedInstallations(空) 如实给值,能力位
    HasPendingRestart / IsShuttingDown / SupportsLibraryMonitor / CanSelfRestart / CanSelfUpdate /
    CanLaunchWebBrowser / SupportsHttps / HasUpdateAvailable / SupportsAutoRunAtStartup 一律 false;
    面板无对应物的 8 个字段(SystemUpdateLevel / WebSocketPortNumber / HttpsPortNumber / WanAddress 等)不回
    —— 不编造面板没有的能力与数值。鉴权只验 token(同类读取端点口径)。字段集小于真机
    (真机 4.8 / 4.9 同构 25 字段),属有意诚实子集,客户端本就该容忍字段缺失,无需改动。
    (决策见 ADR-0057;契约变更记录见
    docs/emby-compat.md;真机逐条见
    docs/emby-realdevice/20-system-info.md)

  • 模板页两个页签调换:进页面默认落在「选站点」(原来是「填参数」),页签顺序一并调过来 ——
    选站点在前、填参数在后。站点是这套模板的主体(上百行的那张表),进页面先看到它;
    参数是偶尔调一次的旋钮,要看再切过去。本次会话内点过的另一档仍会记住,刷新页面才回到默认。

  • 中继取块策略优化(客户端无感):面板 proxy 线路的字节中继在排序并发取块的基础上,新增
    三项按 media-bridge-relay Worker 对齐的策略 ——
    慢判死(连接/响应头 15s + 读满一块 10s〔头部块〕/ 30s〔普通块〕,超时即判该块失败并走重试,
    不再拖死整条有序流)、头部对冲(前 4 块各发 2 路取先成功者,直接压 TTFB)、
    重试口径(每块 3 发、间隔 200/500ms,仅对 429/5xx 多试,其余 4xx 再确认一发即走)。
    对外契约与 playVia 落法不变,客户端无需改动。设计取舍见
    docs/adr/0050。

  • Emby 协议契约变更:读取类端点不再用 UserId 鉴权(真机实测不校验;契约变更记录见
    docs/emby-compat.md):Views / Users/{UserId}/Items / Items/Latest /
    Shows/{Id}/Seasons / Shows/{Id}/Episodes / 条目详情 Users/{UserId}/Items/{ItemId} /
    播放信息 Items/{ItemId}/PlaybackInfo / 直连拉流 videos/{ItemId}/{file} 与 Items/{ItemId}/Stream/{token} /
    下载 Items/{ItemId}/Download / 继续观看 Users/{UserId}/Items/Resume / 接下来看 Shows/NextUp /
    相似推荐 Items/{ItemId}/Similar 十三条端点由
    「UserId 必须属于该 token 账号,否则 401」改为「只验 token、不比对 UserId」—— 有效 token +
    不匹配 / 不存在的 UserId 现在一律回 200(拉流 / 下载则照常出字节)。鉴权身份收敛到 token 一处;
    用户私有进度仍按 token 的账号取,
    不引入跨账号泄露(PlaybackInfo 虽为 POST,但只返回版本清单、不写用户私有数据;拉流 / 下载取的是内容字节,
    均与用户无关,故按读取类处理)。
    其余端点(Users/{UserId}、写端点的 PlayedItems / HideFromResume 等)暂维持旧口径,
    留待各自真机对照。客户端无需改动。

  • Emby 协议契约变更:Studios 收紧为「只验 token」(真机实测要求;契约变更记录见
    docs/emby-compat.md):GET /api/emby/Studios 由完全豁免 token 改为
    无 token / 无效 token 一律 401 纯文本 Access token is invalid or expired.(与 Items / Items/Latest
    同口径)。响应仍是如实回空 {"Items":[],"TotalRecordCount":0}(面板没有片库可枚举,不复刻真机的全量清单)。
    正常客户端都带 token,客户端无需改动。

  • Emby 协议契约变更:Items/Counts 收紧为「只验 token」(真机实测要求;契约变更记录见
    docs/emby-compat.md):GET /api/emby/Items/Counts 由完全不校验账号(连 token 都不看)
    改为无 token / 无效 token 一律 401 纯文本 Access token is invalid or expired.(与 Items / Studios 同口径),
    有效 token + 任意 UserId 照旧 200(不比对 UserId)。响应填 MovieCount / SeriesCount / EpisodeCount
    (前两者取首页插件申报的库总数、后者取剧库行申报的集数,见下一条),其余 11 类仍为 0(面板没有片库索引,不复刻真机的全类别计数)。
    正常客户端都带 token,客户端无需改动。

  • Shows/{Id}/Seasons / Shows/{Id}/Episodes 按真机对齐(响应形状变更):

    • Seasons 返回特别篇:不再过滤 IndexNumber: 0 的那一季(真机会返回)。
    • 季 UserData 补 UnplayedItemCount:由 4 键补到真机的 5 键(本季未看集数)。
    • Episodes 集条目补 SeriesName(剧名):真机每条集都带;取剧名照 progressItem 的做法再 lookup
      一次,命中元数据插件缓存、不额外打上游;查不到只少这一个可选字段,不影响分集照常返回。
  • Emby 实例新增「下载」开关(默认开):实例编辑弹窗可开关客户端下载能力,开关驱动
    同一口径的三处 —— 握手 Policy.EnableContentDownloading、条目 CanDownload、
    Items/{ItemId}/Download 端点(关闭时回 403 纯文本)。默认开 = 行为与以往一致;关闭后
    客户端不再显示下载入口、直接请求下载端点也被拒。

  • 条目详情补条目级 Width / Height(视频分辨率):数据来自片源插件申报的 width/height
    (该契约早已存在,见插件仓库 media-bridge-plugins/docs/plugin-contract.md)。插件没给就没有这俩字段
    (面板不从集名反推)。对齐真机条目级分辨率。

  • 媒体库角标 / 条目计数显示真实库总数(首页插件新增可选申报 total)(契约变更记录见
    docs/emby-compat.md,设计取舍见 docs/adr/0051 /
    docs/adr/0052):

    • Views 库条目 ChildCount:首页插件的行申报(rows)新增可选字段 total —— 申报这个库
      总共有多少条(口径同 run 的 total)。面板据此三级取数:行申报 total → 插件点开过一次后
      记下的数 → 占位 1
      。客户端还没点开库就能看到真数(此前未点开的库一律显示占位 1)。插件
      取不到就不申报(回退占位 1,不编数)。
    • Items/Counts 的 MovieCount / SeriesCount / EpisodeCount:前两者取同一份「首页插件申报的库总数」
      (按库类型归并,同类型多行取最大值、mixed 行不参与),EpisodeCount 取剧库行申报的集数
      (episodes,只对 tvshows 行有意义),其余 11 个字段仍回 0。此前这三个字段恒回 0
      (设计取舍见 docs/adr/0053)。
    • 契约正文在插件仓库 media-bridge-plugins/docs/emby-home-plugin.md。TMDB 首页插件已按此申报:
      抓官网 About 页的全库规模,按库类型映射(movies → 电影数、tvshows → 剧集数、mixed → 两者之和),
      并解析同页的 TV Episodes 作为剧库集数(episodes),带缓存、不阻塞面板的行轮询。客户端无需改动。
  • Emby Items 补「按类型计数」分支(客户端首页总统计不再显示 0)(真机对照见
    docs/emby-realdevice/05-users-userid-items.md,
    契约变更记录见 docs/emby-compat.md):Rex 等客户端首页在拿库列表之前
    先打两条无 ParentId 的 Items?Recursive=true&IncludeItemTypes=Movie|Series&Limit=1&SortBy=SortName&SortOrder=Ascending
    (除类型外参数相同),只读 TotalRecordCount 当「总统计」;此前落到兜底、回 0(首页总统计恒显示 0)。
    现按类型取首页插件申报的库规模(home.libraryTotals(),与 Items/Counts 同一份数据,见
    docs/adr/0052)填 TotalRecordCount;Items 仍回空
    (不给样本条目)。只认单类型,Movie,Series 这类多值仍回空(无样本可循)。客户端无需改动。

  • Emby 版本行副标题改为「标准文件名」(契约变更记录见 docs/emby-compat.md):
    客户端版本行的副标题(取 MediaSources[].Path 解码后「最后一个 / 之后」的文字)此前是
    「站点来源标签 · 集名」(如 小雅 Alist · 4K · <原始文件名>),现改为聚合层拼好的标准文件名
    (scene naming,标题.年份.季集.分辨率.来源.音频(含声道).Atmos.动态范围.视频编码.容器),例如
    蜘蛛侠:崭新之日.2026.2160p.WEB-DL.DDP5.1.Atmos.DV.H.265.mkv。不再带站点前缀
    (来源 / 线路已在标题位 MediaSources[].Name 显示);WEB-DL 等来源由聚合层识别补出,
    H.265 / DDP5.1 / DV 等规格用圈内通行写法。只动副标题 —— 标题位、MediaStreams
    各字段、播放路径(走 /videos/{Id}/stream.{ext},不读这个 Path)均不变,客户端无需改动。


面板「设置 → 版本与更新」里可以手动更新到这个版本;它只安装正式 Release 的资产并校验 sha256。