From 40e79a3a14539cf5e223fa1b847dcd0e2d2b9f0a Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Thu, 2 Mar 2023 22:49:21 -0600 Subject: [PATCH] fix plex scanner crash (#1192) --- .../Repositories/PlexTelevisionRepository.cs | 75 +++++++++++-------- .../Plex/PlexServerApiClient.cs | 73 ++++++++++-------- 2 files changed, 85 insertions(+), 63 deletions(-) diff --git a/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs index 591172cfc..2eea7bd22 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs @@ -254,7 +254,11 @@ public class PlexTelevisionRepository : IPlexTelevisionRepository // deepScan isn't needed here since we create our own plex etags if (plexEpisode.Etag != item.Etag) { - await UpdateEpisodePath(dbContext, plexEpisode, item); + foreach (BaseError error in await UpdateEpisodePath(dbContext, plexEpisode, item)) + { + return error; + } + result.IsUpdated = true; } @@ -465,37 +469,48 @@ public class PlexTelevisionRepository : IPlexTelevisionRepository } } - private async Task UpdateEpisodePath(TvContext dbContext, PlexEpisode existing, PlexEpisode incoming) + private async Task> UpdateEpisodePath( + TvContext dbContext, + PlexEpisode existing, + PlexEpisode incoming) { - // library path is used for search indexing later - incoming.LibraryPath = existing.LibraryPath; - incoming.Id = existing.Id; + try + { + // library path is used for search indexing later + incoming.LibraryPath = existing.LibraryPath; + incoming.Id = existing.Id; - // version - MediaVersion version = existing.MediaVersions.Head(); - MediaVersion incomingVersion = incoming.MediaVersions.Head(); - version.Name = incomingVersion.Name; - version.DateAdded = incomingVersion.DateAdded; + // version + MediaVersion version = existing.MediaVersions.Head(); + MediaVersion incomingVersion = incoming.MediaVersions.Head(); + version.Name = incomingVersion.Name; + version.DateAdded = incomingVersion.DateAdded; - await dbContext.Connection.ExecuteAsync( - @"UPDATE MediaVersion SET Name = @Name, DateAdded = @DateAdded WHERE Id = @Id", - new { version.Name, version.DateAdded, version.Id }); - - // media file - MediaFile file = version.MediaFiles.Head(); - MediaFile incomingFile = incomingVersion.MediaFiles.Head(); - - _logger.LogDebug( - "Updating plex episode (key {Key}) path from {Existing} to {Incoming}", - existing.Key, - file.Path, - incomingFile.Path); - - file.Path = incomingFile.Path; - - - await dbContext.Connection.ExecuteAsync( - @"UPDATE MediaFile SET Path = @Path WHERE Id = @Id", - new { file.Path, file.Id }); + await dbContext.Connection.ExecuteAsync( + @"UPDATE MediaVersion SET Name = @Name, DateAdded = @DateAdded WHERE Id = @Id", + new { version.Name, version.DateAdded, version.Id }); + + // media file + MediaFile file = version.MediaFiles.Head(); + MediaFile incomingFile = incomingVersion.MediaFiles.Head(); + + _logger.LogDebug( + "Updating plex episode (key {Key}) path from {Existing} to {Incoming}", + existing.Key, + file.Path, + incomingFile.Path); + + file.Path = incomingFile.Path; + + await dbContext.Connection.ExecuteAsync( + @"UPDATE MediaFile SET Path = @Path WHERE Id = @Id", + new { file.Path, file.Id }); + + return Option.None; + } + catch (Exception) + { + return BaseError.New("Failed to update episode path"); + } } } diff --git a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs index 9a7761fad..cf0facf68 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs @@ -180,7 +180,7 @@ public class PlexServerApiClient : IPlexServerApiClient { return xmlService.GetSeasonChildren(seasonMetadataKey, skip, pageSize, token.AuthToken) .Map(r => r.Metadata.Filter(m => m.Media.Count > 0 && m.Media[0].Part.Count > 0)) - .Map(list => list.Map(metadata => ProjectToEpisode(metadata, library.MediaSourceId))); + .Map(list => list.Bind(metadata => ProjectToEpisodes(metadata, library.MediaSourceId))); } return GetPagedLibraryContents(connection, CountItems, GetItems); @@ -814,45 +814,52 @@ public class PlexServerApiClient : IPlexServerApiClient return season; } - private PlexEpisode ProjectToEpisode(PlexXmlMetadataResponse response, int mediaSourceId) + private IEnumerable ProjectToEpisodes(PlexXmlMetadataResponse response, int mediaSourceId) { - PlexMediaResponse media = response.Media.Head(); - PlexXmlPartResponse part = media.Part.Head(); - DateTime dateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime; - DateTime lastWriteTime = DateTimeOffset.FromUnixTimeSeconds(response.UpdatedAt).DateTime; + var result = new List(); - EpisodeMetadata metadata = ProjectToEpisodeMetadata(response, mediaSourceId); - var version = new MediaVersion + // TODO: actually use all media records + foreach (PlexMediaResponse media in response.Media.HeadOrNone()) { - Name = "Main", - Duration = TimeSpan.FromMilliseconds(media.Duration), - Width = media.Width, - Height = media.Height, - DateAdded = dateAdded, - DateUpdated = lastWriteTime, - MediaFiles = new List + PlexXmlPartResponse part = media.Part.Head(); + DateTime dateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime; + DateTime lastWriteTime = DateTimeOffset.FromUnixTimeSeconds(response.UpdatedAt).DateTime; + + EpisodeMetadata metadata = ProjectToEpisodeMetadata(response, mediaSourceId); + var version = new MediaVersion { - new PlexMediaFile + Name = "Main", + Duration = TimeSpan.FromMilliseconds(media.Duration), + Width = media.Width, + Height = media.Height, + DateAdded = dateAdded, + DateUpdated = lastWriteTime, + MediaFiles = new List { - PlexId = part.Id, - Key = part.Key, - Path = part.File - } - }, - // specifically omit stream details - Streams = new List() - }; + new PlexMediaFile + { + PlexId = part.Id, + Key = part.Key, + Path = part.File + } + }, + // specifically omit stream details + Streams = new List() + }; - var episode = new PlexEpisode - { - Key = response.Key, - Etag = _plexEtag.ForEpisode(response), - EpisodeMetadata = new List { metadata }, - MediaVersions = new List { version }, - TraktListItems = new List() - }; + var episode = new PlexEpisode + { + Key = response.Key, + Etag = _plexEtag.ForEpisode(response), + EpisodeMetadata = new List { metadata }, + MediaVersions = new List { version }, + TraktListItems = new List() + }; + + result.Add(episode); + } - return episode; + return result; } private EpisodeMetadata ProjectToEpisodeMetadata(PlexMetadataResponse response, int mediaSourceId)