From eca58dbe7fad4add8d6eb5b62426ddd0fbc36d57 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sun, 21 Mar 2021 01:35:18 +0000 Subject: [PATCH] plex fixes (#92) * fix updating plex path replacements * fix adding/removing plex libraries * fix adding/removing plex servers * fix initial plex library sync after sign in * code cleanup --- .../SynchronizePlexLibrariesHandler.cs | 8 +- .../SynchronizePlexMediaSourcesHandler.cs | 15 ++- .../UpdatePlexPathReplacementsHandler.cs | 16 +--- .../Repositories/IMediaSourceRepository.cs | 14 ++- .../Repositories/MediaSourceRepository.cs | 91 ++++++++++++++++++- .../Plex/PlexTvApiClient.cs | 10 +- ErsatzTV/Pages/ChannelEditor.razor | 2 +- 7 files changed, 118 insertions(+), 38 deletions(-) diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs index 799d1dbab..1e86abd5a 100644 --- a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs @@ -78,10 +78,10 @@ namespace ErsatzTV.Application.Plex.Commands var existing = connectionParameters.PlexMediaSource.Libraries.OfType().ToList(); var toAdd = libraries.Filter(library => existing.All(l => l.Key != library.Key)).ToList(); var toRemove = existing.Filter(library => libraries.All(l => l.Key != library.Key)).ToList(); - connectionParameters.PlexMediaSource.Libraries.AddRange(toAdd); - toRemove.ForEach(c => connectionParameters.PlexMediaSource.Libraries.Remove(c)); - - return _mediaSourceRepository.Update(connectionParameters.PlexMediaSource); + return _mediaSourceRepository.UpdateLibraries( + connectionParameters.PlexMediaSource.Id, + toAdd, + toRemove); }, error => { diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs index 2d6b4c441..abf80f28b 100644 --- a/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs @@ -66,23 +66,20 @@ namespace ErsatzTV.Application.Plex.Commands { existing.ProductVersion = server.ProductVersion; existing.ServerName = server.ServerName; - MergeConnections(existing.Connections, server.Connections); - if (existing.Connections.Any() && existing.Connections.All(c => !c.IsActive)) - { - existing.Connections.Head().IsActive = true; - } - - return _mediaSourceRepository.Update(existing); + var toAdd = server.Connections + .Filter(connection => existing.Connections.All(c => c.Uri != connection.Uri)).ToList(); + var toRemove = existing.Connections + .Filter(connection => server.Connections.All(c => c.Uri != connection.Uri)).ToList(); + return _mediaSourceRepository.Update(existing, toAdd, toRemove); }, async () => { - await _mediaSourceRepository.Add(server); if (server.Connections.Any()) { server.Connections.Head().IsActive = true; } - await _mediaSourceRepository.Update(server); + await _mediaSourceRepository.Add(server); }); } diff --git a/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs b/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs index 35abbcbf0..67f469523 100644 --- a/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs @@ -6,7 +6,6 @@ using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; -using static LanguageExt.Prelude; namespace ErsatzTV.Application.Plex.Commands { @@ -35,20 +34,7 @@ namespace ErsatzTV.Application.Plex.Commands var toRemove = plexMediaSource.PathReplacements.Filter(r => incoming.All(pr => pr.Id != r.Id)).ToList(); var toUpdate = incoming.Except(toAdd).ToList(); - plexMediaSource.PathReplacements.AddRange(toAdd); - toRemove.ForEach(pr => plexMediaSource.PathReplacements.Remove(pr)); - foreach (PlexPathReplacement pathReplacement in toUpdate) - { - Optional(plexMediaSource.PathReplacements.SingleOrDefault(pr => pr.Id == pathReplacement.Id)) - .IfSome( - pr => - { - pr.PlexPath = pathReplacement.PlexPath; - pr.LocalPath = pathReplacement.LocalPath; - }); - } - - return _mediaSourceRepository.Update(plexMediaSource).ToUnit(); + return _mediaSourceRepository.UpdatePathReplacements(plexMediaSource.Id, toAdd, toUpdate, toRemove); } private static PlexPathReplacement Project(PlexPathReplacementItem vm) => diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs index 6c829f2c7..c92c71815 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs @@ -20,7 +20,19 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task> GetPlexPathReplacementsByLibraryId(int plexLibraryPathId); Task CountMediaItems(int id); Task Update(LocalMediaSource localMediaSource); - Task Update(PlexMediaSource plexMediaSource); + Task Update(PlexMediaSource plexMediaSource, List toAdd, List toDelete); + + Task UpdateLibraries( + int plexMediaSourceId, + List toAdd, + List toDelete); + + Task UpdatePathReplacements( + int plexMediaSourceId, + List toAdd, + List toUpdate, + List toDelete); + Task Update(PlexLibrary plexMediaSourceLibrary); Task Delete(int mediaSourceId); Task DeleteAllPlex(); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs index 5325600ef..c4fc2b731 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs @@ -152,11 +152,94 @@ namespace ErsatzTV.Infrastructure.Data.Repositories await context.SaveChangesAsync(); } - public async Task Update(PlexMediaSource plexMediaSource) + public async Task Update( + PlexMediaSource plexMediaSource, + List toAdd, + List toDelete) { - await using TvContext context = _dbContextFactory.CreateDbContext(); - context.PlexMediaSources.Update(plexMediaSource); - await context.SaveChangesAsync(); + await _dbConnection.ExecuteAsync( + @"UPDATE PlexMediaSource SET ProductVersion = @ProductVersion, ServerName = @ServerName WHERE Id = @Id", + new { plexMediaSource.ProductVersion, plexMediaSource.ServerName, plexMediaSource.Id }); + + await using TvContext dbContext = _dbContextFactory.CreateDbContext(); + + foreach (PlexConnection add in toAdd) + { + add.PlexMediaSourceId = plexMediaSource.Id; + dbContext.Entry(add).State = EntityState.Added; + } + + foreach (PlexConnection delete in toDelete) + { + dbContext.Entry(delete).State = EntityState.Deleted; + } + + await dbContext.SaveChangesAsync(); + + PlexMediaSource pms = await dbContext.PlexMediaSources.FindAsync(plexMediaSource.Id); + await dbContext.Entry(pms).Collection(x => x.Connections).LoadAsync(); + if (plexMediaSource.Connections.Any() && plexMediaSource.Connections.All(c => !c.IsActive)) + { + plexMediaSource.Connections.Head().IsActive = true; + await dbContext.SaveChangesAsync(); + } + } + + public async Task UpdateLibraries( + int plexMediaSourceId, + List toAdd, + List toDelete) + { + await using TvContext dbContext = _dbContextFactory.CreateDbContext(); + + foreach (PlexLibrary add in toAdd) + { + add.MediaSourceId = plexMediaSourceId; + dbContext.Entry(add).State = EntityState.Added; + } + + foreach (PlexLibrary delete in toDelete) + { + dbContext.Entry(delete).State = EntityState.Deleted; + } + + await dbContext.SaveChangesAsync(); + + return Unit.Default; + } + + public async Task UpdatePathReplacements( + int plexMediaSourceId, + List toAdd, + List toUpdate, + List toDelete) + { + foreach (PlexPathReplacement add in toAdd) + { + await _dbConnection.ExecuteAsync( + @"INSERT INTO PlexPathReplacement + (PlexPath, LocalPath, PlexMediaSourceId) + VALUES (@PlexPath, @LocalPath, @PlexMediaSourceId)", + new { add.PlexPath, add.LocalPath, PlexMediaSourceId = plexMediaSourceId }); + } + + foreach (PlexPathReplacement update in toUpdate) + { + await _dbConnection.ExecuteAsync( + @"UPDATE PlexPathReplacement + SET PlexPath = @PlexPath, LocalPath = @LocalPath + WHERE Id = @Id", + new { update.PlexPath, update.LocalPath, update.Id }); + } + + foreach (PlexPathReplacement delete in toDelete) + { + await _dbConnection.ExecuteAsync( + @"DELETE FROM PlexPathReplacement WHERE Id = @Id", + new { delete.Id }); + } + + return Unit.Default; } public async Task Update(PlexLibrary plexMediaSourceLibrary) diff --git a/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs index 615f2b7dd..4e995c879 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs @@ -51,17 +51,17 @@ namespace ErsatzTV.Infrastructure.Plex .Append(httpsResources.Filter(resource => resource.HttpsRequired)) .ToList(); - IEnumerable sources = allResources + IEnumerable sources = await allResources .Filter(r => r.Provides.Split(",").Any(p => p == "server")) .Filter(r => r.Owned) // TODO: maybe support non-owned servers in the future .Map( - resource => + async resource => { var serverAuthToken = new PlexServerAuthToken( resource.ClientIdentifier, resource.AccessToken); - _plexSecretStore.UpsertServerAuthToken(serverAuthToken); + await _plexSecretStore.UpsertServerAuthToken(serverAuthToken); List sortedConnections = resource.HttpsRequired ? resource.Connections : resource.Connections.OrderBy(c => c.Local ? 0 : 1).ToList(); @@ -76,7 +76,9 @@ namespace ErsatzTV.Infrastructure.Plex }; return source; - }); + }) + .Sequence(); + result.AddRange(sources); } diff --git a/ErsatzTV/Pages/ChannelEditor.razor b/ErsatzTV/Pages/ChannelEditor.razor index f50522cf9..18956e001 100644 --- a/ErsatzTV/Pages/ChannelEditor.razor +++ b/ErsatzTV/Pages/ChannelEditor.razor @@ -96,7 +96,7 @@ else { FFmpegSettingsViewModel ffmpegSettings = await Mediator.Send(new GetFFmpegSettings()); - + // TODO: command for new channel IEnumerable channelNumbers = await Mediator.Send(new GetAllChannels()) .Map(list => list.Map(c => int.TryParse(c.Number.Split(".").Head(), out int result) ? result : 0));