mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 13:26:38 +08:00
bug
This commit is contained in:
@@ -170,7 +170,13 @@ func listLibrariesHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
if acct == nil {
|
if acct == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tmpMount := &model.EmbyMount{Base: model.Base{ID: v.MountID}}
|
tmpMount := &model.EmbyMount{
|
||||||
|
Base: model.Base{ID: v.MountID},
|
||||||
|
AccountID: v.AccountID,
|
||||||
|
RemoteViewID: v.RemoteID,
|
||||||
|
CollectionType: v.CollectionType,
|
||||||
|
Name: v.Library.Name,
|
||||||
|
}
|
||||||
itemTypes := remoteLibraryItemTypes(v.CollectionType)
|
itemTypes := remoteLibraryItemTypes(v.CollectionType)
|
||||||
if _, total, err := svc.EmbyRemote.RemoteLibraryMedia(ctx, tmpMount, acct, v.RemoteID, itemTypes, 0, 1); err == nil {
|
if _, total, err := svc.EmbyRemote.RemoteLibraryMedia(ctx, tmpMount, acct, v.RemoteID, itemTypes, 0, 1); err == nil {
|
||||||
remotePayloads[i].Total = total
|
remotePayloads[i].Total = total
|
||||||
|
|||||||
@@ -820,6 +820,7 @@ func (r *EmbyRemoteService) RemoteLatest(ctx context.Context, mount *model.EmbyM
|
|||||||
if parentID != "" {
|
if parentID != "" {
|
||||||
q.Set("ParentId", parentID)
|
q.Set("ParentId", parentID)
|
||||||
}
|
}
|
||||||
|
q.Set("Fields", "Overview,Genres,ProviderIds,Path,SeriesPrimaryImage,DateCreated,DateLastMediaAdded,PremiereDate,ProductionYear,CommunityRating,CriticRating")
|
||||||
path := "/Users/" + url.PathEscape(r.remoteUserID(cfg)) + "/Items/Latest"
|
path := "/Users/" + url.PathEscape(r.remoteUserID(cfg)) + "/Items/Latest"
|
||||||
var out []map[string]any
|
var out []map[string]any
|
||||||
if err := r.doGet(ctx, acct, cfg, path, q, &out); err != nil {
|
if err := r.doGet(ctx, acct, cfg, path, q, &out); err != nil {
|
||||||
|
|||||||
@@ -180,21 +180,31 @@ func (r *EmbyRemoteService) MapRemoteItemToMedia(ctx context.Context, mount *mod
|
|||||||
if _, rid, ok := DecodeEmbyRemoteID(seriesID); ok {
|
if _, rid, ok := DecodeEmbyRemoteID(seriesID); ok {
|
||||||
seriesID = rid
|
seriesID = rid
|
||||||
}
|
}
|
||||||
rating := remoteItemFloat(item, "CommunityRating")
|
rating := remoteItemFloat(item, "CommunityRating")
|
||||||
if rating == 0 {
|
if rating == 0 {
|
||||||
rating = remoteItemFloat(item, "CriticRating")
|
rating = remoteItemFloat(item, "CriticRating")
|
||||||
}
|
}
|
||||||
media := model.Media{
|
year := remoteItemInt(item, "ProductionYear")
|
||||||
Base: model.Base{ID: EncodeEmbyRemoteID(encodeScope, remoteID)},
|
if year == 0 {
|
||||||
Title: remoteItemString(item, "Name"),
|
year = remoteItemInt(item, "Year")
|
||||||
OriginalName: remoteItemString(item, "OriginalTitle"),
|
}
|
||||||
Overview: remoteItemString(item, "Overview"),
|
if year == 0 {
|
||||||
Year: remoteItemInt(item, "ProductionYear"),
|
year = remoteItemInt(item, "SeriesProductionYear")
|
||||||
Rating: float32(rating),
|
}
|
||||||
Path: remoteItemString(item, "Path"),
|
if year == 0 {
|
||||||
Genres: remoteItemGenres(item),
|
year = remoteItemInt(item, "SeriesYear")
|
||||||
ScrapeStatus: "done",
|
}
|
||||||
}
|
media := model.Media{
|
||||||
|
Base: model.Base{ID: EncodeEmbyRemoteID(encodeScope, remoteID)},
|
||||||
|
Title: remoteItemString(item, "Name"),
|
||||||
|
OriginalName: remoteItemString(item, "OriginalTitle"),
|
||||||
|
Overview: remoteItemString(item, "Overview"),
|
||||||
|
Year: year,
|
||||||
|
Rating: float32(rating),
|
||||||
|
Path: remoteItemString(item, "Path"),
|
||||||
|
Genres: remoteItemGenres(item),
|
||||||
|
ScrapeStatus: "done",
|
||||||
|
}
|
||||||
if date, ok := parseEmbyRemoteDate(remoteItemString(item, "DateCreated")); ok {
|
if date, ok := parseEmbyRemoteDate(remoteItemString(item, "DateCreated")); ok {
|
||||||
media.CreatedAt = date
|
media.CreatedAt = date
|
||||||
media.UpdatedAt = date
|
media.UpdatedAt = date
|
||||||
@@ -561,6 +571,46 @@ func (r *EmbyRemoteService) RemoteLatestCards(ctx context.Context, mount *model.
|
|||||||
if r.cache != nil && r.cache.GetJSON(ctx, cacheKey, &cached) {
|
if r.cache != nil && r.cache.GetJSON(ctx, cacheKey, &cached) {
|
||||||
return cached, nil
|
return cached, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 剧集类媒体库:直接拉取最新入库/更新的 Series 剧集本身(按上次添加集日期倒序)。
|
||||||
|
// 避免 Emby /Items/Latest 默认返回无年份/无系列海报的单集(Episode)。
|
||||||
|
if mount != nil && (mount.CollectionType == "tvshows" || mount.CollectionType == "tv") {
|
||||||
|
q := url.Values{}
|
||||||
|
q.Set("ParentId", remoteViewID)
|
||||||
|
q.Set("IncludeItemTypes", "Series")
|
||||||
|
q.Set("Recursive", "false")
|
||||||
|
q.Set("SortBy", "DateLastContentAdded")
|
||||||
|
q.Set("SortOrder", "Descending")
|
||||||
|
q.Set("Limit", strconv.Itoa(limit))
|
||||||
|
q.Set("Fields", "Overview,Genres,ProviderIds,Path,RecursiveItemCount,SeriesPrimaryImage,DateCreated,DateLastMediaAdded,PremiereDate,ProductionYear,CommunityRating,CriticRating")
|
||||||
|
var body struct {
|
||||||
|
Items []map[string]any `json:"Items"`
|
||||||
|
}
|
||||||
|
if err := r.doGet(ctx, acct, cfg, "/Users/"+url.PathEscape(r.remoteUserID(cfg))+"/Items", q, &body); err == nil && len(body.Items) > 0 {
|
||||||
|
cards := make([]SeriesCard, 0, len(body.Items))
|
||||||
|
for _, it := range body.Items {
|
||||||
|
RewriteEmbyRemoteIDs(it, mount.ID)
|
||||||
|
m := r.MapRemoteItemToMedia(ctx, mount, acct, cfg, it)
|
||||||
|
count := remoteItemInt(it, "RecursiveItemCount")
|
||||||
|
if count == 0 {
|
||||||
|
count = remoteItemInt(it, "ChildCount")
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
count = 1
|
||||||
|
}
|
||||||
|
var lastAdded *time.Time
|
||||||
|
if date, ok := parseEmbyRemoteDate(remoteItemString(it, "DateLastMediaAdded")); ok {
|
||||||
|
lastAdded = &date
|
||||||
|
}
|
||||||
|
cards = append(cards, SeriesCard{Key: m.ID, Rep: m, LinkMedia: m, Count: count, LastAddedAt: lastAdded})
|
||||||
|
}
|
||||||
|
if r.cache != nil {
|
||||||
|
r.cache.SetJSON(ctx, cacheKey, cards, r.remoteMediaCacheTTL())
|
||||||
|
}
|
||||||
|
return cards, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
items, err := r.RemoteLatest(ctx, mount, acct, remoteViewID, limit)
|
items, err := r.RemoteLatest(ctx, mount, acct, remoteViewID, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -576,7 +626,11 @@ func (r *EmbyRemoteService) RemoteLatestCards(ctx context.Context, mount *model.
|
|||||||
t := m.CreatedAt
|
t := m.CreatedAt
|
||||||
lastAdded = &t
|
lastAdded = &t
|
||||||
}
|
}
|
||||||
cards = append(cards, SeriesCard{Key: m.ID, Rep: m, LinkMedia: m, Count: 0, LastAddedAt: lastAdded})
|
count := remoteItemInt(it, "RecursiveItemCount")
|
||||||
|
if count == 0 {
|
||||||
|
count = remoteItemInt(it, "ChildCount")
|
||||||
|
}
|
||||||
|
cards = append(cards, SeriesCard{Key: m.ID, Rep: m, LinkMedia: m, Count: count, LastAddedAt: lastAdded})
|
||||||
}
|
}
|
||||||
if r.cache != nil {
|
if r.cache != nil {
|
||||||
r.cache.SetJSON(ctx, cacheKey, cards, r.remoteMediaCacheTTL())
|
r.cache.SetJSON(ctx, cacheKey, cards, r.remoteMediaCacheTTL())
|
||||||
|
|||||||
@@ -400,3 +400,130 @@ func TestRemoteSearchMedia(t *testing.T) {
|
|||||||
t.Fatalf("expected 0 items due to HiddenLibraryIDs, got %d", len(hiddenFiltered))
|
t.Fatalf("expected 0 items due to HiddenLibraryIDs, got %d", len(hiddenFiltered))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoteLatestCardsTvShowsYearAndPoster(t *testing.T) {
|
||||||
|
var requestedFields string
|
||||||
|
var requestedIncludeItemTypes string
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestedFields = r.URL.Query().Get("Fields")
|
||||||
|
requestedIncludeItemTypes = r.URL.Query().Get("IncludeItemTypes")
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"TotalRecordCount": 1,
|
||||||
|
"Items": []map[string]any{
|
||||||
|
{
|
||||||
|
"Id": "series-100",
|
||||||
|
"Name": "炒翻天",
|
||||||
|
"Type": "Series",
|
||||||
|
"ProductionYear": 2024,
|
||||||
|
"ImageTags": map[string]any{
|
||||||
|
"Primary": "tag123",
|
||||||
|
},
|
||||||
|
"RecursiveItemCount": 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
db := newServiceTestDB(t, &model.StrmAccount{}, &model.EmbyMount{})
|
||||||
|
repos := repository.New(db)
|
||||||
|
svc := NewEmbyRemoteService(&config.Config{}, zap.NewNop(), repos, NewCryptoService("", zap.NewNop()))
|
||||||
|
|
||||||
|
rawConfig, _ := json.Marshal(map[string]string{
|
||||||
|
"url": server.URL,
|
||||||
|
"token": "fake-token",
|
||||||
|
})
|
||||||
|
acct := &model.StrmAccount{
|
||||||
|
Base: model.Base{ID: "acct-tv"},
|
||||||
|
Name: "tv-emby",
|
||||||
|
Provider: model.StrmProviderEmbyRemote,
|
||||||
|
Config: string(rawConfig),
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
_ = repos.StrmAccount.Create(t.Context(), acct)
|
||||||
|
mount := &model.EmbyMount{
|
||||||
|
Base: model.Base{ID: "mount-tv"},
|
||||||
|
AccountID: acct.ID,
|
||||||
|
RemoteViewID: "view-tv",
|
||||||
|
RemoteViewName: "新番连载",
|
||||||
|
CollectionType: "tvshows",
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
_ = repos.EmbyMount.Create(t.Context(), mount)
|
||||||
|
|
||||||
|
cards, err := svc.RemoteLatestCards(t.Context(), mount, acct, "view-tv", 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RemoteLatestCards failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(cards) != 1 {
|
||||||
|
t.Fatalf("expected 1 card, got %d", len(cards))
|
||||||
|
}
|
||||||
|
if cards[0].Rep.Year != 2024 {
|
||||||
|
t.Fatalf("expected Year 2024, got %d", cards[0].Rep.Year)
|
||||||
|
}
|
||||||
|
if cards[0].Count != 12 {
|
||||||
|
t.Fatalf("expected Count 12, got %d", cards[0].Count)
|
||||||
|
}
|
||||||
|
if cards[0].Rep.PosterURL == "" {
|
||||||
|
t.Fatalf("expected PosterURL not empty")
|
||||||
|
}
|
||||||
|
if requestedIncludeItemTypes != "Series" {
|
||||||
|
t.Fatalf("expected IncludeItemTypes=Series, got %q", requestedIncludeItemTypes)
|
||||||
|
}
|
||||||
|
if !strings.Contains(requestedFields, "ProductionYear") {
|
||||||
|
t.Fatalf("expected Fields to contain ProductionYear, got %q", requestedFields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteLatestFields(t *testing.T) {
|
||||||
|
var requestedFields string
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestedFields = r.URL.Query().Get("Fields")
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode([]map[string]any{
|
||||||
|
{
|
||||||
|
"Id": "movie-100",
|
||||||
|
"Name": "测试电影",
|
||||||
|
"Type": "Movie",
|
||||||
|
"ProductionYear": 2023,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
db := newServiceTestDB(t, &model.StrmAccount{}, &model.EmbyMount{})
|
||||||
|
repos := repository.New(db)
|
||||||
|
svc := NewEmbyRemoteService(&config.Config{}, zap.NewNop(), repos, NewCryptoService("", zap.NewNop()))
|
||||||
|
|
||||||
|
rawConfig, _ := json.Marshal(map[string]string{
|
||||||
|
"url": server.URL,
|
||||||
|
"token": "fake-token",
|
||||||
|
})
|
||||||
|
acct := &model.StrmAccount{
|
||||||
|
Base: model.Base{ID: "acct-movie"},
|
||||||
|
Name: "movie-emby",
|
||||||
|
Provider: model.StrmProviderEmbyRemote,
|
||||||
|
Config: string(rawConfig),
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
_ = repos.StrmAccount.Create(t.Context(), acct)
|
||||||
|
mount := &model.EmbyMount{
|
||||||
|
Base: model.Base{ID: "mount-movie"},
|
||||||
|
AccountID: acct.ID,
|
||||||
|
RemoteViewID: "view-movie",
|
||||||
|
CollectionType: "movies",
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
items, err := svc.RemoteLatest(t.Context(), mount, acct, "view-movie", 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RemoteLatest failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(items) != 1 {
|
||||||
|
t.Fatalf("expected 1 item, got %d", len(items))
|
||||||
|
}
|
||||||
|
if !strings.Contains(requestedFields, "ProductionYear") {
|
||||||
|
t.Fatalf("expected Fields to contain ProductionYear, got %q", requestedFields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user