mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-07 05:46:38 +08:00
fix: 远程 Emby 无图条目不再下发无效图片 URL(封面破图修复)
- 远程条目仅当 ImageTags 含 Primary/Backdrop 时才生成图片 URL; 无图条目 poster_url 为空,前端走 No Poster 优雅占位而非裂图 - 剧集海报回退使用 Emby 的 SeriesPrimaryImageTag(Fields=SeriesPrimaryImage), 仅当系列真实有图时才回退,避免连环无效请求 - 列表/剧集接口带 SeriesPrimaryImage 字段
This commit is contained in:
@@ -109,7 +109,8 @@ func (r *EmbyRemoteService) mapRemoteViewToLibrary(acct *model.StrmAccount, cfg
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MapRemoteItemToMedia 把远程 Emby item JSON 映射为本地 Media 结构。
|
// MapRemoteItemToMedia 把远程 Emby item JSON 映射为本地 Media 结构。
|
||||||
// poster/backdrop 填远程图片绝对地址(前端经 /api/img 同源代理拉取)。
|
// poster/backdrop 只有在远程确实存在图片标签时才填 URL(避免对无图条目
|
||||||
|
// 发出必失败的图片请求导致前端破图);剧集回退到系列海报(SeriesPrimaryImage)。
|
||||||
func (r *EmbyRemoteService) MapRemoteItemToMedia(ctx context.Context, acct *model.StrmAccount, cfg *EmbyRemoteConfig, item map[string]any) model.Media {
|
func (r *EmbyRemoteService) MapRemoteItemToMedia(ctx context.Context, acct *model.StrmAccount, cfg *EmbyRemoteConfig, item map[string]any) model.Media {
|
||||||
remoteID := remoteItemString(item, "Id")
|
remoteID := remoteItemString(item, "Id")
|
||||||
// 条目可能已被 RewriteEmbyRemoteIDs 伪装(图片/嵌套 ID 需要原始远程 ID)。
|
// 条目可能已被 RewriteEmbyRemoteIDs 伪装(图片/嵌套 ID 需要原始远程 ID)。
|
||||||
@@ -128,11 +129,16 @@ func (r *EmbyRemoteService) MapRemoteItemToMedia(ctx context.Context, acct *mode
|
|||||||
Year: remoteItemInt(item, "ProductionYear"),
|
Year: remoteItemInt(item, "ProductionYear"),
|
||||||
Rating: float32(remoteItemFloat(item, "CommunityRating")),
|
Rating: float32(remoteItemFloat(item, "CommunityRating")),
|
||||||
Path: remoteItemString(item, "Path"),
|
Path: remoteItemString(item, "Path"),
|
||||||
PosterURL: r.remoteItemImageURL(cfg, remoteID, "Primary"),
|
|
||||||
BackdropURL: r.remoteItemImageURL(cfg, remoteID, "Backdrop"),
|
|
||||||
Genres: remoteItemGenres(item),
|
Genres: remoteItemGenres(item),
|
||||||
ScrapeStatus: "done",
|
ScrapeStatus: "done",
|
||||||
}
|
}
|
||||||
|
// 只有远程明确存在图片标签才下发图片 URL。
|
||||||
|
if remoteItemHasImageTag(item, "Primary") {
|
||||||
|
media.PosterURL = r.remoteItemImageURL(cfg, remoteID, "Primary")
|
||||||
|
}
|
||||||
|
if remoteItemHasImageTag(item, "Backdrop") || len(remoteBackdropTags(item)) > 0 {
|
||||||
|
media.BackdropURL = r.remoteItemImageURL(cfg, remoteID, "Backdrop")
|
||||||
|
}
|
||||||
if ticks := remoteItemInt64(item, "RunTimeTicks"); ticks > 0 {
|
if ticks := remoteItemInt64(item, "RunTimeTicks"); ticks > 0 {
|
||||||
media.DurationSec = int(ticks / 10_000_000)
|
media.DurationSec = int(ticks / 10_000_000)
|
||||||
}
|
}
|
||||||
@@ -158,8 +164,10 @@ func (r *EmbyRemoteService) MapRemoteItemToMedia(ctx context.Context, acct *mode
|
|||||||
if seriesName := remoteItemString(item, "SeriesName"); seriesName != "" {
|
if seriesName := remoteItemString(item, "SeriesName"); seriesName != "" {
|
||||||
media.Title = seriesName
|
media.Title = seriesName
|
||||||
}
|
}
|
||||||
// Emby 单集通常无独立海报:回退到系列海报,避免网页端空图。
|
// 单集通常无独立海报:若远程返回 SeriesPrimaryImageTag(需要
|
||||||
if media.PosterURL == "" && seriesID != "" {
|
// Fields=SeriesPrimaryImage)且系列有图,则回退到系列海报。
|
||||||
|
if media.PosterURL == "" && seriesID != "" &&
|
||||||
|
strings.TrimSpace(remoteItemString(item, "SeriesPrimaryImageTag")) != "" {
|
||||||
media.PosterURL = r.remoteItemImageURL(cfg, seriesID, "Primary")
|
media.PosterURL = r.remoteItemImageURL(cfg, seriesID, "Primary")
|
||||||
}
|
}
|
||||||
default: // Movie / Series / Season / Folder
|
default: // Movie / Series / Season / Folder
|
||||||
@@ -184,7 +192,7 @@ func (r *EmbyRemoteService) RemoteLibraryMedia(ctx context.Context, acct *model.
|
|||||||
q.Set("Recursive", "false")
|
q.Set("Recursive", "false")
|
||||||
q.Set("StartIndex", strconv.Itoa(offset))
|
q.Set("StartIndex", strconv.Itoa(offset))
|
||||||
q.Set("Limit", strconv.Itoa(limit))
|
q.Set("Limit", strconv.Itoa(limit))
|
||||||
q.Set("Fields", "Overview,Genres,ProviderIds,Path")
|
q.Set("Fields", "Overview,Genres,ProviderIds,Path,SeriesPrimaryImage")
|
||||||
var body struct {
|
var body struct {
|
||||||
Items []map[string]any `json:"Items"`
|
Items []map[string]any `json:"Items"`
|
||||||
TotalRecordCount int64 `json:"TotalRecordCount"`
|
TotalRecordCount int64 `json:"TotalRecordCount"`
|
||||||
@@ -268,7 +276,7 @@ func (r *EmbyRemoteService) remoteEpisodesOf(ctx context.Context, acct *model.St
|
|||||||
q.Set("Recursive", "true")
|
q.Set("Recursive", "true")
|
||||||
q.Set("StartIndex", "0")
|
q.Set("StartIndex", "0")
|
||||||
q.Set("Limit", "500")
|
q.Set("Limit", "500")
|
||||||
q.Set("Fields", "Overview,Genres,ProviderIds,Path")
|
q.Set("Fields", "Overview,Genres,ProviderIds,Path,SeriesPrimaryImage")
|
||||||
var body struct {
|
var body struct {
|
||||||
Items []map[string]any `json:"Items"`
|
Items []map[string]any `json:"Items"`
|
||||||
TotalRecordCount int64 `json:"TotalRecordCount"`
|
TotalRecordCount int64 `json:"TotalRecordCount"`
|
||||||
@@ -297,7 +305,7 @@ func (r *EmbyRemoteService) RemoteSeriesCards(ctx context.Context, acct *model.S
|
|||||||
q.Set("Recursive", "false")
|
q.Set("Recursive", "false")
|
||||||
q.Set("StartIndex", "0")
|
q.Set("StartIndex", "0")
|
||||||
q.Set("Limit", "1000")
|
q.Set("Limit", "1000")
|
||||||
q.Set("Fields", "Overview,Genres,ProviderIds,Path,RecursiveItemCount")
|
q.Set("Fields", "Overview,Genres,ProviderIds,Path,RecursiveItemCount,SeriesPrimaryImage")
|
||||||
var body struct {
|
var body struct {
|
||||||
Items []map[string]any `json:"Items"`
|
Items []map[string]any `json:"Items"`
|
||||||
}
|
}
|
||||||
@@ -485,6 +493,40 @@ func (r *EmbyRemoteService) remoteItemImageURL(cfg *EmbyRemoteConfig, remoteID,
|
|||||||
"?api_key=" + url.QueryEscape(cfg.Token)
|
"?api_key=" + url.QueryEscape(cfg.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remoteItemHasImageTag 远程 item 是否带某类型图片标签(Emby 的 ImageTags map)。
|
||||||
|
func remoteItemHasImageTag(item map[string]any, typ string) bool {
|
||||||
|
if item == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch tags := item["ImageTags"].(type) {
|
||||||
|
case map[string]any:
|
||||||
|
_, ok := tags[typ]
|
||||||
|
return ok
|
||||||
|
case map[string]string:
|
||||||
|
_, ok := tags[typ]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// remoteBackdropTags 远程 item 的 BackdropImageTags 数组。
|
||||||
|
func remoteBackdropTags(item map[string]any) []any {
|
||||||
|
if item == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch tags := item["BackdropImageTags"].(type) {
|
||||||
|
case []any:
|
||||||
|
return tags
|
||||||
|
case []string:
|
||||||
|
out := make([]any, 0, len(tags))
|
||||||
|
for _, s := range tags {
|
||||||
|
out = append(out, s)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// remoteItemTypeOf 从映射后的 Media 推断远程类型(无详情载荷时兜底)。
|
// remoteItemTypeOf 从映射后的 Media 推断远程类型(无详情载荷时兜底)。
|
||||||
func remoteItemTypeOf(m *model.Media) string {
|
func remoteItemTypeOf(m *model.Media) string {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user