From f196e5621fc34d2f3429fee1305ab0a0c392f5ba Mon Sep 17 00:00:00 2001 From: truewhile <62226914+truewhile@users.noreply.github.com> Date: Tue, 8 Sep 2026 22:12:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/series.go | 6 ++++-- internal/service/media_versions.go | 19 +++++++++++++++++++ internal/service/media_versions_test.go | 22 ++++++++++++++++++++++ web/src/pages/LibrarySeriesEpisodes.tsx | 2 ++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/internal/handler/series.go b/internal/handler/series.go index 0f56c33..054f269 100644 --- a/internal/handler/series.go +++ b/internal/handler/series.go @@ -195,7 +195,8 @@ func listLibrarySeriesEpisodesHandler(svc *service.Container) gin.HandlerFunc { writeInternalOrCanceled(c, err) return } - c.JSON(http.StatusOK, gin.H{"items": items, "total": len(items)}) + grouped := service.GroupEpisodeVersionsForDisplay(items) + c.JSON(http.StatusOK, gin.H{"items": grouped, "total": len(grouped)}) } } @@ -235,6 +236,7 @@ func listMediaEpisodesHandler(svc *service.Container) gin.HandlerFunc { writeInternalOrCanceled(c, err) return } - c.JSON(http.StatusOK, gin.H{"items": items, "total": len(items)}) + grouped := service.GroupEpisodeVersionsForDisplay(items) + c.JSON(http.StatusOK, gin.H{"items": grouped, "total": len(grouped)}) } } diff --git a/internal/service/media_versions.go b/internal/service/media_versions.go index 5bad02c..dfc4f90 100644 --- a/internal/service/media_versions.go +++ b/internal/service/media_versions.go @@ -120,6 +120,25 @@ func GroupMediaVersions(items []model.Media) []MediaItem { return groupMediaVersions(items) } +// GroupEpisodeVersionsForDisplay folds encoding/container variants into one +// episode while preserving season/episode ordering for episode-list APIs. +func GroupEpisodeVersionsForDisplay(items []model.Media) []MediaItem { + grouped := groupMediaVersions(items) + if grouped == nil { + return []MediaItem{} + } + sort.SliceStable(grouped, func(i, j int) bool { + if grouped[i].SeasonNum != grouped[j].SeasonNum { + return grouped[i].SeasonNum < grouped[j].SeasonNum + } + if grouped[i].EpisodeNum != grouped[j].EpisodeNum { + return grouped[i].EpisodeNum < grouped[j].EpisodeNum + } + return grouped[i].CreatedAt.Before(grouped[j].CreatedAt) + }) + return grouped +} + func mediaVersionGroupKey(m model.Media) string { // 远程 Emby 挂载条目保持独立,不与其它远程条目或本地条目折叠合并。 if IsEmbyRemoteID(m.ID) { diff --git a/internal/service/media_versions_test.go b/internal/service/media_versions_test.go index 7539e69..b037c32 100644 --- a/internal/service/media_versions_test.go +++ b/internal/service/media_versions_test.go @@ -49,6 +49,28 @@ func TestGroupMediaVersionsMergesEpisodeByExternalIDAcrossLibraries(t *testing.T } } +func TestGroupEpisodeVersionsForDisplayMergesAndKeepsEpisodeOrder(t *testing.T) { + rows := []model.Media{ + {Base: model.Base{ID: "ep2"}, LibraryID: "tv", Title: "Show", SeasonNum: 1, EpisodeNum: 2, TMDbID: 99, Path: "/show/s01e02.mkv"}, + {Base: model.Base{ID: "ep1-small"}, LibraryID: "tv", Title: "Show", SeasonNum: 1, EpisodeNum: 1, TMDbID: 99, Path: "/show/s01e01.mp4", SizeBytes: 100}, + {Base: model.Base{ID: "ep1-large"}, LibraryID: "tv", Title: "Show", SeasonNum: 1, EpisodeNum: 1, TMDbID: 99, Path: "/show/s01e01.mkv", SizeBytes: 200}, + } + + grouped := GroupEpisodeVersionsForDisplay(rows) + if len(grouped) != 2 { + t.Fatalf("grouped len = %d, want 2: %#v", len(grouped), grouped) + } + if grouped[0].EpisodeNum != 1 || len(grouped[0].Versions) != 2 { + t.Fatalf("episode 1 was not merged first: %#v", grouped) + } + if grouped[0].ID != "ep1-large" { + t.Fatalf("primary version = %q, want ep1-large", grouped[0].ID) + } + if grouped[1].EpisodeNum != 2 { + t.Fatalf("second item episode = %d, want 2", grouped[1].EpisodeNum) + } +} + func TestGroupMediaVersionsMergesMovieEncodingVariants(t *testing.T) { hd := model.Media{ LibraryID: "movies", diff --git a/web/src/pages/LibrarySeriesEpisodes.tsx b/web/src/pages/LibrarySeriesEpisodes.tsx index 9179227..3058455 100644 --- a/web/src/pages/LibrarySeriesEpisodes.tsx +++ b/web/src/pages/LibrarySeriesEpisodes.tsx @@ -65,6 +65,7 @@ export function LibrarySeriesEpisodes({
{visibleEpisodes.map((ep) => { const displayTitle = episodeDisplayTitle(ep, visibleEpisodes) + const versionCount = ep.versions?.length ?? 1 return (
0 ? `${Math.floor(ep.duration_sec / 60)} 分钟` : formatSize(ep.size_bytes)} + {versionCount > 1 ? ` · ${versionCount} 版本` : ''}