private async Task <Either <BaseError, MediaItemScanResult <Song> > > UpdateMetadata( MediaItemScanResult <Song> result, string ffprobePath) { try { Song song = result.Item; string path = song.GetHeadVersion().MediaFiles.Head().Path; bool shouldUpdate = Optional(song.SongMetadata).Flatten().HeadOrNone().Match( m => m.MetadataKind == MetadataKind.Fallback || m.DateUpdated != _localFileSystem.GetLastWriteTime(path), true); if (shouldUpdate) { song.SongMetadata ??= new List <SongMetadata>(); _logger.LogDebug("Refreshing {Attribute} for {Path}", "Metadata", path); if (await _localMetadataProvider.RefreshTagMetadata(song, ffprobePath)) { result.IsUpdated = true; } } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <PlexMovie> > > UpdateStatistics( MediaItemScanResult <PlexMovie> result, PlexMovie incoming, PlexConnection connection, PlexServerAuthToken token) { PlexMovie existing = result.Item; MediaVersion existingVersion = existing.MediaVersions.Head(); MediaVersion incomingVersion = incoming.MediaVersions.Head(); if (incomingVersion.DateUpdated > existingVersion.DateUpdated || string.IsNullOrWhiteSpace(existingVersion.SampleAspectRatio)) { Either <BaseError, MediaVersion> maybeStatistics = await _plexServerApiClient.GetStatistics(incoming.Key.Split("/").Last(), connection, token); await maybeStatistics.Match( async mediaVersion => { existingVersion.SampleAspectRatio = mediaVersion.SampleAspectRatio ?? "1:1"; existingVersion.VideoScanKind = mediaVersion.VideoScanKind; existingVersion.DateUpdated = incomingVersion.DateUpdated; await _metadataRepository.UpdatePlexStatistics(existingVersion); }, _ => Task.CompletedTask); } return(result); }
protected override Task <Option <MovieMetadata> > GetFullMetadata( EmbyConnectionParameters connectionParameters, EmbyLibrary library, MediaItemScanResult <EmbyMovie> result, EmbyMovie incoming, bool deepScan) => Task.FromResult <Option <MovieMetadata> >(None);
private async Task <Either <BaseError, MediaItemScanResult <PlexEpisode> > > UpdateMetadata( MediaItemScanResult <PlexEpisode> result, PlexEpisode incoming) { PlexEpisode existing = result.Item; var toUpdate = existing.EpisodeMetadata .Where(em => incoming.EpisodeMetadata.Any(em2 => em2.EpisodeNumber == em.EpisodeNumber)) .ToList(); var toRemove = existing.EpisodeMetadata.Except(toUpdate).ToList(); var toAdd = incoming.EpisodeMetadata .Where(em => existing.EpisodeMetadata.All(em2 => em2.EpisodeNumber != em.EpisodeNumber)) .ToList(); foreach (EpisodeMetadata metadata in toRemove) { await _televisionRepository.RemoveMetadata(existing, metadata); } foreach (EpisodeMetadata metadata in toAdd) { metadata.EpisodeId = existing.Id; metadata.Episode = existing; existing.EpisodeMetadata.Add(metadata); await _metadataRepository.Add(metadata); } // TODO: update existing metadata return(result); }
protected override async Task <Option <MovieMetadata> > GetFullMetadata( PlexConnectionParameters connectionParameters, PlexLibrary library, MediaItemScanResult <PlexMovie> result, PlexMovie incoming, bool deepScan) { if (result.IsAdded || result.Item.Etag != incoming.Etag || deepScan) { Either <BaseError, MovieMetadata> maybeMetadata = await _plexServerApiClient.GetMovieMetadata( library, incoming.Key.Split("/").Last(), connectionParameters.Connection, connectionParameters.Token); foreach (BaseError error in maybeMetadata.LeftToSeq()) { _logger.LogWarning("Failed to get movie metadata from Plex: {Error}", error.ToString()); } return(maybeMetadata.ToOption()); } return(None); }
private async Task <Either <BaseError, MediaItemScanResult <OtherVideo> > > UpdateMetadata( MediaItemScanResult <OtherVideo> result) { try { OtherVideo otherVideo = result.Item; if (!Optional(otherVideo.OtherVideoMetadata).Flatten().Any()) { otherVideo.OtherVideoMetadata ??= new List <OtherVideoMetadata>(); string path = otherVideo.MediaVersions.Head().MediaFiles.Head().Path; _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path); if (await _localMetadataProvider.RefreshFallbackMetadata(otherVideo)) { result.IsUpdated = true; } } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <MusicVideo> > > UpdateSubtitles( MediaItemScanResult <MusicVideo> result) { try { await _localSubtitlesProvider.UpdateSubtitles(result.Item, None, true); return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <MusicVideo> > > UpdateMetadata( MediaItemScanResult <MusicVideo> result) { try { MusicVideo musicVideo = result.Item; Option <string> maybeNfoFile = LocateNfoFile(musicVideo); if (maybeNfoFile.IsNone) { if (!Optional(musicVideo.MusicVideoMetadata).Flatten().Any()) { musicVideo.MusicVideoMetadata ??= new List <MusicVideoMetadata>(); string path = musicVideo.MediaVersions.Head().MediaFiles.Head().Path; _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path); if (await _localMetadataProvider.RefreshFallbackMetadata(musicVideo)) { result.IsUpdated = true; } } } foreach (string nfoFile in maybeNfoFile) { bool shouldUpdate = Optional(musicVideo.MusicVideoMetadata).Flatten().HeadOrNone().Match( m => m.MetadataKind == MetadataKind.Fallback || m.DateUpdated != _localFileSystem.GetLastWriteTime(nfoFile), true); if (shouldUpdate) { _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile); if (await _localMetadataProvider.RefreshSidecarMetadata(musicVideo, nfoFile)) { result.IsUpdated = true; } } } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <Song> > > UpdateThumbnail( MediaItemScanResult <Song> result, string ffmpegPath, CancellationToken cancellationToken) { try { // reload the song from the database at this point if (result.IsAdded) { LibraryPath libraryPath = result.Item.LibraryPath; string path = result.Item.GetHeadVersion().MediaFiles.Head().Path; foreach (MediaItemScanResult <Song> s in (await _songRepository.GetOrAdd(libraryPath, path)) .RightToSeq()) { result.Item = s.Item; } } Song song = result.Item; Option <string> maybeThumbnail = LocateThumbnail(song); if (maybeThumbnail.IsNone) { await ExtractEmbeddedArtwork(song, ffmpegPath, cancellationToken); } foreach (string thumbnailFile in maybeThumbnail) { SongMetadata metadata = song.SongMetadata.Head(); await RefreshArtwork( thumbnailFile, metadata, ArtworkKind.Thumbnail, ffmpegPath, None, cancellationToken); } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <Show> > > UpdateMetadataForShow( MediaItemScanResult <Show> result, string showFolder) { try { Show show = result.Item; Option <string> maybeNfo = LocateNfoFileForShow(showFolder); if (maybeNfo.IsNone) { if (!Optional(show.ShowMetadata).Flatten().Any()) { _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", showFolder); if (await _localMetadataProvider.RefreshFallbackMetadata(show, showFolder)) { result.IsUpdated = true; } } } foreach (string nfoFile in maybeNfo) { bool shouldUpdate = Optional(show.ShowMetadata).Flatten().HeadOrNone().Match( m => m.MetadataKind == MetadataKind.Fallback || m.DateUpdated != _localFileSystem.GetLastWriteTime(nfoFile), true); if (shouldUpdate) { _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile); if (await _localMetadataProvider.RefreshSidecarMetadata(show, nfoFile)) { result.IsUpdated = true; } } } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <PlexShow> > > UpdateArtwork( MediaItemScanResult <PlexShow> result, PlexShow incoming) { PlexShow existing = result.Item; ShowMetadata existingMetadata = existing.ShowMetadata.Head(); ShowMetadata incomingMetadata = incoming.ShowMetadata.Head(); if (incomingMetadata.DateUpdated > existingMetadata.DateUpdated) { await UpdateArtworkIfNeeded(existingMetadata, incomingMetadata, ArtworkKind.Poster); await UpdateArtworkIfNeeded(existingMetadata, incomingMetadata, ArtworkKind.FanArt); await _metadataRepository.MarkAsUpdated(existingMetadata, incomingMetadata.DateUpdated); } return(result); }
private async Task <Either <BaseError, MediaItemScanResult <Artist> > > UpdateMetadataForArtist( MediaItemScanResult <Artist> result, string artistFolder) { try { Artist artist = result.Item; await LocateNfoFileForArtist(artistFolder).Match( async nfoFile => { bool shouldUpdate = Optional(artist.ArtistMetadata).Flatten().HeadOrNone().Match( m => m.MetadataKind == MetadataKind.Fallback || m.DateUpdated != _localFileSystem.GetLastWriteTime(nfoFile), true); if (shouldUpdate) { _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile); if (await _localMetadataProvider.RefreshSidecarMetadata(artist, nfoFile)) { result.IsUpdated = true; } } }, async() => { if (!Optional(artist.ArtistMetadata).Flatten().Any()) { _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", artistFolder); if (await _localMetadataProvider.RefreshFallbackMetadata(artist, artistFolder)) { result.IsUpdated = true; } } }); return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <PlexEpisode> > > UpdateArtwork( MediaItemScanResult <PlexEpisode> result, PlexEpisode incoming) { PlexEpisode existing = result.Item; foreach (EpisodeMetadata incomingMetadata in incoming.EpisodeMetadata) { Option <EpisodeMetadata> maybeExistingMetadata = existing.EpisodeMetadata .Find(em => em.EpisodeNumber == incomingMetadata.EpisodeNumber); if (maybeExistingMetadata.IsSome) { EpisodeMetadata existingMetadata = maybeExistingMetadata.ValueUnsafe(); await UpdateArtworkIfNeeded(existingMetadata, incomingMetadata, ArtworkKind.Thumbnail); await _metadataRepository.MarkAsUpdated(existingMetadata, incomingMetadata.DateUpdated); } } return(result); }
private async Task <Either <BaseError, MediaItemScanResult <TMovie> > > UpdateMetadata( TConnectionParameters connectionParameters, TLibrary library, MediaItemScanResult <TMovie> result, TMovie incoming, bool deepScan) { foreach (MovieMetadata fullMetadata in await GetFullMetadata( connectionParameters, library, result, incoming, deepScan)) { // TODO: move some of this code into this scanner // will have to merge JF, Emby, Plex logic return(await UpdateMetadata(result, fullMetadata)); } return(result); }
private async Task <Either <BaseError, MediaItemScanResult <TMovie> > > UpdateStatistics( MediaItemScanResult <TMovie> result, TMovie incoming, string ffmpegPath, string ffprobePath) { TMovie existing = result.Item; if (result.IsAdded || MediaServerItemId(existing) != MediaServerItemId(incoming) || existing.MediaVersions.Head().Streams.Count == 0) { if (_localFileSystem.FileExists(result.LocalPath)) { _logger.LogDebug("Refreshing {Attribute} for {Path}", "Statistics", result.LocalPath); Either <BaseError, bool> refreshResult = await _localStatisticsProvider.RefreshStatistics( ffmpegPath, ffprobePath, existing, result.LocalPath); foreach (BaseError error in refreshResult.LeftToSeq()) { _logger.LogWarning( "Unable to refresh {Attribute} for media item {Path}. Error: {Error}", "Statistics", result.LocalPath, error.Value); } foreach (bool _ in refreshResult.RightToSeq()) { result.IsUpdated = true; } } } return(result); }
private async Task <Either <BaseError, MediaItemScanResult <MusicVideo> > > UpdateThumbnail( MediaItemScanResult <MusicVideo> result, CancellationToken cancellationToken) { try { MusicVideo musicVideo = result.Item; Option <string> maybeThumbnail = LocateThumbnail(musicVideo); foreach (string thumbnailFile in maybeThumbnail) { MusicVideoMetadata metadata = musicVideo.MusicVideoMetadata.Head(); await RefreshArtwork(thumbnailFile, metadata, ArtworkKind.Thumbnail, None, None, cancellationToken); } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <Movie> > > UpdateArtwork( MediaItemScanResult <Movie> result, ArtworkKind artworkKind, CancellationToken cancellationToken) { try { Movie movie = result.Item; Option <string> maybeArtwork = LocateArtwork(movie, artworkKind); foreach (string posterFile in maybeArtwork) { MovieMetadata metadata = movie.MovieMetadata.Head(); await RefreshArtwork(posterFile, metadata, artworkKind, None, None, cancellationToken); } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <PlexEpisode> > > UpdateSubtitles( List <PlexPathReplacement> pathReplacements, MediaItemScanResult <PlexEpisode> result, PlexEpisode incoming) { try { PlexEpisode existing = result.Item; string localPath = _plexPathReplacementService.GetReplacementPlexPath( pathReplacements, incoming.MediaVersions.Head().MediaFiles.Head().Path, false); await _localSubtitlesProvider.UpdateSubtitles(existing, localPath, false); return(result); } catch (Exception ex) { return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <TMovie> > > UpdateSubtitles( MediaItemScanResult <TMovie> existing) { try { // skip checking subtitles for files that don't exist locally if (!_localFileSystem.FileExists(existing.LocalPath)) { return(existing); } if (await _localSubtitlesProvider.UpdateSubtitles(existing.Item, existing.LocalPath, false)) { return(existing); } return(BaseError.New("Failed to update local subtitles")); } catch (Exception ex) { return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <Artist> > > UpdateArtworkForArtist( MediaItemScanResult <Artist> result, string artistFolder, ArtworkKind artworkKind, CancellationToken cancellationToken) { try { Artist artist = result.Item; await LocateArtworkForArtist(artistFolder, artworkKind).IfSomeAsync( async artworkFile => { ArtistMetadata metadata = artist.ArtistMetadata.Head(); await RefreshArtwork(artworkFile, metadata, artworkKind, None, None, cancellationToken); }); return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
private async Task <Either <BaseError, MediaItemScanResult <Show> > > UpdateArtworkForShow( MediaItemScanResult <Show> result, string showFolder, ArtworkKind artworkKind, CancellationToken cancellationToken) { try { Show show = result.Item; Option <string> maybeArtwork = LocateArtworkForShow(showFolder, artworkKind); foreach (string artworkFile in maybeArtwork) { ShowMetadata metadata = show.ShowMetadata.Head(); await RefreshArtwork(artworkFile, metadata, artworkKind, None, None, cancellationToken); } return(result); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.ToString())); } }
protected override Task <Either <BaseError, MediaItemScanResult <EmbyMovie> > > UpdateMetadata( MediaItemScanResult <EmbyMovie> result, MovieMetadata fullMetadata) => Task.FromResult <Either <BaseError, MediaItemScanResult <EmbyMovie> > >(result);
private async Task <Either <BaseError, MediaItemScanResult <PlexShow> > > UpdateMetadata( MediaItemScanResult <PlexShow> result, PlexShow incoming) { PlexShow existing = result.Item; ShowMetadata existingMetadata = existing.ShowMetadata.Head(); ShowMetadata incomingMetadata = incoming.ShowMetadata.Head(); // TODO: this probably doesn't work // plex doesn't seem to update genres returned by the main library call if (incomingMetadata.DateUpdated > existingMetadata.DateUpdated) { foreach (Genre genre in existingMetadata.Genres .Filter(g => incomingMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Remove(genre); if (await _metadataRepository.RemoveGenre(genre)) { result.IsUpdated = true; } } foreach (Genre genre in incomingMetadata.Genres .Filter(g => existingMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Add(genre); if (await _televisionRepository.AddGenre(existingMetadata, genre)) { result.IsUpdated = true; } } foreach (Studio studio in existingMetadata.Studios .Filter(s => incomingMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Remove(studio); if (await _metadataRepository.RemoveStudio(studio)) { result.IsUpdated = true; } } foreach (Studio studio in incomingMetadata.Studios .Filter(s => existingMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Add(studio); if (await _televisionRepository.AddStudio(existingMetadata, studio)) { result.IsUpdated = true; } } await _metadataRepository.MarkAsUpdated(existingMetadata, incomingMetadata.DateUpdated); } return(result); }
private async Task <Either <BaseError, MediaItemScanResult <PlexShow> > > UpdateMetadata( MediaItemScanResult <PlexShow> result, PlexShow incoming, PlexLibrary library, PlexConnection connection, PlexServerAuthToken token, bool deepScan) { PlexShow existing = result.Item; ShowMetadata existingMetadata = existing.ShowMetadata.Head(); if (result.IsAdded || existing.Etag != incoming.Etag || deepScan) { Either <BaseError, ShowMetadata> maybeMetadata = await _plexServerApiClient.GetShowMetadata( library, incoming.Key.Replace("/children", string.Empty).Split("/").Last(), connection, token); await maybeMetadata.Match( async fullMetadata => { if (existingMetadata.MetadataKind != MetadataKind.External) { existingMetadata.MetadataKind = MetadataKind.External; await _metadataRepository.MarkAsExternal(existingMetadata); } if (existingMetadata.ContentRating != fullMetadata.ContentRating) { existingMetadata.ContentRating = fullMetadata.ContentRating; await _metadataRepository.SetContentRating(existingMetadata, fullMetadata.ContentRating); result.IsUpdated = true; } foreach (Genre genre in existingMetadata.Genres .Filter(g => fullMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Remove(genre); if (await _metadataRepository.RemoveGenre(genre)) { result.IsUpdated = true; } } foreach (Genre genre in fullMetadata.Genres .Filter(g => existingMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Add(genre); if (await _televisionRepository.AddGenre(existingMetadata, genre)) { result.IsUpdated = true; } } foreach (Studio studio in existingMetadata.Studios .Filter(s => fullMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Remove(studio); if (await _metadataRepository.RemoveStudio(studio)) { result.IsUpdated = true; } } foreach (Studio studio in fullMetadata.Studios .Filter(s => existingMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Add(studio); if (await _televisionRepository.AddStudio(existingMetadata, studio)) { result.IsUpdated = true; } } foreach (Actor actor in existingMetadata.Actors .Filter( a => fullMetadata.Actors.All( a2 => a2.Name != a.Name || a.Artwork == null && a2.Artwork != null)) .ToList()) { existingMetadata.Actors.Remove(actor); if (await _metadataRepository.RemoveActor(actor)) { result.IsUpdated = true; } } foreach (Actor actor in fullMetadata.Actors .Filter(a => existingMetadata.Actors.All(a2 => a2.Name != a.Name)) .ToList()) { existingMetadata.Actors.Add(actor); if (await _televisionRepository.AddActor(existingMetadata, actor)) { result.IsUpdated = true; } } foreach (MetadataGuid guid in existingMetadata.Guids .Filter(g => fullMetadata.Guids.All(g2 => g2.Guid != g.Guid)) .ToList()) { existingMetadata.Guids.Remove(guid); if (await _metadataRepository.RemoveGuid(guid)) { result.IsUpdated = true; } } foreach (MetadataGuid guid in fullMetadata.Guids .Filter(g => existingMetadata.Guids.All(g2 => g2.Guid != g.Guid)) .ToList()) { existingMetadata.Guids.Add(guid); if (await _metadataRepository.AddGuid(existingMetadata, guid)) { result.IsUpdated = true; } } foreach (Tag tag in existingMetadata.Tags .Filter(g => fullMetadata.Tags.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Tags.Remove(tag); if (await _metadataRepository.RemoveTag(tag)) { result.IsUpdated = true; } } foreach (Tag tag in fullMetadata.Tags .Filter(g => existingMetadata.Tags.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Tags.Add(tag); if (await _televisionRepository.AddTag(existingMetadata, tag)) { result.IsUpdated = true; } } if (result.IsUpdated) { await _metadataRepository.MarkAsUpdated(existingMetadata, fullMetadata.DateUpdated); } }, _ => Task.CompletedTask); } return(result); }
private async Task <Either <BaseError, MediaItemScanResult <PlexEpisode> > > UpdateStatistics( List <PlexPathReplacement> pathReplacements, MediaItemScanResult <PlexEpisode> result, PlexEpisode incoming, PlexLibrary library, PlexConnection connection, PlexServerAuthToken token, string ffmpegPath, string ffprobePath, bool deepScan) { PlexEpisode existing = result.Item; MediaVersion existingVersion = existing.MediaVersions.Head(); MediaVersion incomingVersion = incoming.MediaVersions.Head(); if (result.IsAdded || existing.Etag != incoming.Etag || deepScan || existingVersion.Streams.Count == 0) { foreach (MediaFile incomingFile in incomingVersion.MediaFiles.HeadOrNone()) { foreach (MediaFile existingFile in existingVersion.MediaFiles.HeadOrNone()) { if (incomingFile.Path != existingFile.Path) { _logger.LogDebug( "Plex episode has moved from {OldPath} to {NewPath}", existingFile.Path, incomingFile.Path); existingFile.Path = incomingFile.Path; await _televisionRepository.UpdatePath(existingFile.Id, incomingFile.Path); } } } Either <BaseError, bool> refreshResult = true; string localPath = _plexPathReplacementService.GetReplacementPlexPath( pathReplacements, incoming.MediaVersions.Head().MediaFiles.Head().Path, false); if ((existing.Etag != incoming.Etag || existingVersion.Streams.Count == 0) && _localFileSystem.FileExists(localPath)) { _logger.LogDebug("Refreshing {Attribute} for {Path}", "Statistics", localPath); refreshResult = await _localStatisticsProvider.RefreshStatistics( ffmpegPath, ffprobePath, existing, localPath); } foreach (BaseError error in refreshResult.LeftToSeq()) { _logger.LogWarning( "Unable to refresh {Attribute} for media item {Path}. Error: {Error}", "Statistics", localPath, error.Value); } foreach (var _ in refreshResult.RightToSeq()) { foreach (MediaItem updated in await _searchRepository.GetItemToIndex(incoming.Id)) { await _searchIndex.UpdateItems( _searchRepository, new List <MediaItem> { updated }); } Either <BaseError, Tuple <EpisodeMetadata, MediaVersion> > maybeStatistics = await _plexServerApiClient.GetEpisodeMetadataAndStatistics( library, incoming.Key.Split("/").Last(), connection, token); foreach (Tuple <EpisodeMetadata, MediaVersion> tuple in maybeStatistics.RightToSeq()) { (EpisodeMetadata incomingMetadata, MediaVersion mediaVersion) = tuple; Option <EpisodeMetadata> maybeExisting = existing.EpisodeMetadata .Find(em => em.EpisodeNumber == incomingMetadata.EpisodeNumber); foreach (EpisodeMetadata existingMetadata in maybeExisting) { foreach (MetadataGuid guid in existingMetadata.Guids .Filter(g => incomingMetadata.Guids.All(g2 => g2.Guid != g.Guid)) .ToList()) { existingMetadata.Guids.Remove(guid); await _metadataRepository.RemoveGuid(guid); } foreach (MetadataGuid guid in incomingMetadata.Guids .Filter(g => existingMetadata.Guids.All(g2 => g2.Guid != g.Guid)) .ToList()) { existingMetadata.Guids.Add(guid); await _metadataRepository.AddGuid(existingMetadata, guid); } foreach (Tag tag in existingMetadata.Tags .Filter(g => incomingMetadata.Tags.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Tags.Remove(tag); await _metadataRepository.RemoveTag(tag); } foreach (Tag tag in incomingMetadata.Tags .Filter(g => existingMetadata.Tags.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Tags.Add(tag); await _televisionRepository.AddTag(existingMetadata, tag); } } existingVersion.SampleAspectRatio = mediaVersion.SampleAspectRatio; existingVersion.VideoScanKind = mediaVersion.VideoScanKind; existingVersion.DateUpdated = mediaVersion.DateUpdated; await _metadataRepository.UpdatePlexStatistics(existingVersion.Id, mediaVersion); } } } return(result); }
protected abstract Task <Either <BaseError, MediaItemScanResult <TMovie> > > UpdateMetadata( MediaItemScanResult <TMovie> result, MovieMetadata fullMetadata);
protected abstract Task <Option <MovieMetadata> > GetFullMetadata( TConnectionParameters connectionParameters, TLibrary library, MediaItemScanResult <TMovie> result, TMovie incoming, bool deepScan);
private async Task <Either <BaseError, MediaItemScanResult <PlexMovie> > > UpdateMetadata( MediaItemScanResult <PlexMovie> result, PlexMovie incoming) { PlexMovie existing = result.Item; MovieMetadata existingMetadata = existing.MovieMetadata.Head(); MovieMetadata incomingMetadata = incoming.MovieMetadata.Head(); if (incomingMetadata.DateUpdated > existingMetadata.DateUpdated) { foreach (Genre genre in existingMetadata.Genres .Filter(g => incomingMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Remove(genre); if (await _metadataRepository.RemoveGenre(genre)) { result.IsUpdated = true; } } foreach (Genre genre in incomingMetadata.Genres .Filter(g => existingMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Add(genre); if (await _movieRepository.AddGenre(existingMetadata, genre)) { result.IsUpdated = true; } } foreach (Studio studio in existingMetadata.Studios .Filter(s => incomingMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Remove(studio); if (await _metadataRepository.RemoveStudio(studio)) { result.IsUpdated = true; } } foreach (Studio studio in incomingMetadata.Studios .Filter(s => existingMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Add(studio); if (await _movieRepository.AddStudio(existingMetadata, studio)) { result.IsUpdated = true; } } if (incomingMetadata.SortTitle != existingMetadata.SortTitle) { existingMetadata.SortTitle = incomingMetadata.SortTitle; if (await _movieRepository.UpdateSortTitle(existingMetadata)) { result.IsUpdated = true; } } await _metadataRepository.MarkAsUpdated(existingMetadata, incomingMetadata.DateUpdated); // TODO: update other metadata? } return(result); }
protected override async Task <Either <BaseError, MediaItemScanResult <PlexMovie> > > UpdateMetadata( MediaItemScanResult <PlexMovie> result, MovieMetadata fullMetadata) { PlexMovie existing = result.Item; MovieMetadata existingMetadata = existing.MovieMetadata.Head(); _logger.LogDebug( "Refreshing {Attribute} for {Title}", "Plex Metadata", existingMetadata.Title); if (existingMetadata.MetadataKind != MetadataKind.External) { existingMetadata.MetadataKind = MetadataKind.External; await _metadataRepository.MarkAsExternal(existingMetadata); } if (existingMetadata.ContentRating != fullMetadata.ContentRating) { existingMetadata.ContentRating = fullMetadata.ContentRating; await _metadataRepository.SetContentRating(existingMetadata, fullMetadata.ContentRating); result.IsUpdated = true; } foreach (Genre genre in existingMetadata.Genres .Filter(g => fullMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Remove(genre); if (await _metadataRepository.RemoveGenre(genre)) { result.IsUpdated = true; } } foreach (Genre genre in fullMetadata.Genres .Filter(g => existingMetadata.Genres.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Genres.Add(genre); if (await _movieRepository.AddGenre(existingMetadata, genre)) { result.IsUpdated = true; } } foreach (Studio studio in existingMetadata.Studios .Filter(s => fullMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Remove(studio); if (await _metadataRepository.RemoveStudio(studio)) { result.IsUpdated = true; } } foreach (Studio studio in fullMetadata.Studios .Filter(s => existingMetadata.Studios.All(s2 => s2.Name != s.Name)) .ToList()) { existingMetadata.Studios.Add(studio); if (await _movieRepository.AddStudio(existingMetadata, studio)) { result.IsUpdated = true; } } foreach (Actor actor in existingMetadata.Actors .Filter( a => fullMetadata.Actors.All( a2 => a2.Name != a.Name || a.Artwork == null && a2.Artwork != null)) .ToList()) { existingMetadata.Actors.Remove(actor); if (await _metadataRepository.RemoveActor(actor)) { result.IsUpdated = true; } } foreach (Actor actor in fullMetadata.Actors .Filter(a => existingMetadata.Actors.All(a2 => a2.Name != a.Name)) .ToList()) { existingMetadata.Actors.Add(actor); if (await _movieRepository.AddActor(existingMetadata, actor)) { result.IsUpdated = true; } } foreach (Director director in existingMetadata.Directors .Filter(g => fullMetadata.Directors.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Directors.Remove(director); if (await _metadataRepository.RemoveDirector(director)) { result.IsUpdated = true; } } foreach (Director director in fullMetadata.Directors .Filter(g => existingMetadata.Directors.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Directors.Add(director); if (await _movieRepository.AddDirector(existingMetadata, director)) { result.IsUpdated = true; } } foreach (Writer writer in existingMetadata.Writers .Filter(g => fullMetadata.Writers.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Writers.Remove(writer); if (await _metadataRepository.RemoveWriter(writer)) { result.IsUpdated = true; } } foreach (Writer writer in fullMetadata.Writers .Filter(g => existingMetadata.Writers.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Writers.Add(writer); if (await _movieRepository.AddWriter(existingMetadata, writer)) { result.IsUpdated = true; } } foreach (MetadataGuid guid in existingMetadata.Guids .Filter(g => fullMetadata.Guids.All(g2 => g2.Guid != g.Guid)) .ToList()) { existingMetadata.Guids.Remove(guid); if (await _metadataRepository.RemoveGuid(guid)) { result.IsUpdated = true; } } foreach (MetadataGuid guid in fullMetadata.Guids .Filter(g => existingMetadata.Guids.All(g2 => g2.Guid != g.Guid)) .ToList()) { existingMetadata.Guids.Add(guid); if (await _metadataRepository.AddGuid(existingMetadata, guid)) { result.IsUpdated = true; } } foreach (Tag tag in existingMetadata.Tags .Filter(g => fullMetadata.Tags.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Tags.Remove(tag); if (await _metadataRepository.RemoveTag(tag)) { result.IsUpdated = true; } } foreach (Tag tag in fullMetadata.Tags .Filter(g => existingMetadata.Tags.All(g2 => g2.Name != g.Name)) .ToList()) { existingMetadata.Tags.Add(tag); if (await _movieRepository.AddTag(existingMetadata, tag)) { result.IsUpdated = true; } } if (fullMetadata.SortTitle != existingMetadata.SortTitle) { existingMetadata.SortTitle = fullMetadata.SortTitle; if (await _movieRepository.UpdateSortTitle(existingMetadata)) { result.IsUpdated = true; } } bool poster = await UpdateArtworkIfNeeded(existingMetadata, fullMetadata, ArtworkKind.Poster); bool fanArt = await UpdateArtworkIfNeeded(existingMetadata, fullMetadata, ArtworkKind.FanArt); if (poster || fanArt) { result.IsUpdated = true; } if (result.IsUpdated) { await _metadataRepository.MarkAsUpdated(existingMetadata, fullMetadata.DateUpdated); } return(result); }