/// <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[]
/// <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); }
private async Task SyncUserLibrary(User user, TraktUser traktUser, double progPercent, double percentPerUser, IProgress <double> progress, CancellationToken cancellationToken) { var libraryRoot = user.RootFolder; // purely for progress reporting var mediaItemsCount = libraryRoot.GetRecursiveChildren(user).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 + "'."); var percentPerItem = (float)percentPerUser / mediaItemsCount / 2.0; /* * 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 movieItems = libraryRoot.GetRecursiveChildren(user) .Where(x => x is Movie) .Where(x => _traktApi.CanSync(x, traktUser)) .OrderBy(x => x.Name) .ToList(); var movies = new List <Movie>(); var playedMovies = new List <Movie>(); var unPlayedMovies = new List <Movie>(); foreach (var child in movieItems) { cancellationToken.ThrowIfCancellationRequested(); var movie = child as Movie; var userData = _userDataManager.GetUserData(user.Id, child.GetUserDataKey()); var collectedMovies = SyncFromTraktTask.FindMatches(movie, traktCollectedMovies).ToList(); if (!collectedMovies.Any() || collectedMovies.All(collectedMovie => collectedMovie.MetadataIsDifferent(movie))) { movies.Add(movie); } var movieWatched = SyncFromTraktTask.FindMatch(movie, traktWatchedMovies); if (userData.Played) { if (movieWatched == null) { playedMovies.Add(movie); } } else { if (movieWatched != null) { unPlayedMovies.Add(movie); } } // purely for progress reporting progPercent += percentPerItem; progress.Report(progPercent); } _logger.Info("Movies to add to Collection: " + movies.Count); // send any remaining entries if (movies.Count > 0) { try { var dataContracts = await _traktApi.SendLibraryUpdateAsync(movies, traktUser, cancellationToken, EventType.Add) .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); } // purely for progress reporting progPercent += (percentPerItem * movies.Count); progress.Report(progPercent); } _logger.Info("Movies to set watched: " + playedMovies.Count); if (playedMovies.Count > 0) { try { var dataContracts = await _traktApi.SendMoviePlaystateUpdates(playedMovies, traktUser, true, cancellationToken); if (dataContracts != null) { foreach (var traktSyncResponse in dataContracts) { LogTraktResponseDataContract(traktSyncResponse); } } } catch (Exception e) { _logger.ErrorException("Error updating movie play states", e); } // purely for progress reporting progPercent += (percentPerItem * playedMovies.Count); progress.Report(progPercent); } _logger.Info("Movies to set unwatched: " + unPlayedMovies.Count); if (unPlayedMovies.Count > 0) { try { var dataContracts = await _traktApi.SendMoviePlaystateUpdates(unPlayedMovies, traktUser, false, cancellationToken); if (dataContracts != null) { foreach (var traktSyncResponse in dataContracts) { LogTraktResponseDataContract(traktSyncResponse); } } } catch (Exception e) { _logger.ErrorException("Error updating movie play states", e); } // purely for progress reporting progPercent += (percentPerItem * unPlayedMovies.Count); progress.Report(progPercent); } var traktWatchedShows = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false); var traktCollectedShows = await _traktApi.SendGetCollectedShowsRequest(traktUser).ConfigureAwait(false); var episodeItems = libraryRoot.GetRecursiveChildren(user) .Where(x => x is Episode) .Where(x => _traktApi.CanSync(x, traktUser)) .OrderBy(x => x is Episode ? (x as Episode).SeriesName : null) .ToList(); var episodes = new List <Episode>(); var playedEpisodes = new List <Episode>(); var unPlayedEpisodes = new List <Episode>(); foreach (var child in episodeItems) { cancellationToken.ThrowIfCancellationRequested(); var episode = child as Episode; var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey()); var isPlayedTraktTv = false; var traktWatchedShow = SyncFromTraktTask.FindMatch(episode.Series, traktWatchedShows); if (traktWatchedShow != null && 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) { playedEpisodes.Add(episode); } // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list else if (userData != null && !userData.Played && isPlayedTraktTv) { unPlayedEpisodes.Add(episode); } var traktCollectedShow = SyncFromTraktTask.FindMatch(episode.Series, traktCollectedShows); if (traktCollectedShow == null || 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)) { episodes.Add(episode); } // purely for progress reporting progPercent += percentPerItem; progress.Report(progPercent); } _logger.Info("Episodes to add to Collection: " + episodes.Count); if (episodes.Count > 0) { try { var dataContracts = await _traktApi.SendLibraryUpdateAsync(episodes, traktUser, cancellationToken, EventType.Add) .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); } // purely for progress reporting progPercent += (percentPerItem * episodes.Count); progress.Report(progPercent); } _logger.Info("Episodes to set watched: " + playedEpisodes.Count); if (playedEpisodes.Count > 0) { try { var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(playedEpisodes, traktUser, true, cancellationToken); if (dataContracts != null) { foreach (var traktSyncResponse in dataContracts) { LogTraktResponseDataContract(traktSyncResponse); } } } catch (Exception e) { _logger.ErrorException("Error updating episode play states", e); } // purely for progress reporting progPercent += (percentPerItem * playedEpisodes.Count); progress.Report(progPercent); } _logger.Info("Episodes to set unwatched: " + unPlayedEpisodes.Count); if (unPlayedEpisodes.Count > 0) { try { var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(unPlayedEpisodes, traktUser, false, cancellationToken); if (dataContracts != null) { foreach (var traktSyncResponse in dataContracts) { LogTraktResponseDataContract(traktSyncResponse); } } } catch (Exception e) { _logger.ErrorException("Error updating episode play states", e); } // purely for progress reporting progPercent += (percentPerItem * unPlayedEpisodes.Count); progress.Report(progPercent); } }