private async Task SyncUserLibrary(User user,
                                           TraktUser traktUser,
                                           double progPercent,
                                           double percentPerUser,
                                           IProgress <double> progress,
                                           CancellationToken cancellationToken)
        {
            var libraryRoot = user.RootFolder;

            /*
             * 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.
             */
            List <TraktMovieDataContract> tMovies = await _traktApi.SendGetAllMoviesRequest(traktUser).ConfigureAwait(false);

            List <TraktUserLibraryShowDataContract> tShowsWatched = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false);

            var movies           = new List <Movie>();
            var episodes         = new List <Episode>();
            var playedMovies     = new List <Movie>();
            var playedEpisodes   = new List <Episode>();
            var unPlayedMovies   = new List <Movie>();
            var unPlayedEpisodes = new List <Episode>();
            var currentSeriesId  = Guid.Empty;

            var mediaItems = libraryRoot.GetRecursiveChildren(user)
                             .Where(SyncFromTraktTask.CanSync)
                             .ToList();

            if (mediaItems.Count == 0)
            {
                _logger.Info("No trakt media found for '" + user.Name + "'. Have trakt locations been configured?");
                return;
            }

            // purely for progress reporting
            var percentPerItem = percentPerUser / mediaItems.Count;

            foreach (var child in mediaItems)
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (child.Path == null || child.LocationType == LocationType.Virtual)
                {
                    continue;
                }

                if (child is Movie)
                {
                    var movie = (Movie)child;
                    movies.Add(movie);

                    var userData = _userDataManager.GetUserData(user.Id, child.GetUserDataKey());

                    var traktTvMovie = SyncFromTraktTask.FindMatch(child, tMovies);

                    if (traktTvMovie != null && userData.Played && (traktTvMovie.Watched == false || traktTvMovie.Plays < userData.PlayCount))
                    {
                        playedMovies.Add(movie);
                    }
                    else if (traktTvMovie != null && traktTvMovie.Watched && !userData.Played)
                    {
                        unPlayedMovies.Add(movie);
                    }

                    // publish if the list hits a certain size
                    if (movies.Count >= 120)
                    {
                        // Add movies to library
                        try
                        {
                            var dataContract = await _traktApi.SendLibraryUpdateAsync(movies, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                            if (dataContract != null)
                            {
                                LogTraktResponseDataContract(dataContract);
                            }
                        }
                        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);
                        }
                        movies.Clear();

                        // Mark movies seen
                        if (playedMovies.Count > 0)
                        {
                            try
                            {
                                var dataCotract = await _traktApi.SendMoviePlaystateUpdates(playedMovies, traktUser, true, cancellationToken);

                                if (dataCotract != null)
                                {
                                    LogTraktResponseDataContract(dataCotract);
                                }
                            }
                            catch (Exception e)
                            {
                                _logger.ErrorException("Error updating played state", e);
                            }

                            playedMovies.Clear();
                        }

                        // Mark movies unseen
                        if (unPlayedMovies.Count > 0)
                        {
                            try
                            {
                                var dataContract = await _traktApi.SendMoviePlaystateUpdates(unPlayedMovies, traktUser, false, cancellationToken);

                                if (dataContract != null)
                                {
                                    LogTraktResponseDataContract(dataContract);
                                }
                            }
                            catch (Exception e)
                            {
                                _logger.ErrorException("Error updating played state", e);
                            }

                            unPlayedMovies.Clear();
                        }
                    }
                }
                else if (child is Episode)
                {
                    var ep     = child as Episode;
                    var series = ep.Series;

                    var userData = _userDataManager.GetUserData(user.Id, ep.GetUserDataKey());

                    var isPlayedTraktTv = false;

                    var traktTvShow = SyncFromTraktTask.FindMatch(series, tShowsWatched);

                    if (traktTvShow != null && traktTvShow.Seasons != null && traktTvShow.Seasons.Count > 0)
                    {
                        foreach (var episode in from season in traktTvShow.Seasons where ep.Season != null && season.Season.Equals(ep.Season.IndexNumber) && season.Episodes != null && season.Episodes.Count > 0 from episode in season.Episodes where episode.Equals(ep.IndexNumber) select episode)
                        {
                            isPlayedTraktTv = true;
                        }
                    }

                    if (series != null && currentSeriesId != series.Id && episodes.Count > 0)
                    {
                        // We're starting a new show. Finish up with the old one

                        // Add episodes to trakt.tv library
                        try
                        {
                            var dataContract = await _traktApi.SendLibraryUpdateAsync(episodes, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                            if (dataContract != null)
                            {
                                LogTraktResponseDataContract(dataContract);
                            }
                        }
                        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);
                        }

                        // Update played state of these episodes
                        if (playedEpisodes.Count > 0)
                        {
                            try
                            {
                                var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(playedEpisodes, traktUser, true, cancellationToken);

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

                        if (unPlayedEpisodes.Count > 0)
                        {
                            try
                            {
                                var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(unPlayedEpisodes, traktUser, false, cancellationToken);

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

                        episodes.Clear();
                        playedEpisodes.Clear();
                        unPlayedEpisodes.Clear();
                    }

                    if (ep.Series != null)
                    {
                        currentSeriesId = ep.Series.Id;
                        episodes.Add(ep);
                    }

                    // 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(ep);
                    }
                    // 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(ep);
                    }
                }

                // purely for progress reporting
                progPercent += percentPerItem;
                progress.Report(progPercent);
            }

            // send any remaining entries
            if (movies.Count > 0)
            {
                try
                {
                    var dataContract = await _traktApi.SendLibraryUpdateAsync(movies, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                    if (dataContract != null)
                    {
                        LogTraktResponseDataContract(dataContract);
                    }
                }
                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);
                }
            }

            if (episodes.Count > 0)
            {
                try
                {
                    var dataContract = await _traktApi.SendLibraryUpdateAsync(episodes, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                    if (dataContract != null)
                    {
                        LogTraktResponseDataContract(dataContract);
                    }
                }
                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);
                }
            }

            if (playedMovies.Count > 0)
            {
                try
                {
                    var dataContract = await _traktApi.SendMoviePlaystateUpdates(playedMovies, traktUser, true, cancellationToken);

                    if (dataContract != null)
                    {
                        LogTraktResponseDataContract(dataContract);
                    }
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Error updating movie play states", e);
                }
            }

            if (playedEpisodes.Count > 0)
            {
                try
                {
                    var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(playedEpisodes, traktUser, true, cancellationToken);

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

            if (unPlayedEpisodes.Count > 0)
            {
                try
                {
                    var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(unPlayedEpisodes, traktUser, false, cancellationToken);

                    if (dataContracts != null)
                    {
                        foreach (var dataContract in dataContracts)
                        {
                            LogTraktResponseDataContract(dataContract);
                        }
                    }
                }
                catch (Exception e)
                {
                    _logger.ErrorException("Error updating episode play states", e);
                }
            }
        }
예제 #2
0
        private async Task SyncTraktDataForUser(User user, double currentProgress, CancellationToken cancellationToken, IProgress <double> progress, double percentPerUser)
        {
            var libraryRoot = user.RootFolder;
            var traktUser   = UserHelper.GetTraktUser(user);

            IEnumerable <TraktMovieDataContract>           tMovies;
            IEnumerable <TraktUserLibraryShowDataContract> tShowsCollection;
            IEnumerable <TraktUserLibraryShowDataContract> tShowsWatched;

            try
            {
                /*
                 * In order to be as accurate as possible. We need to download the users show collection & the users watched shows.
                 * It's unfortunate that trakt.tv doesn't explicitly supply a bulk method to determine shows that have not been watched
                 * like they do for movies.
                 */
                tMovies = await _traktApi.SendGetAllMoviesRequest(traktUser).ConfigureAwait(false);

                tShowsCollection = await _traktApi.SendGetCollectionShowsRequest(traktUser).ConfigureAwait(false);

                tShowsWatched = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Exception handled", ex);
                return;
            }


            _logger.Info("Trakt.tv Movies count = " + tMovies.Count());
            _logger.Info("Trakt.tv ShowsCollection count = " + tShowsCollection.Count());
            _logger.Info("Trakt.tv ShowsWatched count = " + tShowsWatched.Count());


            var mediaItems = libraryRoot.GetRecursiveChildren(user)
                             .Where(CanSync)
                             .OrderBy(i =>
            {
                var episode = i as Episode;

                return(episode != null ? episode.Series.Id : i.Id);
            })
                             .ToList();

            // purely for progress reporting
            var percentPerItem = percentPerUser / mediaItems.Count;

            foreach (var movie in mediaItems.OfType <Movie>())
            {
                var matchedMovie = FindMatch(movie, tMovies);

                if (matchedMovie != null)
                {
                    var userData = _userDataManager.GetUserData(user.Id, movie.GetUserDataKey());

                    if (matchedMovie.Plays >= 1)
                    {
                        // set movie as watched
                        userData.Played    = true;
                        userData.PlayCount = Math.Max(matchedMovie.Plays, userData.PlayCount); // keep the highest play count

                        // Set last played to whichever is most recent, remote or local time...
                        if (matchedMovie.LastPlayed > 0)
                        {
                            var tLastPlayed = matchedMovie.LastPlayed.ConvertEpochToDateTime();
                            userData.LastPlayedDate = tLastPlayed > userData.LastPlayedDate
                                                                  ? tLastPlayed
                                                                  : userData.LastPlayedDate;
                        }
                    }
                    else
                    {
                        // set as unwatched
                        userData.Played         = false;
                        userData.PlayCount      = 0;
                        userData.LastPlayedDate = null;
                    }

                    await _userDataManager.SaveUserData(user.Id, movie, userData, UserDataSaveReason.Import, cancellationToken);
                }
                else
                {
                    _logger.Info("Failed to match " + movie.Name);
                }

                // purely for progress reporting
                currentProgress += percentPerItem;
                progress.Report(currentProgress);
            }

            foreach (var episode in mediaItems.OfType <Episode>())
            {
                var matchedShow = FindMatch(episode.Series, tShowsCollection);

                if (matchedShow != null)
                {
                    var matchedSeason = matchedShow.Seasons
                                        .FirstOrDefault(tSeason => tSeason.Season == (episode.ParentIndexNumber ?? -1));

                    // if it's not a match then it means trakt doesn't know about the episode, leave the watched state alone and move on
                    if (matchedSeason != null && matchedSeason.Episodes.Contains(episode.IndexNumber ?? -1))
                    {
                        // episode is in users libary. Now we need to determine if it's watched
                        var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey());

                        var watchedShowMatch = FindMatch(episode.Series, tShowsWatched);

                        var isWatched = false;

                        if (watchedShowMatch != null)
                        {
                            var watchedSeasonMatch = watchedShowMatch.Seasons
                                                     .FirstOrDefault(tSeason => tSeason.Season == (episode.ParentIndexNumber ?? -1));

                            if (watchedSeasonMatch != null)
                            {
                                if (watchedSeasonMatch.Episodes.Contains(episode.IndexNumber ?? -1))
                                {
                                    userData.Played = true;
                                    isWatched       = true;
                                }
                                else
                                {
                                    _logger.Debug("No Episode match in Watched shows list " + GetVerboseEpisodeData(episode));
                                }
                            }
                            else
                            {
                                _logger.Debug("No Season match in Watched shows list " + GetVerboseEpisodeData(episode));
                            }
                        }
                        else
                        {
                            _logger.Debug("No Show match in Watched shows list " + GetVerboseEpisodeData(episode));
                        }

                        if (!isWatched)
                        {
                            userData.Played         = false;
                            userData.PlayCount      = 0;
                            userData.LastPlayedDate = null;
                        }

                        await _userDataManager.SaveUserData(user.Id, episode, userData, UserDataSaveReason.Import, cancellationToken);
                    }
                    else
                    {
                        _logger.Info("Failed to match episode/season numbers " + GetVerboseEpisodeData(episode));
                    }
                }
                else
                {
                    _logger.Info("Failed to match show " + GetVerboseEpisodeData(episode));
                }

                // purely for progress reporting
                currentProgress += percentPerItem;
                progress.Report(currentProgress);
            }
            //_logger.Info(syncItemFailures + " items not parsed");
        }
예제 #3
0
        private async Task SyncTraktDataForUser(User user, double currentProgress, CancellationToken cancellationToken, IProgress <double> progress, double percentPerUser)
        {
            var libraryRoot = user.RootFolder;
            var traktUser   = UserHelper.GetTraktUser(user);

            IEnumerable <TraktMovieDataContract>           tMovies;
            IEnumerable <TraktUserLibraryShowDataContract> tShowsCollection;
            IEnumerable <TraktUserLibraryShowDataContract> tShowsWatched;

            try
            {
                /*
                 * In order to be as accurate as possible. We need to download the users show collection & the users watched shows.
                 * It's unfortunate that trakt.tv doesn't explicitly supply a bulk method to determine shows that have not been watched
                 * like they do for movies.
                 */
                tMovies = await _traktApi.SendGetAllMoviesRequest(traktUser).ConfigureAwait(false);

                tShowsCollection = await _traktApi.SendGetCollectionShowsRequest(traktUser).ConfigureAwait(false);

                tShowsWatched = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Exception handled", ex);
                return;
            }


            _logger.Info("tMovies count = " + tMovies.Count());
            _logger.Info("tShowsCollection count = " + tShowsCollection.Count());
            _logger.Info("tShowsWatched count = " + tShowsWatched.Count());

            var mediaItems = libraryRoot.GetRecursiveChildren(user)
                             .Where(i =>
            {
                var movie = i as Movie;

                if (movie != null)
                {
                    var imdbId = movie.GetProviderId(MetadataProviders.Imdb);

                    if (string.IsNullOrEmpty(imdbId))
                    {
                        return(false);
                    }

                    return(true);
                }

                var episode = i as Episode;

                if (episode != null && episode.Series != null)
                {
                    var tvdbId = episode.Series.GetProviderId(MetadataProviders.Tvdb);

                    if (string.IsNullOrEmpty(tvdbId))
                    {
                        return(false);
                    }

                    return(true);
                }

                return(false);
            })
                             .OrderBy(i =>
            {
                var episode = i as Episode;

                return(episode != null ? episode.Series.Id : i.Id);
            })
                             .ToList();

            // purely for progress reporting
            var percentPerItem = percentPerUser / mediaItems.Count;

            // Turn off UserDataManager.UserDataSaved event listener until task completes
            ServerMediator.Instance.DisableUserDataSavedEventListener();

            foreach (var movie in mediaItems.OfType <Movie>())
            {
                /*
                 * First make sure this child is in the users collection. If not, skip it. if it is in the collection then we need
                 * to see if it's in the watched movies list. If it is, mark it watched, otherwise mark it unwatched.
                 */
                var imdbId = movie.GetProviderId(MetadataProviders.Imdb);

                var matchedMovie = tMovies.FirstOrDefault(i => i.ImdbId == imdbId);

                if (matchedMovie != null)
                {
                    var userData = _userDataManager.GetUserData(user.Id, movie.GetUserDataKey());

                    if (matchedMovie.Plays >= 1)
                    {
                        // set movie as watched
                        userData.Played    = true;
                        userData.PlayCount = Math.Max(matchedMovie.Plays, userData.PlayCount); // keep the highest play count

                        // Set last played to whichever is most recent, remote or local time...
                        if (matchedMovie.LastPlayed > 0)
                        {
                            var tLastPlayed = matchedMovie.LastPlayed.ConvertEpochToDateTime();
                            userData.LastPlayedDate = tLastPlayed > userData.LastPlayedDate
                                                                  ? tLastPlayed
                                                                  : userData.LastPlayedDate;
                        }
                    }
                    else
                    {
                        // set as unwatched
                        userData.Played         = false;
                        userData.PlayCount      = 0;
                        userData.LastPlayedDate = null;
                    }

                    await _userDataManager.SaveUserData(user.Id, movie, userData, UserDataSaveReason.TogglePlayed, cancellationToken);
                }

                // purely for progress reporting
                currentProgress += percentPerItem;
                progress.Report(currentProgress);
            }

            foreach (var episode in mediaItems.OfType <Episode>())
            {
                /*
                 * First make sure this child is in the users collection. If not, skip it. if it is in the collection then we need
                 * to see if it's in the watched shows list. If it is, mark it watched, otherwise mark it unwatched.
                 */
                var tvdbId = episode.Series.GetProviderId(MetadataProviders.Tvdb);

                var matchedShow = tShowsCollection.FirstOrDefault(tShow => tShow.TvdbId == tvdbId);

                if (matchedShow != null)
                {
                    var matchedSeason = matchedShow.Seasons.FirstOrDefault(tSeason => tSeason.Season == (episode.ParentIndexNumber ?? -1));

                    if (matchedSeason != null)
                    {
                        // if it's not a match then it means trakt doesn't know about the episode, leave the watched state alone and move on
                        if (matchedSeason.Episodes.Contains(episode.IndexNumber ?? -1))
                        {
                            // episode is in users libary. Now we need to determine if it's watched
                            var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey());

                            var watchedShowMatch = tShowsWatched.SingleOrDefault(tShow => tShow.TvdbId == tvdbId);

                            var isWatched = false;

                            if (watchedShowMatch != null)
                            {
                                var watchedSeasonMatch = watchedShowMatch.Seasons.FirstOrDefault(tSeason => tSeason.Season == (episode.ParentIndexNumber ?? -1));

                                if (watchedSeasonMatch != null)
                                {
                                    if (watchedSeasonMatch.Episodes.Contains(episode.IndexNumber ?? -1))
                                    {
                                        userData.Played = true;
                                        isWatched       = true;
                                    }
                                }
                            }

                            if (!isWatched)
                            {
                                userData.Played         = false;
                                userData.PlayCount      = 0;
                                userData.LastPlayedDate = null;
                            }

                            await _userDataManager.SaveUserData(user.Id, episode, userData, UserDataSaveReason.TogglePlayed, cancellationToken);
                        }
                    }
                }

                // purely for progress reporting
                currentProgress += percentPerItem;
                progress.Report(currentProgress);
            }

            // Turn on UserDataManager.UserDataSaved event listener since task has completed
            ServerMediator.Instance.EnableUserDataSavedEventListener();
        }
예제 #4
0
        public async Task Execute(CancellationToken cancellationToken, IProgress <double> progress)
        {
            var users = _userManager.Users.Where(u =>
            {
                var traktUser = UserHelper.GetTraktUser(u);

                return(traktUser != null && traktUser.TraktLocations != null && traktUser.TraktLocations.Length > 0);
            }).ToList();

            // No point going further if we don't have users.
            if (users.Count == 0)
            {
                _logger.Info("No Users returned");
                return;
            }

            // purely for progress reporting
            var progPercent    = 0.0;
            var percentPerUser = 100 / users.Count;

            foreach (var user in users)
            {
                var libraryRoot = user.RootFolder;
                var traktUser   = UserHelper.GetTraktUser(user);

                // I'll leave this in here for now, but in reality this continue should never be reached.
                if (traktUser == null || String.IsNullOrEmpty(traktUser.LinkedMbUserId))
                {
                    _logger.Error("traktUser is either null or has no linked MB account");
                    continue;
                }

                /*
                 * 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.
                 */
                IEnumerable <TraktMovieDataContract> tMovies = await _traktApi.SendGetAllMoviesRequest(traktUser).ConfigureAwait(false);

                IEnumerable <TraktUserLibraryShowDataContract> tShowsWatched = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false);

                var movies           = new List <Movie>();
                var episodes         = new List <Episode>();
                var playedMovies     = new List <Movie>();
                var playedEpisodes   = new List <Episode>();
                var unPlayedMovies   = new List <Movie>();
                var unPlayedEpisodes = new List <Episode>();
                var currentSeriesId  = Guid.Empty;

                var mediaItems = libraryRoot.GetRecursiveChildren(user)
                                 .Where(i => i.Name != null &&
                                        (i is Episode && ((Episode)i).Series != null && ((Episode)i).Series.ProviderIds.ContainsKey("Tvdb")) ||
                                        (i is Movie && i.ProviderIds.ContainsKey("Imdb")))
                                 .OrderBy(i =>
                {
                    var episode = i as Episode;

                    return(episode != null ? episode.Series.Id : i.Id);
                })
                                 .ToList();

                if (mediaItems.Count == 0)
                {
                    _logger.Info("No trakt media found for '" + user.Name + "'. Have trakt locations been configured?");
                    continue;
                }

                // purely for progress reporting
                var percentPerItem = percentPerUser / (double)mediaItems.Count;

                foreach (var child in mediaItems)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    if (child.Path == null || child.LocationType == LocationType.Virtual)
                    {
                        continue;
                    }

                    foreach (var s in traktUser.TraktLocations.Where(s => _fileSystem.ContainsSubPath(s, child.Path)))
                    {
                        if (child is Movie)
                        {
                            var movie = child as Movie;
                            movies.Add(movie);

                            var userData = _userDataManager.GetUserData(user.Id, movie.GetUserDataKey());

                            if (movie.ProviderIds.ContainsKey("Tmdb") && userData != null)
                            {
                                var traktTvMovie =
                                    tMovies.FirstOrDefault(
                                        tMovie => tMovie.TmdbId.Equals(movie.GetProviderId(MetadataProviders.Tmdb)));

                                if (traktTvMovie != null && userData.Played && (traktTvMovie.Watched == false || traktTvMovie.Plays < userData.PlayCount))
                                {
                                    playedMovies.Add(movie);
                                }
                                else if (traktTvMovie != null && traktTvMovie.Watched && !userData.Played)
                                {
                                    unPlayedMovies.Add(movie);
                                }
                            }

                            // publish if the list hits a certain size
                            if (movies.Count >= 120)
                            {
                                // Add movies to library
                                try
                                {
                                    var dataContract = await _traktApi.SendLibraryUpdateAsync(movies, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                                    if (dataContract != null)
                                    {
                                        LogTraktResponseDataContract(dataContract);
                                    }
                                }
                                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);
                                }
                                movies.Clear();

                                // Mark movies seen
                                if (playedMovies.Count > 0)
                                {
                                    try
                                    {
                                        var dataCotract = await _traktApi.SendMoviePlaystateUpdates(playedMovies, traktUser, true, cancellationToken);

                                        if (dataCotract != null)
                                        {
                                            LogTraktResponseDataContract(dataCotract);
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        _logger.ErrorException("Error updating played state", e);
                                    }

                                    playedMovies.Clear();
                                }

                                // Mark movies unseen
                                if (unPlayedMovies.Count > 0)
                                {
                                    try
                                    {
                                        var dataContract = await _traktApi.SendMoviePlaystateUpdates(unPlayedMovies, traktUser, false, cancellationToken);

                                        if (dataContract != null)
                                        {
                                            LogTraktResponseDataContract(dataContract);
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        _logger.ErrorException("Error updating played state", e);
                                    }

                                    unPlayedMovies.Clear();
                                }
                            }
                        }
                        else if (child is Episode)
                        {
                            var ep = child as Episode;

                            var userData = _userDataManager.GetUserData(user.Id, ep.GetUserDataKey());

                            var isPlayedTraktTv = false;

                            if (ep.Series != null && ep.Series.ProviderIds.ContainsKey("Tvdb"))
                            {
                                var traktTvShow =
                                    tShowsWatched.FirstOrDefault(
                                        tShow => tShow.TvdbId.Equals(ep.Series.GetProviderId(MetadataProviders.Tvdb)));

                                if (traktTvShow != null && traktTvShow.Seasons != null && traktTvShow.Seasons.Count > 0)
                                {
                                    foreach (var episode in from season in traktTvShow.Seasons where ep.Season != null && season.Season.Equals(ep.Season.IndexNumber) && season.Episodes != null && season.Episodes.Count > 0 from episode in season.Episodes where episode.Equals(ep.IndexNumber) select episode)
                                    {
                                        isPlayedTraktTv = true;
                                    }
                                }
                            }

                            if (ep.Series != null && currentSeriesId != ep.Series.Id && episodes.Count > 0)
                            {
                                // We're starting a new show. Finish up with the old one

                                // Add episodes to trakt.tv library
                                try
                                {
                                    var dataContract = await _traktApi.SendLibraryUpdateAsync(episodes, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                                    if (dataContract != null)
                                    {
                                        LogTraktResponseDataContract(dataContract);
                                    }
                                }
                                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);
                                }

                                // Update played state of these episodes
                                if (playedEpisodes.Count > 0)
                                {
                                    try
                                    {
                                        var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(playedEpisodes, traktUser, true, cancellationToken);

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

                                if (unPlayedEpisodes.Count > 0)
                                {
                                    try
                                    {
                                        var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(unPlayedEpisodes, traktUser, false, cancellationToken);

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

                                episodes.Clear();
                                playedEpisodes.Clear();
                                unPlayedEpisodes.Clear();
                            }

                            if (ep.Series != null)
                            {
                                currentSeriesId = ep.Series.Id;
                                episodes.Add(ep);
                            }

                            // 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(ep);
                            }
                            // 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(ep);
                            }
                        }
                    }

                    // purely for progress reporting
                    progPercent += percentPerItem;
                    progress.Report(progPercent);
                }

                // send any remaining entries
                if (movies.Count > 0)
                {
                    try
                    {
                        var dataContract = await _traktApi.SendLibraryUpdateAsync(movies, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                        if (dataContract != null)
                        {
                            LogTraktResponseDataContract(dataContract);
                        }
                    }
                    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);
                    }
                }

                if (episodes.Count > 0)
                {
                    try
                    {
                        var dataContract = await _traktApi.SendLibraryUpdateAsync(episodes, traktUser, cancellationToken, EventType.Add).ConfigureAwait(false);

                        if (dataContract != null)
                        {
                            LogTraktResponseDataContract(dataContract);
                        }
                    }
                    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);
                    }
                }

                if (playedMovies.Count > 0)
                {
                    try
                    {
                        var dataContract = await _traktApi.SendMoviePlaystateUpdates(playedMovies, traktUser, true, cancellationToken);

                        if (dataContract != null)
                        {
                            LogTraktResponseDataContract(dataContract);
                        }
                    }
                    catch (Exception e)
                    {
                        _logger.ErrorException("Error updating movie play states", e);
                    }
                }

                if (playedEpisodes.Count > 0)
                {
                    try
                    {
                        var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(playedEpisodes, traktUser, true, cancellationToken);

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

                if (unPlayedEpisodes.Count > 0)
                {
                    try
                    {
                        var dataContracts = await _traktApi.SendEpisodePlaystateUpdates(unPlayedEpisodes, traktUser, false, cancellationToken);

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