disambiguate seasons (#1543)
This commit is contained in:
@@ -26,9 +26,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Fix tray icon shortcut to open logs folder on Windows
|
- Fix tray icon shortcut to open logs folder on Windows
|
||||||
- Unlock playout when playout build fails
|
- Unlock playout when playout build fails
|
||||||
- Ignore errors deleting old HLS segments; this should improve stream reliability
|
- Ignore errors deleting old HLS segments; this should improve stream reliability
|
||||||
|
- Update show year when changed within Plex
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Upgrade from .NET 7 to .NET 8
|
- Upgrade from .NET 7 to .NET 8
|
||||||
|
- In schedule items, disambiguate seasons from shows with the same title by including show year
|
||||||
|
- Old format: `Show Title (Season Number)`
|
||||||
|
- New format: `Show Title (Show Year) - Season Number`
|
||||||
|
|
||||||
## [0.8.4-beta] - 2023-12-02
|
## [0.8.4-beta] - 2023-12-02
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ErsatzTV.Core.Domain;
|
using System.Globalization;
|
||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
|
||||||
namespace ErsatzTV.Application.MediaItems;
|
namespace ErsatzTV.Application.MediaItems;
|
||||||
|
|
||||||
@@ -8,13 +9,27 @@ internal static class Mapper
|
|||||||
new(show.Id, show.ShowMetadata.HeadOrNone().Map(sm => $"{sm?.Title} ({sm?.Year})").IfNone("???"));
|
new(show.Id, show.ShowMetadata.HeadOrNone().Map(sm => $"{sm?.Title} ({sm?.Year})").IfNone("???"));
|
||||||
|
|
||||||
internal static NamedMediaItemViewModel ProjectToViewModel(Season season) =>
|
internal static NamedMediaItemViewModel ProjectToViewModel(Season season) =>
|
||||||
new(season.Id, $"{ShowTitle(season)} ({SeasonDescription(season)})");
|
new(season.Id, $"{ShowTitle(season)} - {SeasonDescription(season)}");
|
||||||
|
|
||||||
internal static NamedMediaItemViewModel ProjectToViewModel(Artist artist) =>
|
internal static NamedMediaItemViewModel ProjectToViewModel(Artist artist) =>
|
||||||
new(artist.Id, artist.ArtistMetadata.HeadOrNone().Match(am => am.Title, () => "???"));
|
new(artist.Id, artist.ArtistMetadata.HeadOrNone().Match(am => am.Title, () => "???"));
|
||||||
|
|
||||||
private static string ShowTitle(Season season) =>
|
private static string ShowTitle(Season season)
|
||||||
season.Show.ShowMetadata.HeadOrNone().Map(sm => sm.Title).IfNone("???");
|
{
|
||||||
|
var title = "???";
|
||||||
|
var year = "???";
|
||||||
|
|
||||||
|
foreach (ShowMetadata show in season.Show.ShowMetadata.HeadOrNone())
|
||||||
|
{
|
||||||
|
title = show.Title;
|
||||||
|
foreach (int y in Optional(show.Year))
|
||||||
|
{
|
||||||
|
year = y.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{title} ({year})";
|
||||||
|
}
|
||||||
|
|
||||||
private static string SeasonDescription(Season season) =>
|
private static string SeasonDescription(Season season) =>
|
||||||
season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}";
|
season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Globalization;
|
||||||
using ErsatzTV.Application.MediaItems;
|
using ErsatzTV.Application.MediaItems;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Infrastructure.Data;
|
using ErsatzTV.Infrastructure.Data;
|
||||||
@@ -25,21 +26,25 @@ public class SearchTelevisionSeasonsHandler : IRequestHandler<SearchTelevisionSe
|
|||||||
where EF.Functions.Like(showMetadata.Title + " " + seasonMetadata.Title, $"%{request.Query}%")
|
where EF.Functions.Like(showMetadata.Title + " " + seasonMetadata.Title, $"%{request.Query}%")
|
||||||
orderby EF.Functions.Collate(showMetadata.Title, TvContext.CaseInsensitiveCollation), season
|
orderby EF.Functions.Collate(showMetadata.Title, TvContext.CaseInsensitiveCollation), season
|
||||||
.SeasonNumber
|
.SeasonNumber
|
||||||
select new TelevisionSeason(season.Id, showMetadata.Title, season.SeasonNumber))
|
select new TelevisionSeason(season.Id, showMetadata.Title, showMetadata.Year, season.SeasonNumber))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
.Map(list => list.Map(ToNamedMediaItem).ToList());
|
.Map(list => list.Map(ToNamedMediaItem).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NamedMediaItemViewModel ToNamedMediaItem(TelevisionSeason season) => new(
|
private static NamedMediaItemViewModel ToNamedMediaItem(TelevisionSeason season) =>
|
||||||
season.Id,
|
new(season.Id, $"{ShowTitle(season)} - {SeasonTitle(season)}");
|
||||||
$"{ShowTitle(season)} ({SeasonTitle(season)})");
|
|
||||||
|
|
||||||
private static string ShowTitle(TelevisionSeason season) => $"{season.Title ?? "???"}";
|
private static string ShowTitle(TelevisionSeason season)
|
||||||
|
{
|
||||||
|
string title = season.Title ?? "???";
|
||||||
|
string year = season.Year.HasValue ? season.Year.Value.ToString(CultureInfo.InvariantCulture) : "???";
|
||||||
|
return $"{title} ({year})";
|
||||||
|
}
|
||||||
|
|
||||||
private static string SeasonTitle(TelevisionSeason season) => season.SeasonNumber == 0
|
private static string SeasonTitle(TelevisionSeason season) => season.SeasonNumber == 0
|
||||||
? "Specials"
|
? "Specials"
|
||||||
: $"Season {season.SeasonNumber}";
|
: $"Season {season.SeasonNumber}";
|
||||||
|
|
||||||
public record TelevisionSeason(int Id, string Title, int SeasonNumber);
|
public record TelevisionSeason(int Id, string Title, int? Year, int SeasonNumber);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ public interface ITelevisionRepository
|
|||||||
Task<bool> UpdateTitles(EpisodeMetadata metadata, string title, string sortTitle);
|
Task<bool> UpdateTitles(EpisodeMetadata metadata, string title, string sortTitle);
|
||||||
Task<bool> UpdateOutline(EpisodeMetadata metadata, string outline);
|
Task<bool> UpdateOutline(EpisodeMetadata metadata, string outline);
|
||||||
Task<bool> UpdatePlot(EpisodeMetadata metadata, string plot);
|
Task<bool> UpdatePlot(EpisodeMetadata metadata, string plot);
|
||||||
|
Task<bool> UpdateYear(ShowMetadata metadata, int? year);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -525,6 +525,14 @@ public class TelevisionRepository : ITelevisionRepository
|
|||||||
new { Plot = plot, MetadataId = metadata.Id }).Map(result => result > 0);
|
new { Plot = plot, MetadataId = metadata.Id }).Map(result => result > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UpdateYear(ShowMetadata metadata, int? year)
|
||||||
|
{
|
||||||
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Connection.ExecuteAsync(
|
||||||
|
"UPDATE ShowMetadata SET Year = @Year WHERE Id = @MetadataId",
|
||||||
|
new { Year = year, MetadataId = metadata.Id }).Map(result => result > 0);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<Episode>> GetShowItems(int showId)
|
public async Task<List<Episode>> GetShowItems(int showId)
|
||||||
{
|
{
|
||||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|||||||
@@ -441,6 +441,14 @@ public class PlexTelevisionLibraryScanner :
|
|||||||
result.IsUpdated = true;
|
result.IsUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (existingMetadata.Year != fullMetadata.Year)
|
||||||
|
{
|
||||||
|
if (await _televisionRepository.UpdateYear(existingMetadata, fullMetadata.Year))
|
||||||
|
{
|
||||||
|
result.IsUpdated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result.IsUpdated)
|
if (result.IsUpdated)
|
||||||
{
|
{
|
||||||
await _metadataRepository.MarkAsUpdated(existingMetadata, fullMetadata.DateUpdated);
|
await _metadataRepository.MarkAsUpdated(existingMetadata, fullMetadata.DateUpdated);
|
||||||
|
|||||||
Reference in New Issue
Block a user