mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-10 23:36:37 +08:00
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:
@@ -19,19 +19,19 @@ import (
|
||||
// 播放地址指向远程(直连)还是 MMTL 本地代理端点。
|
||||
func (e *EmbyService) PlaybackInfo(ctx context.Context, mediaID, userID string) (map[string]any, error) {
|
||||
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, err := e.remote.ResolveMount(ctx, mountID)
|
||||
if err != nil {
|
||||
return nil, ErrEmbyRemoteNotFound
|
||||
}
|
||||
out, err := e.remote.RemotePlaybackInfo(ctx, acct, remoteID, userID)
|
||||
out, err := e.remote.RemotePlaybackInfo(ctx, mount, acct, remoteID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if out == nil {
|
||||
return nil, ErrEmbyRemoteNotFound
|
||||
}
|
||||
out["PlaySessionId"] = fmt.Sprintf("remote-%s-%d", acctID, time.Now().Unix())
|
||||
out["PlaySessionId"] = fmt.Sprintf("remote-%s-%d", mountID, time.Now().Unix())
|
||||
return out, nil
|
||||
}
|
||||
m, err := e.playableMedia(ctx, mediaID, userID)
|
||||
@@ -56,48 +56,48 @@ func (e *EmbyService) RemoteAccountByID(ctx context.Context, accountID string) *
|
||||
}
|
||||
|
||||
// ProxyRemoteVideoStream 反向代理远程 Emby 视频流(保留 Range)。
|
||||
func (e *EmbyService) ProxyRemoteVideoStream(ctx context.Context, w http.ResponseWriter, r *http.Request, accountID, remoteID string) error {
|
||||
func (e *EmbyService) ProxyRemoteVideoStream(ctx context.Context, w http.ResponseWriter, r *http.Request, mountID, remoteID string) error {
|
||||
if e == nil || e.remote == nil {
|
||||
return ErrEmbyRemoteNotFound
|
||||
}
|
||||
acct := e.remote.AccountByID(ctx, accountID)
|
||||
if acct == nil {
|
||||
_, acct, err := e.remote.ResolveMount(ctx, mountID)
|
||||
if err != nil {
|
||||
return ErrEmbyRemoteNotFound
|
||||
}
|
||||
return e.remote.ProxyVideoStream(ctx, w, r, acct, remoteID)
|
||||
}
|
||||
|
||||
// ProxyRemoteSubtitle 反向代理远程 Emby 字幕流。
|
||||
func (e *EmbyService) ProxyRemoteSubtitle(ctx context.Context, w http.ResponseWriter, r *http.Request, accountID, remoteID, index string) error {
|
||||
func (e *EmbyService) ProxyRemoteSubtitle(ctx context.Context, w http.ResponseWriter, r *http.Request, mountID, remoteID, index string) error {
|
||||
if e == nil || e.remote == nil {
|
||||
return ErrSubtitleNotFound
|
||||
}
|
||||
acct := e.remote.AccountByID(ctx, accountID)
|
||||
if acct == nil {
|
||||
_, acct, err := e.remote.ResolveMount(ctx, mountID)
|
||||
if err != nil {
|
||||
return ErrSubtitleNotFound
|
||||
}
|
||||
return e.remote.ProxySubtitle(ctx, w, r, acct, remoteID, index)
|
||||
}
|
||||
|
||||
// ProxyRemoteSetPlayed 把已看/未看状态透传到远程 Emby。
|
||||
func (e *EmbyService) ProxyRemoteSetPlayed(ctx context.Context, accountID, remoteID string, played bool) error {
|
||||
func (e *EmbyService) ProxyRemoteSetPlayed(ctx context.Context, mountID, remoteID string, played bool) error {
|
||||
if e == nil || e.remote == nil {
|
||||
return ErrEmbyRemoteNotFound
|
||||
}
|
||||
acct := e.remote.AccountByID(ctx, accountID)
|
||||
if acct == nil {
|
||||
_, acct, err := e.remote.ResolveMount(ctx, mountID)
|
||||
if err != nil {
|
||||
return ErrEmbyRemoteNotFound
|
||||
}
|
||||
return e.remote.ProxySetPlayed(ctx, acct, remoteID, played)
|
||||
}
|
||||
|
||||
// ProxyRemoteSetFavorite 把收藏/取消收藏状态透传到远程 Emby。
|
||||
func (e *EmbyService) ProxyRemoteSetFavorite(ctx context.Context, accountID, remoteID string, favorite bool) error {
|
||||
func (e *EmbyService) ProxyRemoteSetFavorite(ctx context.Context, mountID, remoteID string, favorite bool) error {
|
||||
if e == nil || e.remote == nil {
|
||||
return ErrEmbyRemoteNotFound
|
||||
}
|
||||
acct := e.remote.AccountByID(ctx, accountID)
|
||||
if acct == nil {
|
||||
_, acct, err := e.remote.ResolveMount(ctx, mountID)
|
||||
if err != nil {
|
||||
return ErrEmbyRemoteNotFound
|
||||
}
|
||||
return e.remote.ProxySetFavorite(ctx, acct, remoteID, favorite)
|
||||
|
||||
Reference in New Issue
Block a user