public async Task <Either <BaseError, MediaItemScanResult <PlexShow> > > GetOrAddPlexShow(
            PlexLibrary library,
            PlexShow item)
        {
            await using TvContext dbContext = _dbContextFactory.CreateDbContext();
            Option <PlexShow> maybeExisting = await dbContext.PlexShows
                                              .AsNoTracking()
                                              .Include(i => i.ShowMetadata)
                                              .ThenInclude(sm => sm.Genres)
                                              .Include(i => i.ShowMetadata)
                                              .ThenInclude(sm => sm.Tags)
                                              .Include(i => i.ShowMetadata)
                                              .ThenInclude(sm => sm.Studios)
                                              .Include(i => i.ShowMetadata)
                                              .ThenInclude(sm => sm.Artwork)
                                              .Include(i => i.LibraryPath)
                                              .ThenInclude(lp => lp.Library)
                                              .OrderBy(i => i.Key)
                                              .SingleOrDefaultAsync(i => i.Key == item.Key);

            return(await maybeExisting.Match(
                       plexShow => Right <BaseError, MediaItemScanResult <PlexShow> >(
                           new MediaItemScanResult <PlexShow>(plexShow)
            {
                IsAdded = true
            }).AsTask(),
                       async() => await AddPlexShow(dbContext, library, item)));
        }
Exemple #2
0
        private async Task <Either <BaseError, Unit> > ScanSeasons(
            PlexLibrary plexMediaSourceLibrary,
            PlexShow show,
            PlexConnection connection,
            PlexServerAuthToken token)
        {
            Either <BaseError, List <PlexSeason> > entries = await _plexServerApiClient.GetShowSeasons(
                plexMediaSourceLibrary,
                show,
                connection,
                token);

            return(await entries.Match <Task <Either <BaseError, Unit> > >(
                       async seasonEntries =>
            {
                foreach (PlexSeason incoming in seasonEntries)
                {
                    incoming.ShowId = show.Id;

                    // TODO: figure out how to rebuild playlists
                    Either <BaseError, PlexSeason> maybeSeason = await _televisionRepository
                                                                 .GetOrAddPlexSeason(plexMediaSourceLibrary, incoming)
                                                                 .BindT(existing => UpdateArtwork(existing, incoming));

                    await maybeSeason.Match(
                        async season => await ScanEpisodes(plexMediaSourceLibrary, season, connection, token),
                        error =>
                    {
                        _logger.LogWarning(
                            "Error processing plex show at {Key}: {Error}",
                            incoming.Key,
                            error.Value);
                        return Task.CompletedTask;
                    });
                }

                var seasonKeys = seasonEntries.Map(s => s.Key).ToList();
                await _televisionRepository.RemoveMissingPlexSeasons(show.Key, seasonKeys);

                return Unit.Default;
            },
                       error =>
            {
                _logger.LogWarning(
                    "Error synchronizing plex library {Path}: {Error}",
                    plexMediaSourceLibrary.Name,
                    error.Value);

                return Left <BaseError, Unit>(error).AsTask();
            }));
        }
Exemple #3
0
 public async Task <Either <BaseError, List <PlexSeason> > > GetShowSeasons(
     PlexLibrary library,
     PlexShow show,
     PlexConnection connection,
     PlexServerAuthToken token)
 {
     try
     {
         IPlexServerApi service = XmlServiceFor(connection.Uri);
         return(await service.GetShowChildren(show.Key.Split("/").Reverse().Skip(1).Head(), token.AuthToken)
                .Map(r => r.Metadata)
                .Map(list => list.Map(metadata => ProjectToSeason(metadata, library.MediaSourceId)).ToList()));
     }
     catch (Exception ex)
     {
         return(BaseError.New(ex.ToString()));
     }
 }
Exemple #4
0
        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);
        }
Exemple #5
0
 public Task <Unit> SetPlexEtag(PlexShow show, string etag) => throw new NotSupportedException();
Exemple #6
0
 public Task <Either <BaseError, MediaItemScanResult <PlexShow> > > GetOrAddPlexShow(
     PlexLibrary library,
     PlexShow item) =>
 throw new NotSupportedException();
Exemple #7
0
    private async Task <Either <BaseError, Unit> > ScanSeasons(
        PlexLibrary library,
        List <PlexPathReplacement> pathReplacements,
        PlexShow show,
        PlexConnection connection,
        PlexServerAuthToken token,
        string ffmpegPath,
        string ffprobePath,
        bool deepScan,
        CancellationToken cancellationToken)
    {
        List <PlexItemEtag> existingSeasons = await _plexTelevisionRepository.GetExistingPlexSeasons(library, show);

        Either <BaseError, List <PlexSeason> > entries = await _plexServerApiClient.GetShowSeasons(
            library,
            show,
            connection,
            token);

        foreach (BaseError error in entries.LeftToSeq())
        {
            return(error);
        }

        var seasonEntries = entries.RightToSeq().Flatten().ToList();

        foreach (PlexSeason incoming in seasonEntries)
        {
            incoming.ShowId = show.Id;

            // TODO: figure out how to rebuild playlists
            Either <BaseError, PlexSeason> maybeSeason = await _televisionRepository
                                                         .GetOrAddPlexSeason(library, incoming)
                                                         .BindT(existing => UpdateMetadataAndArtwork(existing, incoming, deepScan));

            foreach (BaseError error in maybeSeason.LeftToSeq())
            {
                _logger.LogWarning(
                    "Error processing plex season at {Key}: {Error}",
                    incoming.Key,
                    error.Value);

                return(error);
            }

            foreach (PlexSeason season in maybeSeason.RightToSeq())
            {
                Either <BaseError, Unit> scanResult = await ScanEpisodes(
                    library,
                    pathReplacements,
                    season,
                    connection,
                    token,
                    ffmpegPath,
                    ffprobePath,
                    deepScan,
                    cancellationToken);

                foreach (ScanCanceled error in scanResult.LeftToSeq().OfType <ScanCanceled>())
                {
                    return(error);
                }

                await _plexTelevisionRepository.SetPlexEtag(season, incoming.Etag);

                season.Show = show;

                // TODO: if any seasons are unavailable or not found, flag show as unavailable/not found

                await _searchIndex.AddItems(_searchRepository, new List <MediaItem> {
                    season
                });
            }
        }

        var        fileNotFoundKeys = existingSeasons.Map(m => m.Key).Except(seasonEntries.Map(m => m.Key)).ToList();
        List <int> ids = await _plexTelevisionRepository.FlagFileNotFoundSeasons(library, fileNotFoundKeys);

        await _searchIndex.RebuildItems(_searchRepository, ids);

        return(Unit.Default);
    }
Exemple #8
0
    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);
    }
Exemple #9
0
        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);
        }