From baf81f31cd11ac0057533207924f67e5637b1bed Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Thu, 8 Apr 2021 18:48:12 -0500 Subject: [PATCH] fix plex server and connection sync (#153) --- .../SynchronizePlexMediaSourcesHandler.cs | 16 +++++- .../Repositories/IMediaSourceRepository.cs | 8 ++- .../Repositories/MediaSourceRepository.cs | 49 ++++++++++++++----- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs index a0541ad3e..fa6269dfd 100644 --- a/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexMediaSourcesHandler.cs @@ -10,6 +10,7 @@ using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; +using Microsoft.Extensions.Logging; namespace ErsatzTV.Application.Plex.Commands { @@ -19,6 +20,7 @@ namespace ErsatzTV.Application.Plex.Commands { private readonly ChannelWriter _channel; private readonly IEntityLocker _entityLocker; + private readonly ILogger _logger; private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IPlexTvApiClient _plexTvApiClient; @@ -26,12 +28,14 @@ namespace ErsatzTV.Application.Plex.Commands IMediaSourceRepository mediaSourceRepository, IPlexTvApiClient plexTvApiClient, ChannelWriter channel, - IEntityLocker entityLocker) + IEntityLocker entityLocker, + ILogger logger) { _mediaSourceRepository = mediaSourceRepository; _plexTvApiClient = plexTvApiClient; _channel = channel; _entityLocker = entityLocker; + _logger = logger; } public Task>> Handle( @@ -47,6 +51,14 @@ namespace ErsatzTV.Application.Plex.Commands await SynchronizeServer(allExisting, server); } + // delete removed servers + foreach (PlexMediaSource removed in allExisting.Filter( + s => servers.All(pms => pms.ClientIdentifier != s.ClientIdentifier))) + { + _logger.LogWarning("Deleting removed Plex server {ServerName}!", removed.Id.ToString()); + await _mediaSourceRepository.DeletePlex(removed); + } + foreach (PlexMediaSource mediaSource in await _mediaSourceRepository.GetAllPlex()) { await _channel.WriteAsync(new SynchronizePlexLibraries(mediaSource.Id)); @@ -72,7 +84,7 @@ namespace ErsatzTV.Application.Plex.Commands .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); + return _mediaSourceRepository.Update(existing, server.Connections, toAdd, toRemove); }, async () => { diff --git a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs index 34de2470a..80318975e 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IMediaSourceRepository.cs @@ -20,7 +20,12 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task> GetPlexPathReplacementsByLibraryId(int plexLibraryPathId); Task CountMediaItems(int id); Task Update(LocalMediaSource localMediaSource); - Task Update(PlexMediaSource plexMediaSource, List toAdd, List toDelete); + + Task Update( + PlexMediaSource plexMediaSource, + List prioritizedConnections, + List toAdd, + List toDelete); Task UpdateLibraries( int plexMediaSourceId, @@ -36,6 +41,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories Task Update(PlexLibrary plexMediaSourceLibrary); Task Delete(int mediaSourceId); Task> DeleteAllPlex(); + Task> DeletePlex(PlexMediaSource plexMediaSource); Task> DisablePlexLibrarySync(List libraryIds); Task EnablePlexLibrarySync(IEnumerable libraryIds); } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs index 2ada42afc..03f478039 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaSourceRepository.cs @@ -155,6 +155,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories public async Task Update( PlexMediaSource plexMediaSource, + List sortedConnections, List toAdd, List toDelete) { @@ -174,27 +175,34 @@ namespace ErsatzTV.Infrastructure.Data.Repositories plexMediaSource.Id }); - await using TvContext dbContext = _dbContextFactory.CreateDbContext(); - foreach (PlexConnection add in toAdd) { - add.PlexMediaSourceId = plexMediaSource.Id; - dbContext.Entry(add).State = EntityState.Added; + await _dbConnection.ExecuteAsync( + @"INSERT INTO PlexConnection (IsActive, Uri, PlexMediaSourceId) + VALUES (0, @Uri, @PlexMediaSourceId)", + new { add.Uri, PlexMediaSourceId = plexMediaSource.Id }); } foreach (PlexConnection delete in toDelete) { - dbContext.Entry(delete).State = EntityState.Deleted; + await _dbConnection.ExecuteAsync( + @"DELETE FROM PlexConnection WHERE Id = @Id", + new { delete.Id }); } - 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)) + int activeCount = await _dbConnection.QuerySingleAsync( + @"SELECT COUNT(*) FROM PlexConnection WHERE IsActive = 1 AND PlexMediaSourceId = @PlexMediaSourceId", + new { PlexMediaSourceId = plexMediaSource.Id }); + if (activeCount == 0) { - plexMediaSource.Connections.Head().IsActive = true; - await dbContext.SaveChangesAsync(); + Option toActivate = + sortedConnections.FirstOrDefault(c => toDelete.All(d => d.Id != c.Id)); + + // update on uri because connections from Plex API don't have our local ids + await toActivate.IfSomeAsync( + async c => await _dbConnection.ExecuteAsync( + @"UPDATE PlexConnection SET IsActive = 1 WHERE Uri = @Uri", + new { c.Uri })); } } @@ -292,6 +300,23 @@ namespace ErsatzTV.Infrastructure.Data.Repositories return movieIds.Append(showIds).ToList(); } + public async Task> DeletePlex(PlexMediaSource plexMediaSource) + { + List mediaItemIds = await _dbConnection.QueryAsync( + @"SELECT MediaItem.Id FROM MediaItem + INNER JOIN LibraryPath LP on MediaItem.LibraryPathId = LP.Id + INNER JOIN Library L on LP.LibraryId = L.Id + WHERE L.MediaSourceId = @PlexMediaSourceId", + new { PlexMediaSourceId = plexMediaSource.Id }) + .Map(result => result.ToList()); + + await _dbConnection.ExecuteAsync( + @"DELETE FROM MediaSource WHERE Id = @PlexMediaSourceId", + new { PlexMediaSourceId = plexMediaSource.Id }); + + return mediaItemIds; + } + public async Task> DisablePlexLibrarySync(List libraryIds) { await _dbConnection.ExecuteAsync(