using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; namespace ErsatzTV.Application.MediaCollections; public class ReplacePlaylistItemsHandler(IDbContextFactory dbContextFactory) : IRequestHandler>> { public async Task>> Handle( ReplacePlaylistItems request, CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Validation validation = await Validate(dbContext, request); return await validation.Apply(ps => Persist(dbContext, request, ps)); } private static async Task> Persist( TvContext dbContext, ReplacePlaylistItems request, Playlist playlist) { playlist.Name = request.Name; //playlist.DateUpdated = DateTime.UtcNow; dbContext.RemoveRange(playlist.Items); playlist.Items = request.Items.Map(i => BuildItem(playlist, i.Index, i)).ToList(); await dbContext.SaveChangesAsync(); return playlist.Items.Map(Mapper.ProjectToViewModel).ToList(); } private static PlaylistItem BuildItem(Playlist playlist, int index, ReplacePlaylistItem item) => new() { PlaylistId = playlist.Id, Index = index, CollectionType = item.CollectionType, CollectionId = item.CollectionId, MultiCollectionId = item.MultiCollectionId, SmartCollectionId = item.SmartCollectionId, MediaItemId = item.MediaItemId, PlaybackOrder = item.PlaybackOrder, PlayAll = item.PlayAll, IncludeInProgramGuide = item.IncludeInProgramGuide }; private static Task> Validate(TvContext dbContext, ReplacePlaylistItems request) => PlaylistMustExist(dbContext, request.PlaylistId) .BindT(playlist => CollectionTypesMustBeValid(request, playlist)); private static Task> PlaylistMustExist(TvContext dbContext, int playlistId) => dbContext.Playlists .Include(b => b.Items) .SelectOneAsync(b => b.Id, b => b.Id == playlistId) .Map(o => o.ToValidation("[PlaylistId] does not exist.")); private static Validation CollectionTypesMustBeValid( ReplacePlaylistItems request, Playlist playlist) => request.Items.Map(item => CollectionTypeMustBeValid(item, playlist)).Sequence().Map(_ => playlist); private static Validation CollectionTypeMustBeValid( ReplacePlaylistItem item, Playlist playlist) { switch (item.CollectionType) { case ProgramScheduleItemCollectionType.Collection: if (item.CollectionId is null) { return BaseError.New("[Collection] is required for collection type 'Collection'"); } break; case ProgramScheduleItemCollectionType.TelevisionShow: if (item.MediaItemId is null) { return BaseError.New("[MediaItem] is required for collection type 'TelevisionShow'"); } break; case ProgramScheduleItemCollectionType.TelevisionSeason: if (item.MediaItemId is null) { return BaseError.New("[MediaItem] is required for collection type 'TelevisionSeason'"); } break; case ProgramScheduleItemCollectionType.Artist: if (item.MediaItemId is null) { return BaseError.New("[MediaItem] is required for collection type 'Artist'"); } break; case ProgramScheduleItemCollectionType.MultiCollection: if (item.MultiCollectionId is null) { return BaseError.New("[MultiCollection] is required for collection type 'MultiCollection'"); } break; case ProgramScheduleItemCollectionType.SmartCollection: if (item.SmartCollectionId is null) { return BaseError.New("[SmartCollection] is required for collection type 'SmartCollection'"); } break; case ProgramScheduleItemCollectionType.Movie: case ProgramScheduleItemCollectionType.Episode: case ProgramScheduleItemCollectionType.MusicVideo: case ProgramScheduleItemCollectionType.OtherVideo: case ProgramScheduleItemCollectionType.Song: case ProgramScheduleItemCollectionType.Image: if (item.MediaItemId is null) { return BaseError.New($"[MediaItem] is required for type '{item.CollectionType}'"); } break; case ProgramScheduleItemCollectionType.FakeCollection: default: return BaseError.New("[CollectionType] is invalid"); } return playlist; } }