using ErsatzTV.Core.Domain; using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Health; using ErsatzTV.Core.Health.Checks; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace ErsatzTV.Infrastructure.Health.Checks; public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck { private readonly IDbContextFactory _dbContextFactory; public FileNotFoundHealthCheck(IDbContextFactory dbContextFactory) => _dbContextFactory = dbContextFactory; public override string Title => "File Not Found"; public async Task Check(CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); IQueryable mediaItems = dbContext.MediaItems .Filter(mi => mi.State == MediaItemState.FileNotFound) .Include(mi => (mi as Episode).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as Movie).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as MusicVideo).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as OtherVideo).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as Song).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as Show).ShowMetadata) .Include(mi => (mi as Season).Show) .ThenInclude(s => s.ShowMetadata) .Include(mi => (mi as Season).SeasonMetadata); List five = await mediaItems .OrderBy(mi => mi.Id) .Take(5) .ToListAsync(cancellationToken); if (mediaItems.Any()) { IEnumerable paths = five.Map( mi => mi switch { Show s => s.ShowMetadata.Head().Title, Season s => $"{s.Show.ShowMetadata.Head().Title} Season {s.SeasonNumber}", _ => mi.GetHeadVersion().MediaFiles.Head().Path }); var files = string.Join(", ", paths); int count = await mediaItems.CountAsync(cancellationToken); return WarningResult( $"There are {count} items that do not exist on disk, including the following: {files}", "media/trash"); } return OkResult(); } }