From e685ad8d957f44d2e3a2af7bd43caacb86031b7c Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Fri, 12 Feb 2021 06:38:10 -0600 Subject: [PATCH] remove last use of dbcontextfactory --- .../AddItemsToSimpleMediaCollection.cs | 9 ++ .../AddItemsToSimpleMediaCollectionHandler.cs | 79 ++++++++++++++++ ... => UpdateSimpleMediaCollectionHandler.cs} | 0 .../GetSimpleMediaCollectionWithItemsById.cs | 11 +++ ...mpleMediaCollectionWithItemsByIdHandler.cs | 37 ++++++++ ErsatzTV.Application/MediaItems/Mapper.cs | 22 +++++ .../MediaItemSearchResultViewModel.cs | 9 ++ .../Queries/GetAllMediaItemsHandler.cs | 5 +- .../MediaItems/Queries/SearchAllMediaItems.cs | 7 ++ .../Queries/SearchAllMediaItemsHandler.cs | 23 +++++ ErsatzTV.Application/Playouts/Mapper.cs | 2 +- .../Fakes/FakeMediaCollectionRepository.cs | 3 + .../IMediaCollectionRepository.cs | 1 + .../Repositories/IMediaItemRepository.cs | 1 + ErsatzTV.Core/Iptv/ChannelGuide.cs | 8 +- .../Metadata/LocalStatisticsProvider.cs | 6 +- .../Repositories/MediaCollectionRepository.cs | 7 ++ .../Data/Repositories/MediaItemRepository.cs | 14 +++ ErsatzTV/Models/UI/MediaItemExtensions.cs | 25 ------ .../Pages/MediaCollectionItemsEditor.razor | 89 +++++++++---------- ErsatzTV/Pages/MediaItems.razor | 1 - ErsatzTV/Program.cs | 2 +- ErsatzTV/Shared/MainLayout.razor | 2 +- ErsatzTV/Startup.cs | 20 ++--- ErsatzTV/_Imports.razor | 2 - 25 files changed, 284 insertions(+), 101 deletions(-) create mode 100644 ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs create mode 100644 ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs rename ErsatzTV.Application/MediaCollections/Commands/{UpdateChannelHandler.cs => UpdateSimpleMediaCollectionHandler.cs} (100%) create mode 100644 ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs create mode 100644 ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs create mode 100644 ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs create mode 100644 ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs create mode 100644 ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs delete mode 100644 ErsatzTV/Models/UI/MediaItemExtensions.cs diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs new file mode 100644 index 000000000..a3d1a2d77 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollection.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using ErsatzTV.Core; +using LanguageExt; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public record AddItemsToSimpleMediaCollection + (int MediaCollectionId, List ItemIds) : MediatR.IRequest>; +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs new file mode 100644 index 000000000..a94211d8e --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToSimpleMediaCollectionHandler.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using static LanguageExt.Prelude; + +namespace ErsatzTV.Application.MediaCollections.Commands +{ + public class + AddItemsToSimpleMediaCollectionHandler : MediatR.IRequestHandler> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + private readonly IMediaItemRepository _mediaItemRepository; + + public AddItemsToSimpleMediaCollectionHandler( + IMediaCollectionRepository mediaCollectionRepository, + IMediaItemRepository mediaItemRepository) + { + _mediaCollectionRepository = mediaCollectionRepository; + _mediaItemRepository = mediaItemRepository; + } + + public Task> Handle( + AddItemsToSimpleMediaCollection request, + CancellationToken cancellationToken) => + Validate(request) + .MapT(ApplyAddItemsRequest) + .Bind(v => v.ToEitherAsync()); + + private async Task ApplyAddItemsRequest(RequestParameters parameters) + { + foreach (MediaItem item in parameters.ItemsToAdd.Where( + item => parameters.Collection.Items.All(i => i.Id != item.Id))) + { + parameters.Collection.Items.Add(item); + } + + await _mediaCollectionRepository.Update(parameters.Collection); + + return Unit.Default; + } + + private async Task> + Validate(AddItemsToSimpleMediaCollection request) => + (await SimpleMediaCollectionMustExist(request), await ValidateItems(request)) + .Apply( + (simpleMediaCollectionToUpdate, itemsToAdd) => + new RequestParameters(simpleMediaCollectionToUpdate, itemsToAdd)); + + private Task> SimpleMediaCollectionMustExist( + AddItemsToSimpleMediaCollection updateSimpleMediaCollection) => + _mediaCollectionRepository.GetSimpleMediaCollection(updateSimpleMediaCollection.MediaCollectionId) + .Map(v => v.ToValidation("SimpleMediaCollection does not exist.")); + + private Task>> ValidateItems( + AddItemsToSimpleMediaCollection request) => + LoadAllMediaItems(request) + .Map(v => v.ToValidation("MediaItem does not exist")); + + private async Task>> LoadAllMediaItems(AddItemsToSimpleMediaCollection request) + { + var items = (await request.ItemIds.Map(async id => await _mediaItemRepository.Get(id)).Sequence()) + .ToList(); + if (items.Any(i => i.IsNone)) + { + return None; + } + + return items.Somes().ToList(); + } + + private record RequestParameters(SimpleMediaCollection Collection, List ItemsToAdd); + } +} diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateChannelHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs similarity index 100% rename from ErsatzTV.Application/MediaCollections/Commands/UpdateChannelHandler.cs rename to ErsatzTV.Application/MediaCollections/Commands/UpdateSimpleMediaCollectionHandler.cs diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs new file mode 100644 index 000000000..470ff51a2 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsById.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using ErsatzTV.Application.MediaItems; +using LanguageExt; +using MediatR; + +namespace ErsatzTV.Application.MediaCollections.Queries +{ + public record GetSimpleMediaCollectionWithItemsById + (int Id) : IRequest>>>; +} diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs new file mode 100644 index 000000000..11f9f79b3 --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSimpleMediaCollectionWithItemsByIdHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Application.MediaItems; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static LanguageExt.Prelude; +using static ErsatzTV.Application.MediaCollections.Mapper; +using static ErsatzTV.Application.MediaItems.Mapper; + +namespace ErsatzTV.Application.MediaCollections.Queries +{ + public class GetSimpleMediaCollectionWithItemsByIdHandler : IRequestHandler>>> + { + private readonly IMediaCollectionRepository _mediaCollectionRepository; + + public GetSimpleMediaCollectionWithItemsByIdHandler(IMediaCollectionRepository mediaCollectionRepository) => + _mediaCollectionRepository = mediaCollectionRepository; + + public async Task>>> Handle( + GetSimpleMediaCollectionWithItemsById request, + CancellationToken cancellationToken) + { + Option maybeCollection = + await _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(request.Id); + + return maybeCollection.Match>>>( + c => Tuple(ProjectToViewModel(c), c.Items.Map(ProjectToSearchViewModel).ToList()), + None); + } + } +} diff --git a/ErsatzTV.Application/MediaItems/Mapper.cs b/ErsatzTV.Application/MediaItems/Mapper.cs index fdac17817..e72cd160e 100644 --- a/ErsatzTV.Application/MediaItems/Mapper.cs +++ b/ErsatzTV.Application/MediaItems/Mapper.cs @@ -1,4 +1,5 @@ using ErsatzTV.Core.Domain; +using static LanguageExt.Prelude; namespace ErsatzTV.Application.MediaItems { @@ -9,5 +10,26 @@ namespace ErsatzTV.Application.MediaItems mediaItem.Id, mediaItem.MediaSourceId, mediaItem.Path); + + internal static MediaItemSearchResultViewModel ProjectToSearchViewModel(MediaItem mediaItem) => + new( + mediaItem.Id, + mediaItem.Source.Name, + mediaItem.Metadata.MediaType.ToString(), + GetDisplayTitle(mediaItem), + GetDisplayDuration(mediaItem)); + + + private static string GetDisplayTitle(this MediaItem mediaItem) => + mediaItem.Metadata.MediaType == MediaType.TvShow && + Optional(mediaItem.Metadata.SeasonNumber).IsSome && + Optional(mediaItem.Metadata.EpisodeNumber).IsSome + ? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}" + : mediaItem.Metadata.Title; + + private static string GetDisplayDuration(MediaItem mediaItem) => + string.Format( + mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", + mediaItem.Metadata.Duration); } } diff --git a/ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs b/ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs new file mode 100644 index 000000000..6ff72c912 --- /dev/null +++ b/ErsatzTV.Application/MediaItems/MediaItemSearchResultViewModel.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Application.MediaItems +{ + public record MediaItemSearchResultViewModel( + int Id, + string Source, + string MediaType, + string Title, + string Duration); +} diff --git a/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs b/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs index 2806e91d0..e646db23d 100644 --- a/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs +++ b/ErsatzTV.Application/MediaItems/Queries/GetAllMediaItemsHandler.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; using MediatR; using static ErsatzTV.Application.MediaItems.Mapper; @@ -15,9 +16,9 @@ namespace ErsatzTV.Application.MediaItems.Queries public GetAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) => _mediaItemRepository = mediaItemRepository; - public async Task> Handle( + public Task> Handle( GetAllMediaItems request, CancellationToken cancellationToken) => - (await _mediaItemRepository.GetAll()).Map(ProjectToViewModel).ToList(); + _mediaItemRepository.GetAll().Map(list => list.Map(ProjectToViewModel).ToList()); } } diff --git a/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs new file mode 100644 index 000000000..f4227717b --- /dev/null +++ b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItems.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using MediatR; + +namespace ErsatzTV.Application.MediaItems.Queries +{ + public record SearchAllMediaItems(string SearchString) : IRequest>; +} diff --git a/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs new file mode 100644 index 000000000..52005df98 --- /dev/null +++ b/ErsatzTV.Application/MediaItems/Queries/SearchAllMediaItemsHandler.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ErsatzTV.Core.Interfaces.Repositories; +using LanguageExt; +using MediatR; +using static ErsatzTV.Application.MediaItems.Mapper; + +namespace ErsatzTV.Application.MediaItems.Queries +{ + public class SearchAllMediaItemsHandler : IRequestHandler> + { + private readonly IMediaItemRepository _mediaItemRepository; + + public SearchAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) => + _mediaItemRepository = mediaItemRepository; + + public Task> + Handle(SearchAllMediaItems request, CancellationToken cancellationToken) => + _mediaItemRepository.Search(request.SearchString).Map(list => list.Map(ProjectToSearchViewModel).ToList()); + } +} diff --git a/ErsatzTV.Application/Playouts/Mapper.cs b/ErsatzTV.Application/Playouts/Mapper.cs index 7aba77ac4..937939822 100644 --- a/ErsatzTV.Application/Playouts/Mapper.cs +++ b/ErsatzTV.Application/Playouts/Mapper.cs @@ -28,7 +28,7 @@ namespace ErsatzTV.Application.Playouts ? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}" : mediaItem.Metadata.Title; - public static string GetDisplayDuration(MediaItem mediaItem) => + private static string GetDisplayDuration(MediaItem mediaItem) => string.Format( mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", mediaItem.Metadata.Duration); diff --git a/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs b/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs index ecf65ee2a..be4e22d7b 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeMediaCollectionRepository.cs @@ -22,6 +22,9 @@ namespace ErsatzTV.Core.Tests.Fakes public Task> GetSimpleMediaCollection(int id) => throw new NotSupportedException(); + public Task> GetSimpleMediaCollectionWithItems(int id) => + throw new NotSupportedException(); + public Task> GetTelevisionMediaCollection(int id) => throw new NotSupportedException(); diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs index 4ce9801c2..afc866430 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaCollectionRepository.cs @@ -11,6 +11,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories public Task Add(SimpleMediaCollection collection); public Task> Get(int id); public Task> GetSimpleMediaCollection(int id); + public Task> GetSimpleMediaCollectionWithItems(int id); public Task> GetTelevisionMediaCollection(int id); public Task> GetSimpleMediaCollections(); public Task> GetAll(); diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs index 93df278cc..9c6961c4c 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaItemRepository.cs @@ -10,6 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories public Task Add(MediaItem mediaItem); public Task> Get(int id); public Task> GetAll(); + public Task> Search(string searchString); public Task> GetAll(MediaType mediaType); public Task> GetAllByMediaSourceId(int mediaSourceId); public Task Update(MediaItem mediaItem); diff --git a/ErsatzTV.Core/Iptv/ChannelGuide.cs b/ErsatzTV.Core/Iptv/ChannelGuide.cs index 8465ff146..f4f71b886 100644 --- a/ErsatzTV.Core/Iptv/ChannelGuide.cs +++ b/ErsatzTV.Core/Iptv/ChannelGuide.cs @@ -26,15 +26,15 @@ namespace ErsatzTV.Core.Iptv using var ms = new MemoryStream(); using var xml = XmlWriter.Create(ms); xml.WriteStartDocument(); - + xml.WriteStartElement("tv"); xml.WriteAttributeString("generator-info-name", "ersatztv"); - + foreach (Channel channel in _channels) { xml.WriteStartElement("channel"); xml.WriteAttributeString("id", channel.Number.ToString()); - + xml.WriteStartElement("display-name"); xml.WriteAttributeString("lang", "en"); xml.WriteString(channel.Name); @@ -72,7 +72,7 @@ namespace ErsatzTV.Core.Iptv xml.WriteAttributeString("lang", "en"); xml.WriteString(metadata.Title); xml.WriteEndElement(); // title - + xml.WriteStartElement("previously-shown"); xml.WriteEndElement(); // previously-shown diff --git a/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs b/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs index cb611e4f4..745b49a77 100644 --- a/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs +++ b/ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs @@ -15,10 +15,12 @@ namespace ErsatzTV.Core.Metadata { public class LocalStatisticsProvider : ILocalStatisticsProvider { - private readonly IMediaItemRepository _mediaItemRepository; private readonly ILogger _logger; + private readonly IMediaItemRepository _mediaItemRepository; - public LocalStatisticsProvider(IMediaItemRepository mediaItemRepository, ILogger logger) + public LocalStatisticsProvider( + IMediaItemRepository mediaItemRepository, + ILogger logger) { _mediaItemRepository = mediaItemRepository; _logger = logger; diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs index 7d2052a17..e767108a7 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs @@ -29,6 +29,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public Task> GetSimpleMediaCollection(int id) => Get(id).Map(c => c.OfType().HeadOrNone()); + public Task> GetSimpleMediaCollectionWithItems(int id) => + _dbContext.SimpleMediaCollections + .Include(s => s.Items) + .ThenInclude(i => i.Source) + .SingleOrDefaultAsync(c => c.Id == id) + .Map(Optional); + public Task> GetTelevisionMediaCollection(int id) => Get(id).Map(c => c.OfType().HeadOrNone()); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs index 92c85f6ba..670b67d81 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaItemRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; @@ -26,6 +27,19 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public Task> GetAll() => _dbContext.MediaItems.ToListAsync(); + public Task> Search(string searchString) + { + IQueryable data = from c in _dbContext.MediaItems.Include(c => c.Source) select c; + + if (!string.IsNullOrEmpty(searchString)) + { + data = data.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%")); + } + + return data.ToListAsync(); + } + + public Task> GetAll(MediaType mediaType) => _dbContext.MediaItems .Include(i => i.Source) diff --git a/ErsatzTV/Models/UI/MediaItemExtensions.cs b/ErsatzTV/Models/UI/MediaItemExtensions.cs deleted file mode 100644 index 46f41f351..000000000 --- a/ErsatzTV/Models/UI/MediaItemExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ErsatzTV.Core.Domain; -using static LanguageExt.Prelude; - -namespace ErsatzTV.Models.UI -{ - public static class MediaItemExtensions - { - public static string GetDisplayDuration(this MediaItem mediaItem) => - string.Format( - mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}", - mediaItem.Metadata.Duration); - - public static string GetDisplayTitle(this MediaItem mediaItem) => - mediaItem.Metadata.MediaType == MediaType.TvShow && - Optional(mediaItem.Metadata.SeasonNumber).IsSome && - Optional(mediaItem.Metadata.EpisodeNumber).IsSome - ? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}" - : mediaItem.Metadata.Title; - - public static string GetDisplayMediaType(this MediaItem mediaItem) => - mediaItem.Metadata.MediaType == MediaType.TvShow - ? "TV Show" - : mediaItem.Metadata.MediaType.ToString(); - } -} diff --git a/ErsatzTV/Pages/MediaCollectionItemsEditor.razor b/ErsatzTV/Pages/MediaCollectionItemsEditor.razor index 929dd0fa4..fbe2df0ce 100644 --- a/ErsatzTV/Pages/MediaCollectionItemsEditor.razor +++ b/ErsatzTV/Pages/MediaCollectionItemsEditor.razor @@ -1,8 +1,16 @@ @page "/media/collections/{Id:int}/items" -@inject IDbContextFactory DbFactory +@using ErsatzTV.Application.MediaCollections +@using ErsatzTV.Application.MediaCollections.Commands +@using ErsatzTV.Application.MediaCollections.Queries +@using ErsatzTV.Application.MediaItems +@using ErsatzTV.Application.MediaItems.Queries +@using Unit = LanguageExt.Unit @inject NavigationManager NavigationManager +@inject IMediator Mediator +@inject ILogger Logger +@inject ISnackbar Snackbar - + @_mediaCollection.Name Media Items @@ -13,20 +21,20 @@ Duration - @context.Source.Name - @context.GetDisplayMediaType() - @context.GetDisplayTitle() - @context.Metadata.Duration.ToString(@"hh\:mm\:ss\.fff") + @context.Source + @context.MediaType + @context.Title + @context.Duration - @if (_mediaCollection.Items.Count > 0) + @if (_collectionItems.Any()) { } - + All Media Items @@ -41,10 +49,10 @@ Duration - @context.Source.Name - @context.GetDisplayMediaType() - @context.GetDisplayTitle() - @context.GetDisplayDuration() + @context.Source + @context.MediaType + @context.Title + @context.Duration @@ -59,32 +67,27 @@ [Parameter] public int Id { get; set; } - private SimpleMediaCollection _mediaCollection; + private MediaCollectionViewModel _mediaCollection; + private IEnumerable _collectionItems; protected override async Task OnParametersSetAsync() => await LoadMediaCollectionAsync(); private List _mediaItemIds; - private IEnumerable _pagedData; - private MudTable _table; + private IEnumerable _pagedData; + private MudTable _table; private int _totalItems; private string _searchString; - private async Task> ServerReload(TableState state) + private async Task> ServerReload(TableState state) { - await using TvContext context = DbFactory.CreateDbContext(); - IQueryable data = from c in context.MediaItems.Include(c => c.Source) select c; - - if (!string.IsNullOrEmpty(_searchString)) - { - data = data.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{_searchString}%")); - } + List data = await Mediator.Send(new SearchAllMediaItems(_searchString)); _mediaItemIds = data.Map(c => c.Id).ToList(); - _totalItems = data.Count(); + _totalItems = data.Count; _pagedData = data.OrderBy(c => c.Id).Skip(state.Page * state.PageSize).Take(state.PageSize).ToArray(); - return new TableData { TotalItems = _totalItems, Items = _pagedData }; + return new TableData { TotalItems = _totalItems, Items = _pagedData }; } private async Task OnSearch(string text) @@ -95,32 +98,24 @@ private async Task AddResultsAsync() { - await using TvContext context = DbFactory.CreateDbContext(); - - SimpleMediaCollection mediaCollection = await context.SimpleMediaCollections.FindAsync(_mediaCollection.Id); - await context.Entry(mediaCollection).Collection(cg => cg.Items).LoadAsync(); - IEnumerable existingMediaItems = _mediaCollection.Items.Select(c => c.Id); - IQueryable mediaItemsToAdd = from c in context.MediaItems - where _mediaItemIds.Contains(c.Id) && !existingMediaItems.Contains(c.Id) - select c; - foreach (MediaItem mediaItem in mediaItemsToAdd) - { - mediaCollection.Items.Add(mediaItem); - } - context.MediaCollections.Update(mediaCollection); - await context.SaveChangesAsync(); - - await LoadMediaCollectionAsync(); + Either result = await Mediator.Send(new AddItemsToSimpleMediaCollection(Id, _mediaItemIds)); + await result.Match( + async _ => await LoadMediaCollectionAsync(), + error => + { + Snackbar.Add(error.Value, Severity.Error); + Logger.LogError("Error adding items to media collection: {Error}", error.Value); + return Task.CompletedTask; + }); } private async Task LoadMediaCollectionAsync() { - await using TvContext context = DbFactory.CreateDbContext(); - _mediaCollection = await context.SimpleMediaCollections - .AsNoTracking() - .Include(cg => cg.Items) - .ThenInclude(c => c.Source) - .FirstAsync(cg => cg.Id == Id); + Option>> maybeResult = + await Mediator.Send(new GetSimpleMediaCollectionWithItemsById(Id)); + maybeResult.Match( + result => (_mediaCollection, _collectionItems) = result, + () => NavigationManager.NavigateTo("404")); } } \ No newline at end of file diff --git a/ErsatzTV/Pages/MediaItems.razor b/ErsatzTV/Pages/MediaItems.razor index 5dd78d151..63bf3e387 100644 --- a/ErsatzTV/Pages/MediaItems.razor +++ b/ErsatzTV/Pages/MediaItems.razor @@ -1,5 +1,4 @@ @page "/media/items" -@inject IDbContextFactory DbFactory diff --git a/ErsatzTV/Program.cs b/ErsatzTV/Program.cs index d9d32a895..517746095 100644 --- a/ErsatzTV/Program.cs +++ b/ErsatzTV/Program.cs @@ -54,4 +54,4 @@ namespace ErsatzTV .UseKestrel(options => options.AddServerHeader = false)) .UseSerilog(); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/MainLayout.razor b/ErsatzTV/Shared/MainLayout.razor index 221b2337a..e348ed1ab 100644 --- a/ErsatzTV/Shared/MainLayout.razor +++ b/ErsatzTV/Shared/MainLayout.razor @@ -12,7 +12,7 @@ M3U XMLTV API - + diff --git a/ErsatzTV/Startup.cs b/ErsatzTV/Startup.cs index 97a5b9cac..7ae4b9cee 100644 --- a/ErsatzTV/Startup.cs +++ b/ErsatzTV/Startup.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Threading.Channels; using ErsatzTV.Application; @@ -100,23 +99,14 @@ namespace ErsatzTV // string xmltvPath = Path.Combine(appDataFolder, "xmltv.xml"); // Log.Logger.Information("XMLTV is at {XmltvPath}", xmltvPath); - services.AddDbContextFactory( - options => - { - options.UseSqlite( - $"Data Source={FileSystemLayout.DatabasePath}", - o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); - - if (Debugger.IsAttached) - { - options.EnableSensitiveDataLogging(); - } - }); - services.AddDbContext( options => options.UseSqlite( $"Data Source={FileSystemLayout.DatabasePath}", - o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery))); + o => + { + o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); + o.MigrationsAssembly("ErsatzTV.Infrastructure"); + })); services.AddMediatR(typeof(GetAllChannels).Assembly); diff --git a/ErsatzTV/_Imports.razor b/ErsatzTV/_Imports.razor index a94e44b42..17af02e6d 100644 --- a/ErsatzTV/_Imports.razor +++ b/ErsatzTV/_Imports.razor @@ -20,7 +20,5 @@ @using ErsatzTV.Core @using ErsatzTV.Core.Domain @using ErsatzTV.Infrastructure.Data -@using ErsatzTV.Models -@using ErsatzTV.Models.UI @using ErsatzTV.Shared @using ErsatzTV.ViewModels \ No newline at end of file