mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 11:16:37 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6355d5582 |
@@ -301,16 +301,8 @@ func groupMediaSeriesCards(items []model.Media) []SeriesCard {
|
||||
if betterSeriesLinkMedia(item, card.LinkMedia) {
|
||||
card.LinkMedia = item
|
||||
}
|
||||
currentArtwork := seriesArtworkScore(item)
|
||||
representativeArtwork := seriesArtworkScore(card.Rep)
|
||||
if currentArtwork > representativeArtwork {
|
||||
if betterSeriesRepresentative(item, card.Rep) {
|
||||
card.Rep = item
|
||||
} else if currentArtwork == representativeArtwork {
|
||||
cur := item.SeasonNum*10000 + item.EpisodeNum
|
||||
rep := card.Rep.SeasonNum*10000 + card.Rep.EpisodeNum
|
||||
if cur > 0 && (rep == 0 || cur < rep) {
|
||||
card.Rep = item
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -335,6 +327,27 @@ func groupMediaSeriesCards(items []model.Media) []SeriesCard {
|
||||
return cards
|
||||
}
|
||||
|
||||
func betterSeriesRepresentative(candidate, current model.Media) bool {
|
||||
// A theatrical feature can have local poster.jpg/background.jpg files and
|
||||
// therefore a higher artwork score than its TV episodes. Keep the TV row as
|
||||
// the visible identity of a mixed series card so a movie cannot hijack the
|
||||
// series title, overview, and artwork.
|
||||
candidateTheatrical := mediaLooksLikeTheatricalFeature(&candidate)
|
||||
currentTheatrical := mediaLooksLikeTheatricalFeature(¤t)
|
||||
if candidateTheatrical != currentTheatrical {
|
||||
return !candidateTheatrical
|
||||
}
|
||||
|
||||
currentArtwork := seriesArtworkScore(candidate)
|
||||
representativeArtwork := seriesArtworkScore(current)
|
||||
if currentArtwork != representativeArtwork {
|
||||
return currentArtwork > representativeArtwork
|
||||
}
|
||||
cur := candidate.SeasonNum*10000 + candidate.EpisodeNum
|
||||
rep := current.SeasonNum*10000 + current.EpisodeNum
|
||||
return cur > 0 && (rep == 0 || cur < rep)
|
||||
}
|
||||
|
||||
func seriesMediaTime(media model.Media) time.Time {
|
||||
if releaseDate := strings.TrimSpace(media.ReleaseDate); releaseDate != "" {
|
||||
if parsed, err := time.Parse("2006-01-02", releaseDate); err == nil {
|
||||
|
||||
@@ -410,15 +410,19 @@ func TestGroupMediaSeriesCardsKeepsTheatricalMovieWithTVSeries(t *testing.T) {
|
||||
LibraryID: "anime",
|
||||
Title: "摇曳露营△",
|
||||
Path: `/media/动漫/摇曳露营△ (2018)/Season 01/摇曳露营△.S01E01.mkv`,
|
||||
PosterURL: "https://image.tmdb.org/t/p/w500/episode.jpg",
|
||||
SeasonNum: 1,
|
||||
EpisodeNum: 1,
|
||||
}
|
||||
theatrical := model.Media{
|
||||
Base: model.Base{ID: "theatrical"},
|
||||
LibraryID: "anime",
|
||||
Title: "摇曳露营△ 剧场版",
|
||||
Path: `/media/动漫/摇曳露营△ (2018)/摇曳露营△ 剧场版 (2022)/Eiga.Yurukyan.2022.Bluray.mkv`,
|
||||
TMDbID: 566466,
|
||||
Base: model.Base{ID: "theatrical"},
|
||||
LibraryID: "anime",
|
||||
Title: "摇曳露营△ 剧场版",
|
||||
Path: `/media/动漫/摇曳露营△ (2018)/摇曳露营△ 剧场版 (2022)/Eiga.Yurukyan.2022.Bluray.mkv`,
|
||||
PosterURL: "/media/动漫/摇曳露营△ (2018)/剧场版/poster.jpg",
|
||||
BackdropURL: "/media/动漫/摇曳露营△ (2018)/剧场版/background.jpg",
|
||||
Overview: "剧场版简介",
|
||||
TMDbID: 566466,
|
||||
}
|
||||
|
||||
cards := groupMediaSeriesCards([]model.Media{episode, theatrical})
|
||||
@@ -428,6 +432,12 @@ func TestGroupMediaSeriesCardsKeepsTheatricalMovieWithTVSeries(t *testing.T) {
|
||||
if cards[0].Count != 2 {
|
||||
t.Fatalf("series card count=%d, want TV episode plus theatrical movie", cards[0].Count)
|
||||
}
|
||||
if cards[0].Rep.ID != episode.ID {
|
||||
t.Fatalf("series representative=%q, want TV episode %q", cards[0].Rep.ID, episode.ID)
|
||||
}
|
||||
if cards[0].Rep.Title != episode.Title {
|
||||
t.Fatalf("series representative title=%q, want %q", cards[0].Rep.Title, episode.Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupMediaSeriesCardsDoesNotCollideMovieAndTVExternalIDs(t *testing.T) {
|
||||
|
||||
@@ -480,20 +480,31 @@ export function groupSeries(items: Media[] = []): SeriesCard[] {
|
||||
if (betterSeriesLinkMedia(m, g.linkMedia)) {
|
||||
g.linkMedia = m
|
||||
}
|
||||
const currentArtwork = artworkScore(m)
|
||||
const representativeArtwork = artworkScore(g.rep)
|
||||
if (currentArtwork > representativeArtwork) {
|
||||
if (betterSeriesRepresentative(m, g.rep)) {
|
||||
g.rep = m
|
||||
} else if (currentArtwork === representativeArtwork) {
|
||||
const cur = (m.season_num ?? 0) * 10000 + (m.episode_num ?? 0)
|
||||
const rep = (g.rep.season_num ?? 0) * 10000 + (g.rep.episode_num ?? 0)
|
||||
if (cur > 0 && (rep === 0 || cur < rep)) g.rep = m
|
||||
}
|
||||
}
|
||||
}
|
||||
return Array.from(groups.values())
|
||||
}
|
||||
|
||||
function betterSeriesRepresentative(candidate: Media, current: Media): boolean {
|
||||
// A theatrical feature can have local poster/background artwork that scores
|
||||
// higher than the TV poster. Keep the TV row as the series-card identity so
|
||||
// the movie cannot replace the whole series title and overview.
|
||||
const candidateTheatrical = isTheatricalFeature(candidate)
|
||||
const currentTheatrical = isTheatricalFeature(current)
|
||||
if (candidateTheatrical !== currentTheatrical) return !candidateTheatrical
|
||||
|
||||
const candidateArtwork = artworkScore(candidate)
|
||||
const currentArtwork = artworkScore(current)
|
||||
if (candidateArtwork !== currentArtwork) return candidateArtwork > currentArtwork
|
||||
|
||||
const cur = (candidate.season_num ?? 0) * 10000 + (candidate.episode_num ?? 0)
|
||||
const rep = (current.season_num ?? 0) * 10000 + (current.episode_num ?? 0)
|
||||
return cur > 0 && (rep === 0 || cur < rep)
|
||||
}
|
||||
|
||||
function repeatedSeriesExternalRawKey(media: Media): string {
|
||||
if (!isEpisodeLike(media) && !pathLooksEpisodic(media)) return ''
|
||||
let identity = ''
|
||||
|
||||
Reference in New Issue
Block a user