예제 #1
0
        /// <summary>
        /// Count media items and call <see cref="SyncMovies"/> and <see cref="SyncShows"/>
        /// </summary>
        /// <returns></returns>
        private async Task SyncUserLibrary(
            User user,
            TraktUser traktUser,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            // purely for progress reporting
            var mediaItemsCount =
                _libraryManager.GetItemList(
                    new InternalItemsQuery
            {
                IncludeItemTypes     = new[] { typeof(Movie).Name, typeof(Episode).Name },
                ExcludeLocationTypes = new[] { LocationType.Virtual }
            })
                .Count(i => _traktApi.CanSync(i, traktUser));

            if (mediaItemsCount == 0)
            {
                _logger.Info("No media found for '" + user.Name + "'.");
                return;
            }

            _logger.Info(mediaItemsCount + " Items found for '" + user.Name + "'.");

            await SyncMovies(user, traktUser, progress.Split(2), cancellationToken);
            await SyncShows(user, traktUser, progress.Split(2), cancellationToken);
        }
예제 #2
0
        private async Task SendEpisodePlaystateUpdates(
            bool seen,
            TraktUser traktUser,
            List <Episode> playedEpisodes,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            _logger.Info("Episodes to set " + (seen ? string.Empty : "un") + "watched: " + playedEpisodes.Count);
            if (playedEpisodes.Count > 0)
            {
                try
                {
                    var dataContracts =
                        await _traktApi.SendEpisodePlaystateUpdates(playedEpisodes, traktUser, false, seen, cancellationToken).ConfigureAwait(false);

                    if (dataContracts != null)
                    {
                        foreach (var con in dataContracts)
                        {
                            LogTraktResponseDataContract(con);
                        }
                    }
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Error updating episode play states", e);
                }

                progress.Report(100);
            }
        }
        private async Task SendMoviePlaystateUpdates(
            bool seen,
            TraktUser traktUser,
            List <Movie> playedMovies,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            _logger.LogInformation("Movies to set " + (seen ? string.Empty : "un") + "watched: " + playedMovies.Count);
            if (playedMovies.Count > 0)
            {
                try
                {
                    var dataContracts =
                        await _traktApi.SendMoviePlaystateUpdates(playedMovies, traktUser, seen, cancellationToken).ConfigureAwait(false);

                    if (dataContracts != null)
                    {
                        foreach (var traktSyncResponse in dataContracts)
                        {
                            LogTraktResponseDataContract(traktSyncResponse);
                        }
                    }
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Error updating movie play states");
                }

                progress.Report(100);
            }
        }
예제 #4
0
 /// <summary>
 /// Count media items and call <see cref="SyncMovies"/> and <see cref="SyncShows"/>
 /// </summary>
 /// <returns></returns>
 private async Task SyncUserLibrary(
     User user,
     TraktUser traktUser,
     ISplittableProgress <double> progress,
     CancellationToken cancellationToken)
 {
     await SyncMovies(user, traktUser, progress.Split(2), cancellationToken).ConfigureAwait(false);
     await SyncShows(user, traktUser, progress.Split(2), cancellationToken).ConfigureAwait(false);
 }
    /// <summary>
    /// Sync watched and collected status of <see cref="Movie"/>s with trakt.
    /// </summary>
    private async Task SyncMovies(
        Jellyfin.Data.Entities.User user,
        TraktUser traktUser,
        ISplittableProgress <double> progress,
        CancellationToken cancellationToken)
    {
        /*
         * In order to sync watched status to trakt.tv we need to know what's been watched on Trakt already. This
         * will stop us from endlessly incrementing the watched values on the site.
         */
        var traktWatchedMovies = await _traktApi.SendGetAllWatchedMoviesRequest(traktUser).ConfigureAwait(false);

        var traktCollectedMovies = await _traktApi.SendGetAllCollectedMoviesRequest(traktUser).ConfigureAwait(false);

        var libraryMovies =
            _libraryManager.GetItemList(
                new InternalItemsQuery(user)
        {
            IncludeItemTypes = new[] { BaseItemKind.Movie },
            IsVirtualItem    = false,
            OrderBy          = new[]
예제 #6
0
        private async Task SendEpisodeCollectionUpdates(
            bool collected,
            TraktUser traktUser,
            List <Episode> collectedEpisodes,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            _logger.Info("Episodes to add to Collection: " + collectedEpisodes.Count);
            if (collectedEpisodes.Count > 0)
            {
                try
                {
                    var dataContracts =
                        await
                        _traktApi.SendLibraryUpdateAsync(
                            collectedEpisodes,
                            traktUser,
                            cancellationToken,
                            collected?EventType.Add : EventType.Remove).ConfigureAwait(false);

                    if (dataContracts != null)
                    {
                        foreach (var traktSyncResponse in dataContracts)
                        {
                            LogTraktResponseDataContract(traktSyncResponse);
                        }
                    }
                }
                catch (ArgumentNullException argNullEx)
                {
                    _logger.ErrorException("ArgumentNullException handled sending episodes to trakt.tv", argNullEx);
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Exception handled sending episodes to trakt.tv", e);
                }

                progress.Report(100);
            }
        }
        private async Task SendMovieCollectionUpdates(
            bool collected,
            TraktUser traktUser,
            List <Movie> movies,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            _logger.LogInformation("Movies to " + (collected ? "add to" : "remove from") + " Collection: " + movies.Count);
            if (movies.Count > 0)
            {
                try
                {
                    var dataContracts =
                        await
                        _traktApi.SendLibraryUpdateAsync(
                            movies,
                            traktUser,
                            cancellationToken,
                            collected?EventType.Add : EventType.Remove).ConfigureAwait(false);

                    if (dataContracts != null)
                    {
                        foreach (var traktSyncResponse in dataContracts)
                        {
                            LogTraktResponseDataContract(traktSyncResponse);
                        }
                    }
                }
                catch (ArgumentNullException argNullEx)
                {
                    _logger.LogError(argNullEx, "ArgumentNullException handled sending movies to trakt.tv");
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Exception handled sending movies to trakt.tv");
                }

                progress.Report(100);
            }
        }
예제 #8
0
        private async Task SendEpisodeCollectionRemovals(
            TraktUser traktUser,
            List <Api.DataContracts.Sync.Collection.TraktShowCollected> uncollectedEpisodes,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            _logger.Info("Episodes to remove from Collection: " + uncollectedEpisodes.Count);
            if (uncollectedEpisodes.Count > 0)
            {
                try
                {
                    var dataContracts =
                        await
                        _traktApi.SendLibraryRemovalsAsync(
                            uncollectedEpisodes,
                            traktUser,
                            cancellationToken).ConfigureAwait(false);

                    if (dataContracts != null)
                    {
                        foreach (var traktSyncResponse in dataContracts)
                        {
                            LogTraktResponseDataContract(traktSyncResponse);
                        }
                    }
                }
                catch (ArgumentNullException argNullEx)
                {
                    _logger.ErrorException("ArgumentNullException handled sending episodes to trakt.tv", argNullEx);
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Exception handled sending episodes to trakt.tv", e);
                }

                progress.Report(100);
            }
        }
예제 #9
0
        private async Task SendMovieCollectionRemoves(
            TraktUser traktUser,
            List <TraktMovieCollected> movies,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            _logger.Info("Movies to remove from collection: " + movies.Count);
            if (movies.Count > 0)
            {
                try
                {
                    var dataContracts =
                        await
                        _traktApi.SendCollectionRemovalsAsync(
                            movies.Select(m => m.movie).ToList(),
                            traktUser,
                            cancellationToken).ConfigureAwait(false);

                    if (dataContracts != null)
                    {
                        foreach (var traktSyncResponse in dataContracts)
                        {
                            LogTraktResponseDataContract(traktSyncResponse);
                        }
                    }
                }
                catch (ArgumentNullException argNullEx)
                {
                    _logger.ErrorException("ArgumentNullException handled sending movies to trakt.tv", argNullEx);
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Exception handled sending movies to trakt.tv", e);
                }

                progress.Report(100);
            }
        }
예제 #10
0
        /// <summary>
        /// Sync watched and collected status of <see cref="Movie"/>s with trakt.
        /// </summary>
        private async Task SyncShows(
            User user,
            TraktUser traktUser,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            var traktWatchedShows = await _traktApi.SendGetWatchedShowsRequest(traktUser, cancellationToken).ConfigureAwait(false);

            var traktCollectedShows = await _traktApi.SendGetCollectedShowsRequest(traktUser, cancellationToken).ConfigureAwait(false);

            var episodeItems =
                _libraryManager.GetItemList(
                    new InternalItemsQuery(user)
            {
                IncludeItemTypes = new[] { typeof(Episode).Name },
                IsVirtualItem    = false,
                OrderBy          = new[]
                {
                    new ValueTuple <string, SortOrder>(ItemSortBy.SeriesSortName, SortOrder.Ascending)
                }
            })
                .Where(x => _traktApi.CanSync(x, traktUser))
                .ToList();

            var series =
                _libraryManager.GetItemList(
                    new InternalItemsQuery(user)
            {
                IncludeItemTypes = new[] { typeof(Series).Name },
                IsVirtualItem    = false
            })
                .Where(x => _traktApi.CanSync(x, traktUser))
                .OfType <Series>()
                .ToList();

            var collectedEpisodes = new List <Episode>();
            var uncollectedShows  = new List <Api.DataContracts.Sync.Collection.TraktShowCollected>();
            var playedEpisodes    = new List <Episode>();
            var unplayedEpisodes  = new List <Episode>();


            var decisionProgress = progress.Split(4).Split(episodeItems.Count);

            foreach (var child in episodeItems)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var episode          = child as Episode;
                var userData         = _userDataManager.GetUserData(user, episode);
                var isPlayedTraktTv  = false;
                var traktWatchedShow = Match.FindMatch(episode.Series, traktWatchedShows);

                if (traktWatchedShow?.seasons != null && traktWatchedShow.seasons.Count > 0)
                {
                    isPlayedTraktTv =
                        traktWatchedShow.seasons.Any(
                            season =>
                            season.number == episode.GetSeasonNumber() && season.episodes != null &&
                            season.episodes.Any(te => te.number == episode.IndexNumber && te.plays > 0));
                }

                // if the show has been played locally and is unplayed on trakt.tv then add it to the list
                if (userData != null && userData.Played && !isPlayedTraktTv)
                {
                    if (traktUser.PostWatchedHistory)
                    {
                        playedEpisodes.Add(episode);
                    }
                    else if (!traktUser.SkipUnwatchedImportFromTrakt)
                    {
                        if (userData.Played)
                        {
                            userData.Played = false;

                            _userDataManager.SaveUserData(
                                user.InternalId,
                                episode,
                                userData,
                                UserDataSaveReason.Import,
                                cancellationToken);
                        }
                    }
                }
                else if (userData != null && !userData.Played && isPlayedTraktTv)
                {
                    // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list
                    unplayedEpisodes.Add(episode);
                }

                var traktCollectedShow = Match.FindMatch(episode.Series, traktCollectedShows);
                if (traktCollectedShow?.seasons == null ||
                    traktCollectedShow.seasons.All(x => x.number != episode.ParentIndexNumber) ||
                    traktCollectedShow.seasons.First(x => x.number == episode.ParentIndexNumber)
                    .episodes.All(e => e.number != episode.IndexNumber))
                {
                    collectedEpisodes.Add(episode);
                }

                decisionProgress.Report(100);
            }
            // Check if we have all the collected items, add missing to uncollectedShows
            foreach (var traktShowCollected in traktCollectedShows)
            {
                _logger.Debug(_jsonSerializer.SerializeToString(series));
                var seriesMatch = Match.FindMatch(traktShowCollected.show, series);
                if (seriesMatch != null)
                {
                    var seriesEpisodes = episodeItems.OfType <Episode>().Where(e => e.Series.Id == seriesMatch.Id);

                    var uncollectedSeasons = new List <TraktShowCollected.TraktSeasonCollected>();
                    foreach (var traktSeasonCollected in traktShowCollected.seasons)
                    {
                        var uncollectedEpisodes =
                            new List <TraktEpisodeCollected>();
                        foreach (var traktEpisodeCollected in traktSeasonCollected.episodes)
                        {
                            if (seriesEpisodes.Any(e =>
                                                   e.ParentIndexNumber == traktSeasonCollected.number &&
                                                   e.IndexNumber == traktEpisodeCollected.number))
                            {
                            }
                            else
                            {
                                _logger.Debug("Could not match S{0}E{1} from {2} to any Emby episode, marking for collection removal", traktSeasonCollected.number, traktEpisodeCollected.number, _jsonSerializer.SerializeToString(traktShowCollected.show));
                                uncollectedEpisodes.Add(new TraktEpisodeCollected()
                                {
                                    number = traktEpisodeCollected.number
                                });
                            }
                        }

                        if (uncollectedEpisodes.Any())
                        {
                            uncollectedSeasons.Add(new TraktShowCollected.TraktSeasonCollected()
                            {
                                number = traktSeasonCollected.number, episodes = uncollectedEpisodes
                            });
                        }
                    }

                    if (uncollectedSeasons.Any())
                    {
                        uncollectedShows.Add(new TraktShowCollected()
                        {
                            ids = traktShowCollected.show.ids, title = traktShowCollected.show.title, year = traktShowCollected.show.year, seasons = uncollectedSeasons
                        });
                    }
                }
                else
                {
                    _logger.Debug("Could not match {0} to any Emby show, marking for collection removal", _jsonSerializer.SerializeToString(traktShowCollected.show));
                    uncollectedShows.Add(new TraktShowCollected()
                    {
                        ids = traktShowCollected.show.ids, title = traktShowCollected.show.title, year = traktShowCollected.show.year
                    });
                }
            }

            if (traktUser.SyncCollection)
            {
                await SendEpisodeCollectionAdds(traktUser, collectedEpisodes, progress.Split(4), cancellationToken)
                .ConfigureAwait(false);

                await SendEpisodeCollectionRemovals(traktUser, uncollectedShows, progress.Split(5), cancellationToken)
                .ConfigureAwait(false);
            }

            await SendEpisodePlaystateUpdates(true, traktUser, playedEpisodes, progress.Split(4), cancellationToken).ConfigureAwait(false);

            await SendEpisodePlaystateUpdates(false, traktUser, unplayedEpisodes, progress.Split(4), cancellationToken).ConfigureAwait(false);
        }
예제 #11
0
        /// <summary>
        /// Sync watched and collected status of <see cref="Movie"/>s with trakt.
        /// </summary>
        private async Task SyncMovies(
            User user,
            TraktUser traktUser,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            /*
             * In order to sync watched status to trakt.tv we need to know what's been watched on Trakt already. This
             * will stop us from endlessly incrementing the watched values on the site.
             */
            var traktWatchedMovies = await _traktApi.SendGetAllWatchedMoviesRequest(traktUser, cancellationToken).ConfigureAwait(false);

            var traktCollectedMovies = await _traktApi.SendGetAllCollectedMoviesRequest(traktUser, cancellationToken).ConfigureAwait(false);

            var libraryMovies =
                _libraryManager.GetItemList(
                    new InternalItemsQuery(user)
            {
                IncludeItemTypes = new[] { typeof(Movie).Name },
                IsVirtualItem    = false,
                OrderBy          = new[]
                {
                    new ValueTuple <string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending)
                }
            })
                .Where(x => _traktApi.CanSync(x, traktUser))
                .ToList();
            var collectedMovies   = new List <Movie>();
            var uncollectedMovies = new List <TraktMovieCollected>();
            var playedMovies      = new List <Movie>();
            var unplayedMovies    = new List <Movie>();

            var decisionProgress = progress.Split(4).Split(libraryMovies.Count);

            foreach (var child in libraryMovies)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var libraryMovie = child as Movie;
                var userData     = _userDataManager.GetUserData(user, child);

                // if movie is not collected, or (export media info setting is enabled and every collected matching movie has different metadata), collect it
                var collectedMathingMovies = Match.FindMatches(libraryMovie, traktCollectedMovies).ToList();
                if (!collectedMathingMovies.Any() ||
                    (traktUser.ExportMediaInfo &&
                     collectedMathingMovies.All(
                         collectedMovie => collectedMovie.MetadataIsDifferent(libraryMovie))))
                {
                    collectedMovies.Add(libraryMovie);
                }

                var movieWatched = Match.FindMatch(libraryMovie, traktWatchedMovies);

                // if the movie has been played locally and is unplayed on trakt.tv then add it to the list
                if (userData.Played)
                {
                    if (movieWatched == null)
                    {
                        if (traktUser.PostWatchedHistory)
                        {
                            playedMovies.Add(libraryMovie);
                        }
                        else if (!traktUser.SkipUnwatchedImportFromTrakt)
                        {
                            if (userData.Played)
                            {
                                userData.Played = false;

                                _userDataManager.SaveUserData(
                                    user.InternalId,
                                    libraryMovie,
                                    userData,
                                    UserDataSaveReason.Import,
                                    cancellationToken);
                            }
                        }
                    }
                }
                else
                {
                    // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list
                    if (movieWatched != null)
                    {
                        unplayedMovies.Add(libraryMovie);
                    }
                }

                decisionProgress.Report(100);
            }

            foreach (var traktCollectedMovie in traktCollectedMovies)
            {
                if (!Match.FindMatches(traktCollectedMovie, libraryMovies).Any())
                {
                    _logger.Debug("No matches for {0}, will be uncollected on Trakt", _jsonSerializer.SerializeToString(traktCollectedMovie.movie));
                    uncollectedMovies.Add(traktCollectedMovie);
                }
            }

            if (traktUser.SyncCollection)
            {
                // send movies to mark collected
                await SendMovieCollectionAdds(traktUser, collectedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false);

                // send movies to mark uncollected
                await SendMovieCollectionRemoves(traktUser, uncollectedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false);
            }
            // send movies to mark watched
            await SendMoviePlaystateUpdates(true, traktUser, playedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false);

            // send movies to mark unwatched
            await SendMoviePlaystateUpdates(false, traktUser, unplayedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false);
        }
예제 #12
0
        /// <summary>
        /// Sync watched and collected status of <see cref="Movie"/>s with trakt.
        /// </summary>
        private async Task SyncShows(
            User user,
            TraktUser traktUser,
            ISplittableProgress <double> progress,
            CancellationToken cancellationToken)
        {
            var traktWatchedShows = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false);

            var traktCollectedShows = await _traktApi.SendGetCollectedShowsRequest(traktUser).ConfigureAwait(false);

            var episodeItems =
                _libraryManager.GetItemList(
                    new InternalItemsQuery
            {
                IncludeItemTypes     = new[] { typeof(Episode).Name },
                ExcludeLocationTypes = new[] { LocationType.Virtual }
            })
                .Where(x => _traktApi.CanSync(x, traktUser))
                .OrderBy(x => (x as Episode)?.SeriesName)
                .ToList();

            var collectedEpisodes = new List <Episode>();
            var playedEpisodes    = new List <Episode>();
            var unplayedEpisodes  = new List <Episode>();

            var decisionProgress = progress.Split(4).Split(episodeItems.Count);

            foreach (var child in episodeItems)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var episode          = child as Episode;
                var userData         = _userDataManager.GetUserData(user.Id, episode);
                var isPlayedTraktTv  = false;
                var traktWatchedShow = SyncFromTraktTask.FindMatch(episode.Series, traktWatchedShows);

                if (traktWatchedShow?.Seasons != null && traktWatchedShow.Seasons.Count > 0)
                {
                    isPlayedTraktTv =
                        traktWatchedShow.Seasons.Any(
                            season =>
                            season.Number == episode.GetSeasonNumber() && season.Episodes != null &&
                            season.Episodes.Any(te => te.Number == episode.IndexNumber && te.Plays > 0));
                }

                // if the show has been played locally and is unplayed on trakt.tv then add it to the list
                if (userData != null && userData.Played && !isPlayedTraktTv)
                {
                    if (traktUser.PostWatchedHistory)
                    {
                        playedEpisodes.Add(episode);
                    }
                    else if (!traktUser.SkipUnwatchedImportFromTrakt)
                    {
                        userData.Played = false;
                        await
                        _userDataManager.SaveUserData(
                            user.Id,
                            episode,
                            userData,
                            UserDataSaveReason.Import,
                            cancellationToken);
                    }
                }
                else if (userData != null && !userData.Played && isPlayedTraktTv)
                {
                    // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list
                    unplayedEpisodes.Add(episode);
                }

                var traktCollectedShow = SyncFromTraktTask.FindMatch(episode.Series, traktCollectedShows);
                if (traktCollectedShow?.Seasons == null ||
                    traktCollectedShow.Seasons.All(x => x.Number != episode.ParentIndexNumber) ||
                    traktCollectedShow.Seasons.First(x => x.Number == episode.ParentIndexNumber)
                    .Episodes.All(e => e.Number != episode.IndexNumber))
                {
                    collectedEpisodes.Add(episode);
                }

                decisionProgress.Report(100);
            }

            await SendEpisodeCollectionUpdates(true, traktUser, collectedEpisodes, progress.Split(4), cancellationToken);

            await SendEpisodePlaystateUpdates(true, traktUser, playedEpisodes, progress.Split(4), cancellationToken);

            await SendEpisodePlaystateUpdates(false, traktUser, unplayedEpisodes, progress.Split(4), cancellationToken);
        }