mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 21:36:37 +08:00
fix(emby): 电影条目不下发空的 SeasonId/SeriesId,修复小幻影视起播失败
小幻影视(RodelPlayer)2.2610 会把电影条目的 SeasonId 空串原样传进自己的 播放准备逻辑并抛 ArgumentException(Parameter 'seasonId'),客户端日志里 表现为 failedStage=PreparationFailed、playback generation null,也就是 点播放立刻「播放失败」;剧集条目的 SeasonId 非空,所以同一台设备放剧集正常。 Emby/Jellyfin 对「不适用」的字段是直接不下发(JSON 里没有该键)而不是下发空串。 这里对非剧集条目省略 SeasonId/SeriesId/SeasonName/SeriesName(顺带去掉电影被 标成「特别篇」的 SeasonName),剧集则保留真实季/剧信息、只清空值。 同时回退上一版针对 115 直链 UA 绑定的同源一跳改动:客户端日志显示该客户端的 播放器层与 API 层 UA 是一致的(剧集走服务端换链后的一跳直连可以正常解码播放), 不需要为此牺牲起播速度。
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/truewhile/MeBox/internal/model"
|
||||
)
|
||||
|
||||
// Emby/Jellyfin 对「不适用」的字段不下发(JSON 里没有该键),而不是下发空串。
|
||||
// 小幻影视(RodelPlayer)2.2610 会把电影条目的 "SeasonId": "" 原样传进播放准备
|
||||
// 逻辑并抛 ArgumentException(Parameter 'seasonId'),导致整个起播失败。
|
||||
func TestEmbyMovieItemOmitsEmptySeasonAndSeriesFields(t *testing.T) {
|
||||
svc := newTestEmbyService(t)
|
||||
lib := model.Library{Name: "小姐姐", Path: `/media/小姐姐`, Type: "movie", Enabled: true}
|
||||
if err := svc.repo.Library.Create(t.Context(), &lib); err != nil {
|
||||
t.Fatalf("create library: %v", err)
|
||||
}
|
||||
movie := model.Media{
|
||||
Base: model.Base{ID: "movie-strm"},
|
||||
LibraryID: lib.ID,
|
||||
Title: "SNIS-919",
|
||||
Path: `/media/小姐姐/三上悠亚/SNIS-919/SNIS-919.strm`,
|
||||
Container: "strm",
|
||||
STRMURL: "/api/strm/play/cloud115/video.mkv?acct=a1&pickcode=pc1",
|
||||
}
|
||||
if err := svc.repo.DB.Create(&movie).Error; err != nil {
|
||||
t.Fatalf("create media: %v", err)
|
||||
}
|
||||
|
||||
item := svc.itemPayload(t.Context(), &movie, false, 0, false)
|
||||
if item["Type"] != "Movie" {
|
||||
t.Fatalf("Type = %#v, want Movie", item["Type"])
|
||||
}
|
||||
for _, key := range []string{"SeasonId", "SeriesId", "SeasonName", "SeriesName"} {
|
||||
if value, ok := item[key]; ok {
|
||||
t.Fatalf("%s must be omitted for a movie, got %#v", key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 剧集的季/剧归属是真实存在的,不能被一起删掉。
|
||||
func TestEmbyEpisodeItemKeepsSeasonAndSeriesFields(t *testing.T) {
|
||||
svc := newTestEmbyService(t)
|
||||
lib := model.Library{Name: "剧集", Path: `/media/tv`, Type: "tv", Enabled: true}
|
||||
if err := svc.repo.Library.Create(t.Context(), &lib); err != nil {
|
||||
t.Fatalf("create library: %v", err)
|
||||
}
|
||||
episode := model.Media{
|
||||
Base: model.Base{ID: "ep-1"},
|
||||
LibraryID: lib.ID,
|
||||
Title: "命运石之门",
|
||||
Path: `/media/tv/命运石之门/Season 01/命运石之门 - S01E01.mkv`,
|
||||
SeasonNum: 1,
|
||||
EpisodeNum: 1,
|
||||
}
|
||||
if err := svc.repo.DB.Create(&episode).Error; err != nil {
|
||||
t.Fatalf("create media: %v", err)
|
||||
}
|
||||
|
||||
item := svc.itemPayload(t.Context(), &episode, false, 0, false)
|
||||
if item["Type"] != "Episode" {
|
||||
t.Fatalf("Type = %#v, want Episode", item["Type"])
|
||||
}
|
||||
for _, key := range []string{"SeasonId", "SeriesId", "SeasonName", "SeriesName"} {
|
||||
value, _ := item[key].(string)
|
||||
if value == "" {
|
||||
t.Fatalf("%s = %#v, want a non-empty value for an episode", key, item[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropEmptyStringFields(t *testing.T) {
|
||||
item := map[string]any{
|
||||
"SeasonId": "",
|
||||
"SeriesId": " ",
|
||||
"SeriesName": "命运石之门",
|
||||
"IndexNumber": 0,
|
||||
}
|
||||
dropEmptyStringFields(item, "SeasonId", "SeriesId", "SeriesName")
|
||||
if _, ok := item["SeasonId"]; ok {
|
||||
t.Fatalf("empty SeasonId must be removed")
|
||||
}
|
||||
if _, ok := item["SeriesId"]; ok {
|
||||
t.Fatalf("whitespace-only SeriesId must be removed")
|
||||
}
|
||||
if item["SeriesName"] != "命运石之门" {
|
||||
t.Fatalf("non-empty SeriesName must be kept, got %#v", item["SeriesName"])
|
||||
}
|
||||
if item["IndexNumber"] != 0 {
|
||||
t.Fatalf("non-string fields must be left alone, got %#v", item["IndexNumber"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user