Пример #1
0
        public string AuthorizeDevice(TraktUser traktUser)
        {
            var deviceCodeRequest = new
            {
                client_id = TraktUris.ClientId
            };

            TraktDeviceCode deviceCode;

            using (var response = PostToTrakt(TraktUris.DeviceCode, deviceCodeRequest, null))
            {
                deviceCode = _jsonSerializer.DeserializeFromStream <TraktDeviceCode>(response.Result);
            }

            // Start polling in the background
            Plugin.Instance.PollingTasks[traktUser.LinkedMbUserId] = Task.Run(() => PollForAccessToken(deviceCode, traktUser));

            return(deviceCode.user_code);
        }
Пример #2
0
        public async Task RefreshUserAccessToken(TraktUser traktUser)
        {
            if (string.IsNullOrWhiteSpace(traktUser.RefreshToken))
            {
                _logger.LogError("Tried to reauthenticate with Trakt, but no refreshToken was available");
                return;
            }

            var data = new TraktUserRefreshTokenRequest
            {
                client_id     = TraktUris.ClientId,
                client_secret = TraktUris.ClientSecret,
                redirect_uri  = "urn:ietf:wg:oauth:2.0:oob",
                refresh_token = traktUser.RefreshToken,
                grant_type    = "refresh_token"
            };

            TraktUserAccessToken userAccessToken;

            try
            {
                using (var response = await PostToTrakt(TraktUris.AccessToken, data).ConfigureAwait(false))
                {
                    await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

                    userAccessToken = await JsonSerializer.DeserializeAsync <TraktUserAccessToken>(stream, _jsonOptions).ConfigureAwait(false);
                }
            }
            catch (HttpRequestException ex)
            {
                _logger.LogError(ex, "An error occurred during token refresh");
                return;
            }

            if (userAccessToken != null)
            {
                traktUser.AccessToken           = userAccessToken.access_token;
                traktUser.RefreshToken          = userAccessToken.refresh_token;
                traktUser.AccessTokenExpiration = DateTime.Now.AddSeconds(userAccessToken.expirationWithBuffer);
                Plugin.Instance.SaveConfiguration();
                _logger.LogInformation("Successfully refreshed the access token for user {UserId}", traktUser.LinkedMbUserId);
            }
        }
Пример #3
0
        public async Task <string> AuthorizeDevice(TraktUser traktUser)
        {
            var deviceCodeRequest = new
            {
                client_id = TraktUris.ClientId
            };

            TraktDeviceCode deviceCode;

            using (var response = await PostToTrakt(TraktUris.DeviceCode, deviceCodeRequest, null).ConfigureAwait(false))
            {
                deviceCode = await JsonSerializer.DeserializeAsync <TraktDeviceCode>(response, _jsonOptions).ConfigureAwait(false);
            }

            // Start polling in the background
            Plugin.Instance.PollingTasks[traktUser.LinkedMbUserId] = Task.Run(() => PollForAccessToken(deviceCode, traktUser));

            return(deviceCode.user_code);
        }
Пример #4
0
        /// <summary>
        /// Add or remove a Show(Series) to/from the users trakt.tv library
        /// </summary>
        /// <param name="show">The show to remove</param>
        /// <param name="traktUser">The user who's library is being updated</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="eventType"></param>
        /// <returns>Task{TraktResponseDataContract}.</returns>
        public async Task <TraktSyncResponse> SendLibraryUpdateAsync(Series show, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType)
        {
            if (show == null)
            {
                throw new ArgumentNullException("show");
            }
            if (traktUser == null)
            {
                throw new ArgumentNullException("traktUser");
            }

            if (eventType == EventType.Update)
            {
                return(null);
            }

            var showPayload = new List <TraktShowCollected>
            {
                new TraktShowCollected
                {
                    title = show.Name,
                    year  = show.ProductionYear,
                    ids   = new TraktShowId
                    {
                        tvdb   = show.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(),
                        imdb   = show.GetProviderId(MetadataProviders.Imdb),
                        tvrage = show.GetProviderId(MetadataProviders.TvRage).ConvertToInt()
                    },
                }
            };

            var data = new TraktSyncCollected
            {
                shows = showPayload.ToList()
            };

            var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;

            using (var response = await PostToTrakt(url, data, cancellationToken, traktUser).ConfigureAwait(false))
            {
                return(_jsonSerializer.DeserializeFromStream <TraktSyncResponse>(response));
            }
        }
Пример #5
0
 private async Task SetRequestHeaders(HttpRequestOptions options, TraktUser traktUser)
 {
     options.RequestHeaders.Add("trakt-api-version", "2");
     options.RequestHeaders.Add("trakt-api-key", TraktUris.Id);
     if (traktUser != null)
     {
         if (DateTime.Now > traktUser.AccessTokenExpiration)
         {
             traktUser.AccessToken = "";
         }
         if (string.IsNullOrEmpty(traktUser.AccessToken) || !string.IsNullOrEmpty(traktUser.PIN))
         {
             await RefreshUserAuth(traktUser);
         }
         if (!string.IsNullOrEmpty(traktUser.AccessToken))
         {
             options.RequestHeaders.Add("Authorization", "Bearer " + traktUser.AccessToken);
         }
     }
 }
    /// <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[]
Пример #7
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);
            }
        }
Пример #9
0
        public async Task RefreshUserAuth(TraktUser traktUser)
        {
            var data = new TraktUserTokenRequest
            {
                client_id     = TraktUris.Id,
                client_secret = TraktUris.Secret,
                redirect_uri  = "urn:ietf:wg:oauth:2.0:oob"
            };

            if (!string.IsNullOrWhiteSpace(traktUser.PIN))
            {
                data.code       = traktUser.PIN;
                data.grant_type = "authorization_code";
            }
            else if (!string.IsNullOrWhiteSpace(traktUser.RefreshToken))
            {
                data.code       = traktUser.RefreshToken;
                data.grant_type = "refresh_token";
            }
            else
            {
                _logger.Error("Tried to reauthenticate with Trakt, but neither PIN nor refreshToken was available");
            }

            TraktUserToken userToken;

            using (var response = await PostToTrakt(TraktUris.Token, data, null))
            {
                userToken = _jsonSerializer.DeserializeFromStream <TraktUserToken>(response);
            }

            if (userToken != null)
            {
                traktUser.AccessToken           = userToken.access_token;
                traktUser.RefreshToken          = userToken.refresh_token;
                traktUser.PIN                   = null;
                traktUser.AccessTokenExpiration = DateTime.Now.AddMonths(2);
                Plugin.Instance.SaveConfiguration();
            }
        }
Пример #10
0
        /// <summary>
        /// Delete all media from a users trakt.tv library, including watch history and then add all media that's stored
        /// in the MB server library to the trakt.tv library.
        /// Intended to be used to clean a users library. THIS IS A DESTRUCTIVE EVENT.
        /// </summary>
        /// <param name="traktUser"></param>
        /// <returns></returns>
        public async Task ResetTraktTvLibrary(TraktUser traktUser)
        {
            // Get a list of all the media in a users library
            var allMovies = await SendGetAllMoviesRequest(traktUser).ConfigureAwait(false);

            var allShows = await SendGetCollectionShowsRequest(traktUser).ConfigureAwait(false);

            // then delete them all

            if (allMovies != null && allMovies.Any())
            {
            }

            if (allShows != null && allShows.Any())
            {
                foreach (var show in allShows)
                {
                    var data = new
                    {
                        username = traktUser.UserName,
                        password = traktUser.PasswordHash,
                        tvdb_id  = show.TvdbId,
                        title    = show.Title,
                        year     = show.Year
                    };

                    var options = new HttpRequestOptions
                    {
                        RequestContent    = _jsonSerializer.SerializeToString(data),
                        ResourcePool      = Plugin.Instance.TraktResourcePool,
                        CancellationToken = CancellationToken.None,
                        Url = TraktUris.ShowUnLibrary
                    };

                    await _httpClient.Post(options).ConfigureAwait(false);
                }
            }

            // How to manually run the 'SyncLibraryTask' so that we add back a 'clean' library?
        }
Пример #11
0
        private async Task SetRequestHeaders(HttpRequestOptions options, TraktUser traktUser)
        {
            options.RequestHeaders.Add("trakt-api-version", "2");
            options.RequestHeaders.Add("trakt-api-key", TraktUris.Devkey);
            if (traktUser != null)
            {
                if (string.IsNullOrEmpty(traktUser.UserToken))
                {
                    var userToken = await GetUserToken(traktUser);

                    if (userToken != null)
                    {
                        traktUser.UserToken = userToken.Token;
                    }
                }
                if (!string.IsNullOrEmpty(traktUser.UserToken))
                {
                    options.RequestHeaders.Add("trakt-user-login", traktUser.UserName);
                    options.RequestHeaders.Add("trakt-user-token", traktUser.UserToken);
                }
            }
        }
Пример #12
0
        internal static void FollowUser(TraktUser user)
        {
            Thread followUserThread = new Thread(delegate(object obj)
            {
                var currUser = obj as TraktUser;

                var response = TraktAPI.TraktAPI.NetworkFollow(CreateNetworkData(currUser));
                TraktLogger.LogTraktResponse <TraktNetworkFollowResponse>(response);

                // notify user if follow is pending approval by user
                if (response.Pending)
                {
                    GUIUtils.ShowNotifyDialog(Translation.Follow, string.Format(Translation.FollowPendingApproval, currUser.Username));
                }
            })
            {
                IsBackground = true,
                Name         = "FollowUser"
            };

            followUserThread.Start(user);
        }
Пример #13
0
    private async Task <Stream> GetFromTrakt(string url, TraktUser traktUser, CancellationToken cancellationToken)
    {
        var httpClient = GetHttpClient();

        if (traktUser != null)
        {
            await SetRequestHeaders(httpClient, traktUser).ConfigureAwait(false);
        }

        await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);

        try
        {
            var response = await RetryHttpRequest(async() => await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);

            return(await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false));
        }
        finally
        {
            _traktResourcePool.Release();
        }
    }
Пример #14
0
        /// <summary>
        /// Checks whether it's possible/allowed to sync a <see cref="BaseItem"/> for a <see cref="TraktUser"/>.
        /// </summary>
        /// <param name="item">
        /// Item to check.
        /// </param>
        /// <param name="traktUser">
        /// The trakt user to check for.
        /// </param>
        /// <returns>
        /// <see cref="bool"/> indicates if it's possible/allowed to sync this item.
        /// </returns>
        public bool CanSync(BaseItem item, TraktUser traktUser)
        {
            if (item.Path == null || item.LocationType == LocationType.Virtual)
            {
                return false;
            }

            if (traktUser.LocationsExcluded != null && traktUser.LocationsExcluded.Any(s => _fileSystem.ContainsSubPath(s, item.Path)))
            {
                return false;
            }

            if (item is Movie movie)
            {
                return !string.IsNullOrEmpty(movie.GetProviderId(MetadataProviders.Imdb)) ||
                    !string.IsNullOrEmpty(movie.GetProviderId(MetadataProviders.Tmdb));
            }

            if (item is Episode episode && episode.Series != null && !episode.IsMissingEpisode && (episode.IndexNumber.HasValue || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProviders.Tvdb))))
            {
                var series = episode.Series;

                return !string.IsNullOrEmpty(series.GetProviderId(MetadataProviders.Imdb)) ||
                    !string.IsNullOrEmpty(series.GetProviderId(MetadataProviders.Tvdb));
            }

            if (item is Series show)
            {

                return !string.IsNullOrEmpty(show.GetProviderId(MetadataProviders.Imdb)) ||
                       !string.IsNullOrEmpty(show.GetProviderId(MetadataProviders.Tvdb)) ||
                       !string.IsNullOrEmpty(show.GetProviderId(MetadataProviders.Tmdb)) ||
                       !string.IsNullOrEmpty(show.GetProviderId(MetadataProviders.TvRage));
            }


            return false;
        }
Пример #15
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);
            }
        }
Пример #16
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);
            }
        }
        internal static void FollowUser(TraktUser user)
        {
            var followUserThread = new Thread(obj =>
            {
                var currUser = obj as TraktUser;

                var response = TraktAPI.TraktAPI.NetworkFollowUser(currUser.Username);
                TraktLogger.LogTraktResponse <TraktNetworkApproval>(response);

                // notify user if follow is pending approval by user
                // approved date will be null if user is marked as private
                if (response != null && response.ApprovedAt == null)
                {
                    GUIUtils.ShowNotifyDialog(Translation.Follow, string.Format(Translation.FollowPendingApproval, currUser.Username));
                }
            })
            {
                IsBackground = true,
                Name         = "FollowUser"
            };

            followUserThread.Start(user);
        }
Пример #18
0
        /// <summary>
        ///     Posts data to url, authenticating with <see cref="TraktUser"/>.
        /// </summary>
        /// <param name="traktUser">If null, authentication headers not added.</param>
        private async Task <Stream> PostToTrakt(
            string url,
            object data,
            CancellationToken cancellationToken,
            TraktUser traktUser)
        {
            var requestContent = data == null ? string.Empty : _jsonSerializer.SerializeToString(data);

            if (traktUser != null && traktUser.ExtraLogging)
            {
                _logger.LogDebug(requestContent);
            }

            var options = GetHttpRequestOptions();

            options.Url = url;
            options.CancellationToken = cancellationToken;
            options.RequestContent    = requestContent;

            if (traktUser != null)
            {
                await SetRequestHeaders(options, traktUser).ConfigureAwait(false);
            }

            await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                var retryResponse = await Retry(async() => await _httpClient.Post(options).ConfigureAwait(false)).ConfigureAwait(false);

                return(retryResponse.Content);
            }
            finally
            {
                _traktResourcePool.Release();
            }
        }
Пример #19
0
    /// <summary>
    ///     Posts data to url, authenticating with <see cref="TraktUser"/>.
    /// </summary>
    /// <param name="traktUser">If null, authentication headers not added.</param>
    private async Task <Stream> PostToTrakt(
        string url,
        object data,
        TraktUser traktUser,
        CancellationToken cancellationToken)
    {
        if (traktUser != null && traktUser.ExtraLogging)
        {
            _logger.LogDebug("{@JsonData}", data);
        }

        var httpClient = GetHttpClient();

        if (traktUser != null)
        {
            await SetRequestHeaders(httpClient, traktUser).ConfigureAwait(false);
        }

        var bytes   = JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions);
        var content = new ByteArrayContent(bytes);

        content.Headers.Add(HeaderNames.ContentType, MediaTypeNames.Application.Json);

        await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);

        try
        {
            var response = await RetryHttpRequest(async() => await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);

            return(await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false));
        }
        finally
        {
            _traktResourcePool.Release();
        }
    }
Пример #20
0
        /// <summary>
        /// Send a list of episodes to trakt.tv that have been marked watched or unwatched
        /// </summary>
        /// <param name="episodes">The list of episodes to send</param>
        /// <param name="traktUser">The trakt user profile that is being updated</param>
        /// <param name="seen">True if episodes are being marked seen, false otherwise</param>
        /// <param name="cancellationToken">The Cancellation Token</param>
        /// <returns></returns>
        public async Task <List <TraktResponseDataContract> > SendEpisodePlaystateUpdates(List <Episode> episodes, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
        {
            if (episodes == null)
            {
                throw new ArgumentNullException("episodes");
            }

            if (traktUser == null)
            {
                throw new ArgumentNullException("traktUser");
            }

            var episodesPayload = episodes.Select(ep => new
            {
                season  = ep.ParentIndexNumber,
                episode = ep.IndexNumber
            }).Cast <object>().ToList();

            var traktResponses = new List <TraktResponseDataContract>();

            var payloadParcel = new List <object>();

            foreach (var episode in episodesPayload)
            {
                payloadParcel.Add(episode);

                if (payloadParcel.Count == 100)
                {
                    var response = await
                                   SendEpisodePlaystateUpdatesInternalAsync(payloadParcel, episodes[0].Series, traktUser, seen,
                                                                            cancellationToken);

                    if (response != null)
                    {
                        traktResponses.Add(response);
                    }

                    payloadParcel.Clear();
                }
            }

            if (payloadParcel.Count > 0)
            {
                var response = await
                               SendEpisodePlaystateUpdatesInternalAsync(payloadParcel, episodes[0].Series, traktUser, seen,
                                                                        cancellationToken);

                if (response != null)
                {
                    traktResponses.Add(response);
                }
            }

            return(traktResponses);
        }
Пример #21
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);
        }
        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);
            }
        }
Пример #23
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);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="events"></param>
        /// <param name="traktUser"></param>
        /// <param name="eventType"></param>
        /// <returns></returns>
        private async Task ProcessQueuedMovieEvents(IEnumerable <LibraryEvent> events, TraktUser traktUser, EventType eventType)
        {
            var movies = events.Select(lev => (Movie)lev.Item)
                         .Where(lev => !string.IsNullOrEmpty(lev.Name) && !string.IsNullOrEmpty(lev.GetProviderId(MetadataProviders.Imdb)))
                         .ToList();

            try
            {
                await _traktApi.SendLibraryUpdateAsync(movies, traktUser, CancellationToken.None, eventType).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _logger.LogError("Exception handled processing queued movie events", ex);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="events"></param>
        /// <param name="traktUser"></param>
        /// <param name="eventType"></param>
        /// <returns></returns>
        private async Task ProcessQueuedEpisodeEvents(IEnumerable <LibraryEvent> events, TraktUser traktUser, EventType eventType)
        {
            var episodes = events.Select(lev => (Episode)lev.Item)
                           .Where(lev => lev.Series != null && (!string.IsNullOrEmpty(lev.Series.Name) && !string.IsNullOrEmpty(lev.Series.GetProviderId(MetadataProviders.Tvdb))))
                           .OrderBy(i => i.Series.Id)
                           .ToList();

            // Can't progress further without episodes
            if (!episodes.Any())
            {
                _logger.LogInformation("episodes count is 0");

                return;
            }

            var payload         = new List <Episode>();
            var currentSeriesId = episodes[0].Series.Id;

            foreach (var ep in episodes)
            {
                if (!currentSeriesId.Equals(ep.Series.Id))
                {
                    // We're starting a new series. Time to send the current one to trakt.tv
                    await _traktApi.SendLibraryUpdateAsync(payload, traktUser, CancellationToken.None, eventType);

                    currentSeriesId = ep.Series.Id;
                    payload.Clear();
                }

                payload.Add(ep);
            }

            if (payload.Any())
            {
                try
                {
                    await _traktApi.SendLibraryUpdateAsync(payload, traktUser, CancellationToken.None, eventType);
                }
                catch (Exception ex)
                {
                    _logger.LogError("Exception handled processing queued episode events", ex);
                }
            }
        }
        private async Task ProcessQueuedShowEvents(IEnumerable <LibraryEvent> events, TraktUser traktUser, EventType eventType)
        {
            var shows = events.Select(lev => (Series)lev.Item)
                        .Where(lev => !string.IsNullOrEmpty(lev.Name) && !string.IsNullOrEmpty(lev.GetProviderId(MetadataProviders.Tvdb)))
                        .ToList();

            try
            {
                // Should probably not be awaiting this, but it's unlikely a user will be deleting more than one or two shows at a time
                foreach (var show in shows)
                {
                    await _traktApi.SendLibraryUpdateAsync(show, traktUser, CancellationToken.None, eventType).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Exception handled processing queued series events", ex);
            }
        }
Пример #27
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="userDataSaveEventArgs"></param>
        /// <param name="traktUser"></param>
        public async Task ProcessUserDataSaveEventArgs(UserDataSaveEventArgs userDataSaveEventArgs, TraktUser traktUser, CancellationToken cancellationToken)
        {
            var userPackage = _userDataPackages.FirstOrDefault(e => e.TraktUser.Equals(traktUser));

            if (userPackage == null)
            {
                userPackage = new UserDataPackage {
                    TraktUser = traktUser
                };
                _userDataPackages.Add(userPackage);
            }


            if (_timer == null)
            {
                _timer = new Timer(OnTimerCallback, null, TimeSpan.FromMilliseconds(5000),
                                   Timeout.InfiniteTimeSpan);
            }
            else
            {
                _timer.Change(TimeSpan.FromMilliseconds(5000), Timeout.InfiniteTimeSpan);
            }

            var movie = userDataSaveEventArgs.Item as Movie;

            if (movie != null)
            {
                if (userDataSaveEventArgs.UserData.Played)
                {
                    userPackage.SeenMovies.Add(movie);

                    if (userPackage.SeenMovies.Count >= 100)
                    {
                        await _traktApi.SendMoviePlaystateUpdates(userPackage.SeenMovies, userPackage.TraktUser, true, true,
                                                                  cancellationToken).ConfigureAwait(false);

                        userPackage.SeenMovies = new List <Movie>();
                    }

                    await MovieStatusUpdate(movie, userPackage.TraktUser, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    userPackage.UnSeenMovies.Add(movie);

                    if (userPackage.UnSeenMovies.Count >= 100)
                    {
                        await _traktApi.SendMoviePlaystateUpdates(userPackage.UnSeenMovies, userPackage.TraktUser, true, false,
                                                                  cancellationToken).ConfigureAwait(false);

                        userPackage.UnSeenMovies = new List <Movie>();
                    }
                }

                return;
            }

            var episode = userDataSaveEventArgs.Item as Episode;

            if (episode == null)
            {
                return;
            }

            // If it's not the series we're currently storing, upload our episodes and reset the arrays
            if (!userPackage.CurrentSeriesId.Equals(episode.Series.Id))
            {
                if (userPackage.SeenEpisodes.Any())
                {
                    await _traktApi.SendEpisodePlaystateUpdates(userPackage.SeenEpisodes, userPackage.TraktUser, true, true,
                                                                cancellationToken).ConfigureAwait(false);

                    userPackage.SeenEpisodes = new List <Episode>();
                }

                if (userPackage.UnSeenEpisodes.Any())
                {
                    await _traktApi.SendEpisodePlaystateUpdates(userPackage.UnSeenEpisodes, userPackage.TraktUser, true, false,
                                                                cancellationToken).ConfigureAwait(false);

                    userPackage.UnSeenEpisodes = new List <Episode>();
                }

                userPackage.CurrentSeriesId = episode.Series.Id;
            }

            if (userDataSaveEventArgs.UserData.Played)
            {
                userPackage.SeenEpisodes.Add(episode);

                await EpisodeStatusUpdate(episode, traktUser, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                userPackage.UnSeenEpisodes.Add(episode);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="userDataSaveEventArgs"></param>
        /// <param name="traktUser"></param>
        public void ProcessUserDataSaveEventArgs(UserDataSaveEventArgs userDataSaveEventArgs, TraktUser traktUser)
        {
            var userPackage = _userDataPackages.FirstOrDefault(e => e.TraktUser.Equals(traktUser));

            if (userPackage == null)
            {
                userPackage = new UserDataPackage {
                    TraktUser = traktUser
                };
                _userDataPackages.Add(userPackage);
            }


            if (_timer == null)
            {
                _timer          = new Timer(5000);
                _timer.Elapsed += TimerElapsed;
            }

            if (_timer.Enabled)
            {
                _timer.Stop();
                _timer.Start();
            }
            else
            {
                _timer.Start();
            }

            var movie = userDataSaveEventArgs.Item as Movie;

            if (movie != null)
            {
                if (userDataSaveEventArgs.UserData.Played)
                {
                    userPackage.SeenMovies.Add(movie);

                    if (userPackage.SeenMovies.Count >= 100)
                    {
                        _traktApi.SendMoviePlaystateUpdates(userPackage.SeenMovies, userPackage.TraktUser, true,
                                                            CancellationToken.None).ConfigureAwait(false);
                        userPackage.SeenMovies = new List <Movie>();
                    }
                }
                else
                {
                    userPackage.UnSeenMovies.Add(movie);

                    if (userPackage.UnSeenMovies.Count >= 100)
                    {
                        _traktApi.SendMoviePlaystateUpdates(userPackage.UnSeenMovies, userPackage.TraktUser, false,
                                                            CancellationToken.None).ConfigureAwait(false);
                        userPackage.UnSeenMovies = new List <Movie>();
                    }
                }

                return;
            }

            var episode = userDataSaveEventArgs.Item as Episode;

            if (episode == null)
            {
                return;
            }

            // If it's not the series we're currently storing, upload our episodes and reset the arrays
            if (!userPackage.CurrentSeriesId.Equals(episode.Series.Id))
            {
                if (userPackage.SeenEpisodes.Any())
                {
                    _traktApi.SendEpisodePlaystateUpdates(userPackage.SeenEpisodes, userPackage.TraktUser, true,
                                                          CancellationToken.None).ConfigureAwait(false);
                    userPackage.SeenEpisodes = new List <Episode>();
                }

                if (userPackage.UnSeenEpisodes.Any())
                {
                    _traktApi.SendEpisodePlaystateUpdates(userPackage.UnSeenEpisodes, userPackage.TraktUser, false,
                                                          CancellationToken.None).ConfigureAwait(false);
                    userPackage.SeenEpisodes = new List <Episode>();
                }

                userPackage.CurrentSeriesId = episode.Series.Id;
            }

            if (userDataSaveEventArgs.UserData.Played)
            {
                userPackage.SeenEpisodes.Add(episode);
            }
            else
            {
                userPackage.UnSeenEpisodes.Add(episode);
            }
        }
Пример #29
0
        /// <summary>
        /// Report to trakt.tv that a movie is being watched, or has been watched.
        /// </summary>
        /// <param name="movie">The movie being watched/scrobbled</param>
        /// <param name="mediaStatus">MediaStatus enum dictating whether item is being watched or scrobbled</param>
        /// <param name="traktUser">The user that watching the current movie</param>
        /// <returns>A standard TraktResponse Data Contract</returns>
        public async Task <TraktResponseDataContract> SendMovieStatusUpdateAsync(Movie movie, MediaStatus mediaStatus, TraktUser traktUser)
        {
            var data = new Dictionary <string, string>
            {
                { "username", traktUser.UserName },
                { "password", traktUser.PasswordHash },
                { "imdb_id", movie.GetProviderId(MetadataProviders.Imdb) }
            };

            if (movie.ProviderIds != null && movie.ProviderIds.ContainsKey("Tmdb"))
            {
                data.Add("tmdb_id", movie.ProviderIds["Tmdb"]);
            }

            data.Add("title", movie.Name);
            data.Add("year", movie.ProductionYear != null ? movie.ProductionYear.ToString() : "");
            data.Add("duration", movie.RunTimeTicks != null ? ((int)((movie.RunTimeTicks / 10000000) / 60)).ToString(CultureInfo.InvariantCulture) : "");


            Stream response = null;

            if (mediaStatus == MediaStatus.Watching)
            {
                response = await _httpClient.Post(TraktUris.MovieWatching, data, Plugin.Instance.TraktResourcePool, CancellationToken.None).ConfigureAwait(false);
            }
            else if (mediaStatus == MediaStatus.Scrobble)
            {
                response = await _httpClient.Post(TraktUris.MovieScrobble, data, Plugin.Instance.TraktResourcePool, CancellationToken.None).ConfigureAwait(false);
            }

            return(_jsonSerializer.DeserializeFromStream <TraktResponseDataContract>(response));
        }
Пример #30
0
        private async Task <TraktResponseDataContract> SendEpisodePlaystateUpdatesInternalAsync(List <object> episodesPayload, Series series, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
        {
            var data = new
            {
                username = traktUser.UserName,
                password = traktUser.PasswordHash,
                imdb_id  = series.GetProviderId(MetadataProviders.Imdb),
                tvdb_id  = series.GetProviderId(MetadataProviders.Tvdb),
                title    = series.Name,
                year     = (series.ProductionYear ?? 0).ToString(CultureInfo.InvariantCulture),
                episodes = episodesPayload
            };

            var options = new HttpRequestOptions
            {
                RequestContent    = _jsonSerializer.SerializeToString(data),
                ResourcePool      = Plugin.Instance.TraktResourcePool,
                CancellationToken = cancellationToken,
                Url = seen ? TraktUris.ShowEpisodeSeen : TraktUris.ShowEpisodeUnSeen
            };

            var response = await _httpClient.Post(options).ConfigureAwait(false);

            return(_jsonSerializer.DeserializeFromStream <TraktResponseDataContract>(response.Content));
        }