Browse Source

clean up old show artwork; properly update season artwork

pull/2778/head
Jason Dove 7 months ago
parent
commit
1837fb7c56
No known key found for this signature in database
  1. 2
      CHANGELOG.md
  2. 34
      ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs
  3. 9
      ErsatzTV.Scanner/Core/Metadata/LocalMetadataProvider.cs
  4. 66
      ErsatzTV.Scanner/Core/Metadata/TelevisionFolderScanner.cs

2
CHANGELOG.md

@ -25,7 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix 500 errors when serving XMLTV due to concurrent file reads and writes - Fix 500 errors when serving XMLTV due to concurrent file reads and writes
- Fix playback of AC3 audio when targeting stereo output and input layout changes mid-stream - Fix playback of AC3 audio when targeting stereo output and input layout changes mid-stream
- Use other video artwork in XMLTV template - Use other video artwork in XMLTV template
- Remove artwork from other videos when artwork has been removed from disk - Properly update (add or remove) artwork for all local media libraries when files have changed
## [26.1.1] - 2026-01-08 ## [26.1.1] - 2026-01-08
### Fixed ### Fixed

34
ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs

@ -271,17 +271,18 @@ public class TelevisionRepository : ITelevisionRepository
try try
{ {
metadata.DateAdded = DateTime.UtcNow; metadata.DateAdded = DateTime.UtcNow;
metadata.Genres ??= new List<Genre>(); metadata.Genres ??= [];
metadata.Tags ??= new List<Tag>(); metadata.Tags ??= [];
metadata.Studios ??= new List<Studio>(); metadata.Studios ??= [];
metadata.Actors ??= new List<Actor>(); metadata.Actors ??= [];
metadata.Guids ??= new List<MetadataGuid>(); metadata.Guids ??= [];
metadata.Artwork ??= [];
var show = new Show var show = new Show
{ {
LibraryPathId = libraryPathId, LibraryPathId = libraryPathId,
ShowMetadata = new List<ShowMetadata> { metadata }, ShowMetadata = [metadata],
Seasons = new List<Season>(), Seasons = [],
TraktListItems = new List<TraktListItem>() TraktListItems = []
}; };
await dbContext.Shows.AddAsync(show); await dbContext.Shows.AddAsync(show);
@ -670,17 +671,18 @@ public class TelevisionRepository : ITelevisionRepository
LibraryPathId = libraryPathId, LibraryPathId = libraryPathId,
ShowId = show.Id, ShowId = show.Id,
SeasonNumber = seasonNumber, SeasonNumber = seasonNumber,
Episodes = new List<Episode>(), Episodes = [],
SeasonMetadata = new List<SeasonMetadata> SeasonMetadata =
{ [
new() new SeasonMetadata
{ {
DateAdded = DateTime.UtcNow, DateAdded = DateTime.UtcNow,
Guids = new List<MetadataGuid>(), Guids = [],
Tags = new List<Tag>() Tags = [],
Artwork = []
} }
}, ],
TraktListItems = new List<TraktListItem>() TraktListItems = []
}; };
await dbContext.Seasons.AddAsync(season); await dbContext.Seasons.AddAsync(season);
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();

9
ErsatzTV.Scanner/Core/Metadata/LocalMetadataProvider.cs

@ -563,7 +563,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
{ {
var updated = false; var updated = false;
episode.EpisodeMetadata ??= new List<EpisodeMetadata>(); episode.EpisodeMetadata ??= [];
var toUpdate = episode.EpisodeMetadata var toUpdate = episode.EpisodeMetadata
.Where(em => episodeMetadata.Any(em2 => em2.EpisodeNumber == em.EpisodeNumber)) .Where(em => episodeMetadata.Any(em2 => em2.EpisodeNumber == em.EpisodeNumber))
@ -1317,7 +1317,8 @@ public class LocalMetadataProvider : ILocalMetadataProvider
Actors = Actors(nfo.Actors, dateAdded, dateUpdated), Actors = Actors(nfo.Actors, dateAdded, dateUpdated),
Guids = nfo.UniqueIds Guids = nfo.UniqueIds
.Map(id => new MetadataGuid { Guid = $"{id.Type}://{id.Guid}" }) .Map(id => new MetadataGuid { Guid = $"{id.Type}://{id.Guid}" })
.ToList() .ToList(),
Artwork = []
}; };
} }
@ -1409,8 +1410,8 @@ public class LocalMetadataProvider : ILocalMetadataProvider
Writers = nfo.Writers.Map(w => new Writer { Name = w }).ToList(), Writers = nfo.Writers.Map(w => new Writer { Name = w }).ToList(),
Genres = nfo.Genres.Map(g => new Genre { Name = g }).ToList(), Genres = nfo.Genres.Map(g => new Genre { Name = g }).ToList(),
Tags = nfo.Tags.Map(t => new Tag { Name = t }).ToList(), Tags = nfo.Tags.Map(t => new Tag { Name = t }).ToList(),
Studios = new List<Studio>(), Studios = [],
Artwork = new List<Artwork>() Artwork = []
}; };
result.Add(metadata); result.Add(metadata);

66
ErsatzTV.Scanner/Core/Metadata/TelevisionFolderScanner.cs

@ -248,17 +248,15 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
maybeParentFolder, maybeParentFolder,
seasonFolder); seasonFolder);
// skip folder if etag matches // cache etag match for later checking
if (knownFolder.Etag == etag) // we still need to scan the season folder in case season artwork has changed
bool etagMatch = knownFolder.Etag == etag;
if (etagMatch)
{ {
if (allTrashedItems.Any(f => f.StartsWith(seasonFolder, StringComparison.OrdinalIgnoreCase))) if (allTrashedItems.Any(f => f.StartsWith(seasonFolder, StringComparison.OrdinalIgnoreCase)))
{ {
_logger.LogDebug("Previously trashed items are now present in folder {Folder}", seasonFolder); _logger.LogDebug("Previously trashed items are now present in folder {Folder}", seasonFolder);
} etagMatch = false;
else
{
// etag matches and no trashed items are now present, continue to next folder
continue;
} }
} }
@ -280,6 +278,12 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
foreach (Season season in maybeSeason.RightToSeq()) foreach (Season season in maybeSeason.RightToSeq())
{ {
// skip scanning episodes when season folder etag matches
if (etagMatch)
{
continue;
}
Either<BaseError, Unit> scanResult = await ScanEpisodes( Either<BaseError, Unit> scanResult = await ScanEpisodes(
libraryPath, libraryPath,
knownFolder, knownFolder,
@ -410,7 +414,7 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
private async Task<Either<BaseError, Season>> EnsureMetadataExists(Season season) private async Task<Either<BaseError, Season>> EnsureMetadataExists(Season season)
{ {
season.SeasonMetadata ??= new List<SeasonMetadata>(); season.SeasonMetadata ??= [];
if (season.SeasonMetadata.Count == 0) if (season.SeasonMetadata.Count == 0)
{ {
@ -419,8 +423,9 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
SeasonId = season.Id, SeasonId = season.Id,
Season = season, Season = season,
DateAdded = DateTime.UtcNow, DateAdded = DateTime.UtcNow,
Guids = new List<MetadataGuid>(), Guids = [],
Tags = new List<Tag>() Tags = [],
Artwork = []
}; };
season.SeasonMetadata.Add(metadata); season.SeasonMetadata.Add(metadata);
@ -492,11 +497,18 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
try try
{ {
Show show = result.Item; Show show = result.Item;
Option<string> maybeArtwork = LocateArtworkForShow(showFolder, artworkKind); foreach (ShowMetadata metadata in show.ShowMetadata.HeadOrNone())
foreach (string artworkFile in maybeArtwork)
{ {
ShowMetadata metadata = show.ShowMetadata.Head(); Option<string> maybeArtwork = LocateArtworkForShow(showFolder, artworkKind);
await RefreshArtwork(artworkFile, metadata, artworkKind, None, None, cancellationToken); foreach (string artworkFile in maybeArtwork)
{
await RefreshArtwork(artworkFile, metadata, artworkKind, None, None, cancellationToken);
}
if (maybeArtwork.IsNone && metadata.Artwork.Any(a => a.ArtworkKind == artworkKind))
{
await _metadataRepository.RemoveArtworkWithKind(metadata, artworkKind);
}
} }
return result; return result;
@ -515,11 +527,18 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
{ {
try try
{ {
Option<string> maybePoster = LocatePoster(season, seasonFolder); foreach (SeasonMetadata metadata in season.SeasonMetadata.HeadOrNone())
foreach (string posterFile in maybePoster)
{ {
SeasonMetadata metadata = season.SeasonMetadata.Head(); Option<string> maybePoster = LocatePoster(season, seasonFolder);
await RefreshArtwork(posterFile, metadata, ArtworkKind.Poster, None, None, cancellationToken); foreach (string posterFile in maybePoster)
{
await RefreshArtwork(posterFile, metadata, ArtworkKind.Poster, None, None, cancellationToken);
}
if (maybePoster.IsNone && metadata.Artwork.Any(a => a.ArtworkKind is ArtworkKind.Poster))
{
await _metadataRepository.RemoveArtworkWithKind(metadata, ArtworkKind.Poster);
}
} }
return season; return season;
@ -535,10 +554,10 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
{ {
try try
{ {
Option<string> maybeThumbnail = LocateThumbnail(episode); foreach (EpisodeMetadata metadata in episode.EpisodeMetadata)
foreach (string thumbnailFile in maybeThumbnail)
{ {
foreach (EpisodeMetadata metadata in episode.EpisodeMetadata) Option<string> maybeThumbnail = LocateThumbnail(episode);
foreach (string thumbnailFile in maybeThumbnail)
{ {
await RefreshArtwork( await RefreshArtwork(
thumbnailFile, thumbnailFile,
@ -548,6 +567,11 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan
None, None,
cancellationToken); cancellationToken);
} }
if (maybeThumbnail.IsNone && metadata.Artwork.Any(a => a.ArtworkKind is ArtworkKind.Thumbnail))
{
await _metadataRepository.RemoveArtworkWithKind(metadata, ArtworkKind.Thumbnail);
}
} }
return episode; return episode;

Loading…
Cancel
Save