feat: Emby 挂载独立管理(媒体库级选择挂载 + 每库代理开关)

- 新增 EmbyMount 表与 /admin/emby/mounts CRUD:把远程 Emby 的媒体库按需
  挂载到本项目(同一服务器可选择性挂载/全量挂载),代理开关下沉到每个
  挂载(一个 Emby 服务器可部分库代理、部分库直连)
- 伪装 ID 改为 embyremote~{mountID}~{remoteID}:播放/详情/状态/图片链路
  全部经 ResolveMount 解析挂载与账号
- 新增「Emby 挂载」独立菜单页(/emby-mount):账号管理(添加/测试/编辑/
  删除)+ 挂载列表(代理切换/停用/取消)+ 选择挂载对话框(多选+全量)
- STRM 管理页过滤 emby_remote 账号;账号表单移除 emby 类型
- Boot 幂等 AutoSeedMounts:旧 emby 账号自动全量挂载(沿用旧代理配置),
  升级后媒体库不消失
- 修复:远程流请求注入浏览器 UA(防 Cloudflare 风控)+ 代理强制 Static=true
  (阻止远程 ffmpeg 转码调度,反代纯字节中继)
- 全局搜索改为按挂载逐个搜索(结果归属与伪装 ID 正确)
This commit is contained in:
truewhile
2026-09-01 13:53:15 +08:00
parent 5a5555d28a
commit 73b95a8e38
24 changed files with 1331 additions and 186 deletions
+8 -8
View File
@@ -13,12 +13,12 @@ import (
func (e *EmbyService) Item(ctx context.Context, mediaID, userID string) (map[string]any, error) {
// 远程 Emby 条目:不查本地库,直接向远程转发(保持远程最新元数据)。
if e.remote != nil && IsEmbyRemoteID(mediaID) {
acctID, remoteID, _ := DecodeEmbyRemoteID(mediaID)
acct := e.remote.AccountByID(ctx, acctID)
if acct == nil {
mountID, remoteID, _ := DecodeEmbyRemoteID(mediaID)
mount, acct, _ := e.remote.ResolveMount(ctx, mountID)
if mount == nil || acct == nil {
return nil, nil
}
return e.remote.RemoteItem(ctx, acct, remoteID)
return e.remote.RemoteItem(ctx, mount, acct, remoteID)
}
if lib, err := e.repo.Library.FindByID(ctx, mediaID); err != nil {
return nil, err
@@ -86,12 +86,12 @@ func (e *EmbyService) LatestItems(ctx context.Context, userID, parentID string,
limit = 20
}
if e.remote != nil && IsEmbyRemoteID(parentID) {
acctID, remoteParent, _ := DecodeEmbyRemoteID(parentID)
acct := e.remote.AccountByID(ctx, acctID)
if acct == nil {
mountID, remoteParent, _ := DecodeEmbyRemoteID(parentID)
mount, acct, _ := e.remote.ResolveMount(ctx, mountID)
if mount == nil || acct == nil {
return nil, nil
}
return e.remote.RemoteLatest(ctx, acct, remoteParent, limit)
return e.remote.RemoteLatest(ctx, mount, acct, remoteParent, limit)
}
cacheKey := e.embyLatestCacheKey(userID, parentID, limit)
var cached embyLatestCacheValue