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; } var movie = item as Movie; if (movie != null) { return !string.IsNullOrEmpty(movie.GetProviderId(MetadataProviders.Imdb)) || !string.IsNullOrEmpty(movie.GetProviderId(MetadataProviders.Tmdb)); } var episode = item as Episode; if (episode != null && episode.Series != null && !episode.IsVirtualUnaired && !episode.IsMissingEpisode) { var series = episode.Series; return !string.IsNullOrEmpty(series.GetProviderId(MetadataProviders.Imdb)) || !string.IsNullOrEmpty(series.GetProviderId(MetadataProviders.Tvdb)); } return false; }
/// <summary> /// Return a list of the users friends /// </summary> /// <param name="traktUser">The user who's friends you want to retrieve</param> /// <returns>A TraktFriendDataContract</returns> public async Task<TraktFriendDataContract> GetUserFriends(TraktUser traktUser) { var data = new Dictionary<string, string> { { "username", traktUser.UserName }, { "password", traktUser.PasswordHash } }; var response = await _httpClient.Post(string.Format(TraktUris.Friends, traktUser.UserName), data, Plugin.Instance.TraktResourcePool, CancellationToken.None).ConfigureAwait(false); return _jsonSerializer.DeserializeFromStream<TraktFriendDataContract>(response); }
/// <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.Info("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.ErrorException("Exception handled processing queued episode events", ex); } } }
public PluginConfiguration() { TraktUsers = new TraktUser[] {}; }
/// <summary> /// Rate an item /// </summary> /// <param name="item"></param> /// <param name="rating"></param> /// <param name="traktUser"></param> /// <returns></returns> public async Task<TraktSyncResponse> SendItemRating(BaseItem item, int rating, TraktUser traktUser) { object data = new {}; if (item is Movie) { data = new { movies = new[] { new TraktMovieRated { Title = item.Name, Year = item.ProductionYear, Ids = new TraktMovieId { Imdb = item.GetProviderId(MetadataProviders.Imdb), Tmdb = item.GetProviderId(MetadataProviders.Tmdb).ConvertToInt() }, Rating = rating } } }; } else if (item is Episode ) { var episode = item as Episode; if (string.IsNullOrEmpty(episode.GetProviderId(MetadataProviders.Tvdb))) { if (episode.IndexNumber.HasValue) { var show = new TraktShowRated { Ids = new TraktShowId { Tvdb = episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(), Imdb = episode.Series.GetProviderId(MetadataProviders.Imdb), TvRage = episode.Series.GetProviderId(MetadataProviders.TvRage).ConvertToInt() }, Seasons = new List<TraktShowRated.TraktSeasonRated> { new TraktShowRated.TraktSeasonRated { Number = episode.GetSeasonNumber(), Episodes = new List<TraktEpisodeRated> { new TraktEpisodeRated { Number = episode.IndexNumber.Value, Rating = rating } } } } }; data = new { shows = new[] { show } }; } } else { data = new { episodes = new[] { new TraktEpisodeRated { Rating = rating, Ids = new TraktEpisodeId { Tvdb = episode.GetProviderId(MetadataProviders.Tvdb).ConvertToInt() } } } }; } } else // It's a Series { data = new { shows = new[] { new TraktShowRated { Rating = rating, Title = item.Name, Year = item.ProductionYear, Ids = new TraktShowId { Imdb = item.GetProviderId(MetadataProviders.Imdb), Tvdb = item.GetProviderId(MetadataProviders.Tvdb).ConvertToInt() } } } }; } var response = await PostToTrakt(TraktUris.SyncRatingsAdd, data, traktUser); return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response); }
private async Task<Stream> GetFromTrakt(string url, TraktUser traktUser) { return await GetFromTrakt(url, CancellationToken.None, traktUser); }
private async Task<Stream> PostToTrakt(string url, object data, CancellationToken cancellationToken, TraktUser traktUser) { var requestContent = data.ToJSON(); var tries = 0; while (tries < 3) { try { var options = new HttpRequestOptions { Url = url, ResourcePool = Plugin.Instance.TraktResourcePool, CancellationToken = cancellationToken, RequestContentType = "application/json", RequestContent = requestContent, TimeoutMs = 120000, LogErrorResponseBody = false, LogRequest = true }; await SetRequestHeaders(options, traktUser); var response = await _httpClient.Post(options).ConfigureAwait(false); return response.Content; } catch (Exception) { tries ++; Thread.Sleep(10000); if (tries >= 3) { throw; } } } return null; }
/// <summary> /// Send a list of movies to trakt.tv that have been marked watched or unwatched /// </summary> /// <param name="movies">The list of movies to send</param> /// <param name="traktUser">The trakt user profile that is being updated</param> /// <param name="seen">True if movies are being marked seen, false otherwise</param> /// <param name="cancellationToken">The Cancellation Token</param> /// <returns></returns> public async Task<List<TraktSyncResponse>> SendMoviePlaystateUpdates(List<Movie> movies, TraktUser traktUser, bool seen, CancellationToken cancellationToken) { if (movies == null) throw new ArgumentNullException("movies"); if (traktUser == null) throw new ArgumentNullException("traktUser"); var moviesPayload = movies.Select(m => { var lastPlayedDate = seen ? _userDataManager.GetUserData(new Guid(traktUser.LinkedMbUserId), m.GetUserDataKey()).LastPlayedDate : null; return new TraktMovieWatched { Title = m.Name, Ids = new TraktMovieId { Imdb = m.GetProviderId(MetadataProviders.Imdb), Tmdb = string.IsNullOrEmpty(m.GetProviderId(MetadataProviders.Tmdb)) ? (int?) null : int.Parse(m.GetProviderId(MetadataProviders.Tmdb)) }, Year = m.ProductionYear, WatchedAt = lastPlayedDate.HasValue ? lastPlayedDate.Value.ToISO8601() : null }; }).ToList(); var chunks = moviesPayload.ToChunks(100).ToList(); var traktResponses = new List<TraktSyncResponse>(); foreach (var chunk in chunks) { var data = new TraktSyncWatched { Movies = chunk.ToList() }; var url = seen ? TraktUris.SyncWatchedHistoryAdd : TraktUris.SyncWatchedHistoryRemove; var response = await PostToTrakt(url, data, cancellationToken, traktUser); if (response != null) traktResponses.Add(_jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response)); } return traktResponses; }
private async Task<TraktSyncResponse> SendEpisodePlaystateUpdatesInternalAsync(IEnumerable<Episode> episodeChunk, TraktUser traktUser, bool seen, CancellationToken cancellationToken) { var data = new TraktSyncWatched{ Episodes = new List<TraktEpisodeWatched>(), Shows = new List<TraktShowWatched>() }; foreach (var episode in episodeChunk) { var tvDbId = episode.GetProviderId(MetadataProviders.Tvdb); var lastPlayedDate = seen ? _userDataManager.GetUserData(new Guid(traktUser.LinkedMbUserId), episode.GetUserDataKey()) .LastPlayedDate : null; if (!string.IsNullOrEmpty(tvDbId) && (!episode.IndexNumberEnd.HasValue || episode.IndexNumberEnd == episode.IndexNumber)) { data.Episodes.Add(new TraktEpisodeWatched { Ids = new TraktEpisodeId { Tvdb = int.Parse(tvDbId) }, WatchedAt = lastPlayedDate.HasValue ? lastPlayedDate.Value.ToISO8601() : null }); } else if (episode.IndexNumber.HasValue) { var syncShow = data.Shows.FirstOrDefault(sre => sre.Ids != null && sre.Ids.Tvdb == episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt()); if (syncShow == null) { syncShow = new TraktShowWatched { Ids = new TraktShowId { Tvdb = episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(), Imdb = episode.Series.GetProviderId(MetadataProviders.Imdb), TvRage = episode.Series.GetProviderId(MetadataProviders.TvRage).ConvertToInt() }, Seasons = new List<TraktSeasonWatched>() }; data.Shows.Add(syncShow); } var syncSeason = syncShow.Seasons.FirstOrDefault(ss => ss.Number == episode.GetSeasonNumber()); if(syncSeason == null) { syncSeason = new TraktSeasonWatched { Number = episode.GetSeasonNumber(), Episodes = new List<TraktEpisodeWatched>() }; syncShow.Seasons.Add(syncSeason); } syncSeason.Episodes.AddRange(Enumerable.Range(episode.IndexNumber.Value, ((episode.IndexNumberEnd ?? episode.IndexNumber).Value - episode.IndexNumber.Value) + 1) .Select(number => new TraktEpisodeWatched { Number = number, WatchedAt = lastPlayedDate.HasValue ? lastPlayedDate.Value.ToISO8601() : null }) .ToList()); } } var url = seen ? TraktUris.SyncWatchedHistoryAdd : TraktUris.SyncWatchedHistoryRemove; var response = await PostToTrakt(url,data, cancellationToken, traktUser); return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response); }
/// <summary> /// /// </summary> /// <param name="item"></param> /// <param name="comment"></param> /// <param name="containsSpoilers"></param> /// <param name="traktUser"></param> /// <param name="isReview"></param> /// <returns></returns> public async Task<object> SendItemComment(BaseItem item, string comment, bool containsSpoilers, TraktUser traktUser, bool isReview = false) { return null; //TODO: This functionallity is not available yet // string url; // var data = new Dictionary<string, string> // { // {"username", traktUser.UserName}, // {"password", traktUser.Password} // }; // // if (item is Movie) // { // if (item.ProviderIds != null && item.ProviderIds.ContainsKey("Imdb")) // data.Add("imdb_id", item.ProviderIds["Imdb"]); // // data.Add("title", item.Name); // data.Add("year", item.ProductionYear != null ? item.ProductionYear.ToString() : ""); // url = TraktUris.CommentMovie; // } // else // { // var episode = item as Episode; // if (episode != null) // { // if (episode.Series.ProviderIds != null) // { // if (episode.Series.ProviderIds.ContainsKey("Imdb")) // data.Add("imdb_id", episode.Series.ProviderIds["Imdb"]); // // if (episode.Series.ProviderIds.ContainsKey("Tvdb")) // data.Add("tvdb_id", episode.Series.ProviderIds["Tvdb"]); // } // // data.Add("season", episode.AiredSeasonNumber.ToString()); // data.Add("episode", episode.IndexNumber.ToString()); // url = TraktUris.CommentEpisode; // } // else // It's a Series // { // data.Add("title", item.Name); // data.Add("year", item.ProductionYear != null ? item.ProductionYear.ToString() : ""); // // if (item.ProviderIds != null) // { // if (item.ProviderIds.ContainsKey("Imdb")) // data.Add("imdb_id", item.ProviderIds["Imdb"]); // // if (item.ProviderIds.ContainsKey("Tvdb")) // data.Add("tvdb_id", item.ProviderIds["Tvdb"]); // } // // url = TraktUris.CommentShow; // } // } // // data.Add("comment", comment); // data.Add("spoiler", containsSpoilers.ToString()); // data.Add("review", isReview.ToString()); // // Stream response = // await // _httpClient.Post(url, data, Plugin.Instance.TraktResourcePool, // CancellationToken.None).ConfigureAwait(false); // // return _jsonSerializer.DeserializeFromStream<TraktResponseDataContract>(response); }
/// <summary> /// /// </summary> /// <param name="traktUser"></param> /// <returns></returns> public async Task<List<DataContracts.Users.Watched.TraktMovieWatched>> SendGetAllWatchedMoviesRequest(TraktUser traktUser) { var response = await GetFromTrakt(TraktUris.WatchedMovies, traktUser); return _jsonSerializer.DeserializeFromStream<List<DataContracts.Users.Watched.TraktMovieWatched>>(response); }
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); } catch (Exception ex) { _logger.ErrorException("Exception handled processing queued series events", ex); } }
/// <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); } catch (Exception ex) { _logger.ErrorException("Exception handled processing queued movie events", ex); } }
private async Task<Stream> PostToTrakt(string url, object data, CancellationToken cancellationToken, TraktUser traktUser) { var requestContent = data.ToJSON(); if (traktUser != null && traktUser.ExtraLogging && url != TraktUris.Login) { _logger.Debug(requestContent); } var options = new HttpRequestOptions { Url = url, ResourcePool = Plugin.Instance.TraktResourcePool, CancellationToken = cancellationToken, RequestContentType = "application/json", RequestContent = requestContent, TimeoutMs = 120000, LogErrorResponseBody = false, LogRequest = true }; await SetRequestHeaders(options, traktUser); var response = await _httpClient.Post(options).ConfigureAwait(false); return response.Content; }
private async Task<Stream> GetFromTrakt(string url, CancellationToken cancellationToken, TraktUser traktUser) { var options = new HttpRequestOptions { Url = url, ResourcePool = Plugin.Instance.TraktResourcePool, CancellationToken = cancellationToken, RequestContentType = "application/json", TimeoutMs = 120000, LogErrorResponseBody = false, LogRequest = true }; await SetRequestHeaders(options, traktUser); var response = await _httpClient.Get(options).ConfigureAwait(false); return response; }
private async Task<TraktSyncResponse> SendLibraryUpdateInternalAsync(IEnumerable<Episode> episodes, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType) { var episodesPayload = new List<TraktEpisodeCollected>(); var showPayload = new List<TraktShowCollected>(); foreach (Episode episode in episodes) { var audioStream = episode.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio); var tvDbId = episode.GetProviderId(MetadataProviders.Tvdb); if (!string.IsNullOrEmpty(tvDbId) && (!episode.IndexNumber.HasValue || !episode.IndexNumberEnd.HasValue || episode.IndexNumberEnd <= episode.IndexNumber)) { var traktEpisodeCollected = new TraktEpisodeCollected { CollectedAt = episode.DateCreated.ToISO8601(), Ids = new TraktEpisodeId { Tvdb = tvDbId.ConvertToInt() } }; if (traktUser.ExportMediaInfo) { traktEpisodeCollected.Is3D = episode.Is3D; traktEpisodeCollected.AudioChannels = audioStream.GetAudioChannels(); traktEpisodeCollected.Audio = audioStream.GetCodecRepresetation(); traktEpisodeCollected.Resolution = episode.GetDefaultVideoStream().GetResolution(); } episodesPayload.Add(traktEpisodeCollected); } else if (episode.IndexNumber.HasValue) { var indexNumber = episode.IndexNumber.Value; var finalNumber = (episode.IndexNumberEnd ?? episode.IndexNumber).Value; var syncShow = showPayload.FirstOrDefault( sre => sre.Ids != null && sre.Ids.Tvdb == episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt()); if (syncShow == null) { syncShow = new TraktShowCollected { Ids = new TraktShowId { Tvdb = episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(), Imdb = episode.Series.GetProviderId(MetadataProviders.Imdb), TvRage = episode.Series.GetProviderId(MetadataProviders.TvRage).ConvertToInt() }, Seasons = new List<TraktShowCollected.TraktSeasonCollected>() }; showPayload.Add(syncShow); } var syncSeason = syncShow.Seasons.FirstOrDefault(ss => ss.Number == episode.GetSeasonNumber()); if (syncSeason == null) { syncSeason = new TraktShowCollected.TraktSeasonCollected { Number = episode.GetSeasonNumber(), Episodes = new List<TraktEpisodeCollected>() }; syncShow.Seasons.Add(syncSeason); } for (var number = indexNumber; number <= finalNumber; number++) { var traktEpisodeCollected = new TraktEpisodeCollected { Number = number, CollectedAt = episode.DateCreated.ToISO8601(), Ids = new TraktEpisodeId { Tvdb = tvDbId.ConvertToInt() } }; if (traktUser.ExportMediaInfo) { traktEpisodeCollected.Is3D = episode.Is3D; traktEpisodeCollected.AudioChannels = audioStream.GetAudioChannels(); traktEpisodeCollected.Audio = audioStream.GetCodecRepresetation(); traktEpisodeCollected.Resolution = episode.GetDefaultVideoStream().GetResolution(); } syncSeason.Episodes.Add(traktEpisodeCollected); } } } var data = new TraktSyncCollected { Episodes = episodesPayload.ToList(), Shows = showPayload.ToList() }; var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove; var response = await PostToTrakt(url, data, cancellationToken, traktUser); return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response); }
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() || (traktUser.ExportMediaInfo && 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); } }
/// <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 >= 300) { _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 >= 300) { _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); } }
/// <summary> /// /// </summary> /// <param name="traktUser"></param> /// <returns></returns> public async Task<List<TraktShow>> SendShowRecommendationsRequest(TraktUser traktUser) { var response = await GetFromTrakt(TraktUris.RecommendationsShows, traktUser); return _jsonSerializer.DeserializeFromStream<List<TraktShow>>(response); }
// /// <summary> // /// Return information about the user, including ratings format // /// </summary> // /// <param name="traktUser"></param> // /// <returns></returns> // public async Task<AccountSettingsDataContract> GetUserAccount(TraktUser traktUser) // { // var data = new Dictionary<string, string> { { "username", traktUser.UserName }, { "password", traktUser.Password } }; // // var response = // await // _httpClient.Post(TraktUris.AccountSettings, data, Plugin.Instance.TraktResourcePool, // CancellationToken.None).ConfigureAwait(false); // // return _jsonSerializer.DeserializeFromStream<AccountSettingsDataContract>(response); // } // // // // /// <summary> // /// Return a list of the users friends // /// </summary> // /// <param name="traktUser">The user who's friends you want to retrieve</param> // /// <returns>A TraktFriendDataContract</returns> // public async Task<TraktFriendDataContract> GetUserFriends(TraktUser traktUser) // { // var data = new Dictionary<string, string> { { "username", traktUser.UserName }, { "password", traktUser.Password } }; // // var response = await _httpClient.Post(string.Format(TraktUris.Friends, traktUser.UserName), data, Plugin.Instance.TraktResourcePool, // CancellationToken.None).ConfigureAwait(false); // // return _jsonSerializer.DeserializeFromStream<TraktFriendDataContract>(response); // // } // // // /// <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> /// <param name="progressPercent"></param> /// <returns>A standard TraktResponse Data Contract</returns> public async Task<TraktScrobbleResponse> SendMovieStatusUpdateAsync(Movie movie, MediaStatus mediaStatus, TraktUser traktUser, float progressPercent) { var movieData = new TraktScrobbleMovie { AppDate = DateTime.Today.ToString("yyyy-MM-dd"), AppVersion = _appHost.ApplicationVersion.ToString(), Progress = progressPercent, Movie = new TraktMovie { Title = movie.Name, Year = movie.ProductionYear, Ids = new TraktMovieId { Imdb = movie.GetProviderId(MetadataProviders.Imdb), Tmdb = movie.GetProviderId(MetadataProviders.Tmdb).ConvertToInt() } } }; string url; switch (mediaStatus) { case MediaStatus.Watching: url = TraktUris.ScrobbleStart; break; case MediaStatus.Paused: url = TraktUris.ScrobblePause; break; default: url = TraktUris.ScrobbleStop; break; } var response = await PostToTrakt(url, movieData, CancellationToken.None, traktUser); return _jsonSerializer.DeserializeFromStream<TraktScrobbleResponse>(response); }
/// <summary> /// /// </summary> /// <param name="traktUser"></param> /// <returns></returns> public async Task<List<DataContracts.Users.Collection.TraktShowCollected>> SendGetCollectedShowsRequest(TraktUser traktUser) { var response = await GetFromTrakt(TraktUris.CollectedShows, traktUser); return _jsonSerializer.DeserializeFromStream<List<DataContracts.Users.Collection.TraktShowCollected>>(response); }
/// <summary> /// Reports to trakt.tv that an episode is being watched. Or that Episode(s) have been watched. /// </summary> /// <param name="episode">The episode being watched</param> /// <param name="status">Enum indicating whether an episode is being watched or scrobbled</param> /// <param name="traktUser">The user that's watching the episode</param> /// <param name="progressPercent"></param> /// <returns>A List of standard TraktResponse Data Contracts</returns> public async Task<List<TraktScrobbleResponse>> SendEpisodeStatusUpdateAsync(Episode episode, MediaStatus status, TraktUser traktUser, float progressPercent) { var episodeDatas = new List<TraktScrobbleEpisode>(); if ((episode.IndexNumberEnd == null || episode.IndexNumberEnd == episode.IndexNumber) && !string.IsNullOrEmpty(episode.GetProviderId(MetadataProviders.Tvdb))) { episodeDatas.Add(new TraktScrobbleEpisode { AppDate = DateTime.Today.ToString("yyyy-MM-dd"), AppVersion = _appHost.ApplicationVersion.ToString(), Progress = progressPercent, Episode = new TraktEpisode { Ids = new TraktEpisodeId { Tvdb = episode.GetProviderId(MetadataProviders.Tvdb).ConvertToInt() }, } }); } // It's a multi-episode file. Add all episodes else if (episode.IndexNumber.HasValue) { episodeDatas.AddRange(Enumerable.Range(episode.IndexNumber.Value, ((episode.IndexNumberEnd ?? episode.IndexNumber).Value - episode.IndexNumber.Value) + 1) .Select(number => new TraktScrobbleEpisode { AppDate = DateTime.Today.ToString("yyyy-MM-dd"), AppVersion = _appHost.ApplicationVersion.ToString(), Progress = progressPercent, Episode = new TraktEpisode { Season = episode.GetSeasonNumber(), Number = number }, Show = new TraktShow { Title = episode.Series.Name, Year = episode.Series.ProductionYear, Ids = new TraktShowId { Tvdb = episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(), Imdb = episode.Series.GetProviderId(MetadataProviders.Imdb), TvRage = episode.Series.GetProviderId(MetadataProviders.TvRage).ConvertToInt() } } }).ToList()); } string url; switch (status) { case MediaStatus.Watching: url = TraktUris.ScrobbleStart; break; case MediaStatus.Paused: url = TraktUris.ScrobblePause; break; default: url = TraktUris.ScrobbleStop; break; } var responses = new List<TraktScrobbleResponse>(); foreach (var traktScrobbleEpisode in episodeDatas) { var response = await PostToTrakt(url, traktScrobbleEpisode, CancellationToken.None, traktUser); responses.Add(_jsonSerializer.DeserializeFromStream<TraktScrobbleResponse>(response)); } return responses; }
/// <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<TraktSyncResponse>> 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 chunks = episodes.ToChunks(100).ToList(); var traktResponses = new List<TraktSyncResponse>(); foreach (var chunk in chunks) { var response = await SendEpisodePlaystateUpdatesInternalAsync(chunk, traktUser, seen, cancellationToken); if (response != null) traktResponses.Add(response); } return traktResponses; }
/// <summary> /// Add or remove a list of movies to/from the users trakt.tv library /// </summary> /// <param name="movies">The movies to add</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<IEnumerable<TraktSyncResponse>> SendLibraryUpdateAsync(List<Movie> movies, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType) { if (movies == null) throw new ArgumentNullException("movies"); if (traktUser == null) throw new ArgumentNullException("traktUser"); if (eventType == EventType.Update) return null; var moviesPayload = movies.Select(m => { var audioStream = m.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio); return new TraktMovieCollected { CollectedAt = m.DateCreated.ToISO8601(), Is3D = m.Is3D, AudioChannels = audioStream.GetAudioChannels(), Audio = audioStream.GetCodecRepresetation(), Resolution = m.GetDefaultVideoStream().GetResolution(), Title = m.Name, Year = m.ProductionYear, Ids = new TraktMovieId { Imdb = m.GetProviderId(MetadataProviders.Imdb), Tmdb = m.GetProviderId(MetadataProviders.Tmdb).ConvertToInt() } }; }).ToList(); var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove; var responses = new List<TraktSyncResponse>(); var chunks = moviesPayload.ToChunks(100); foreach (var chunk in chunks) { var data = new TraktSyncCollected { Movies = chunk.ToList() }; var response = await PostToTrakt(url, data, cancellationToken, traktUser); responses.Add(_jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response)); } return responses; }
public async Task<TraktUserToken> GetUserToken(TraktUser traktUser) { var data = new TraktUserTokenRequest { Login = traktUser.UserName, Password = traktUser.Password }; var response = await PostToTrakt(TraktUris.Login, data, null); return _jsonSerializer.DeserializeFromStream<TraktUserToken>(response); }
/// <summary> /// Add or remove a list of Episodes to/from the users trakt.tv library /// </summary> /// <param name="episodes">The episodes to add</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<IEnumerable<TraktSyncResponse>> SendLibraryUpdateAsync(IReadOnlyList<Episode> episodes, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType) { if (episodes == null) throw new ArgumentNullException("episodes"); if (traktUser == null) throw new ArgumentNullException("traktUser"); if (eventType == EventType.Update) return null; var responses = new List<TraktSyncResponse>(); var chunks = episodes.ToChunks(100); foreach (var chunk in chunks) { responses.Add(await SendLibraryUpdateInternalAsync(chunk.ToList(), traktUser, cancellationToken, eventType)); } return responses; }
private async Task<Stream> PostToTrakt(string url, object data, TraktUser traktUser) { return await PostToTrakt(url, data, CancellationToken.None, traktUser); }
private async Task<TraktSyncResponse> SendLibraryUpdateInternalAsync(IReadOnlyList<Episode> episodes, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType) { var episodesPayload = new List<TraktEpisodeCollected>(); var showPayload = new List<TraktShowCollected>(); foreach (var episode in episodes) { var audioStream = episode.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio); if ((episode.IndexNumberEnd == null || episode.IndexNumberEnd == episode.IndexNumber) && !string.IsNullOrEmpty(episode.GetProviderId(MetadataProviders.Tvdb))) { episodesPayload.Add(new TraktEpisodeCollected { CollectedAt = episode.DateCreated.ToISO8601(), Ids = new TraktEpisodeId { Tvdb = episode.GetProviderId(MetadataProviders.Tvdb).ConvertToInt() }, Is3D = episode.Is3D, AudioChannels = audioStream.GetAudioChannels(), Audio = audioStream.GetCodecRepresetation(), Resolution = episode.GetDefaultVideoStream().GetResolution() }); } // It's a multi-episode file. Add all episodes else if (episode.IndexNumber.HasValue) { var syncShow = showPayload.FirstOrDefault( sre => sre.Ids != null && sre.Ids.Tvdb == episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt()); if (syncShow == null) { syncShow = new TraktShowCollected { Ids = new TraktShowId { Tvdb = episode.Series.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(), Imdb = episode.Series.GetProviderId(MetadataProviders.Imdb), TvRage = episode.Series.GetProviderId(MetadataProviders.TvRage).ConvertToInt() }, Seasons = new List<TraktShowCollected.TraktSeasonCollected>() }; showPayload.Add(syncShow); } var syncSeason = syncShow.Seasons.FirstOrDefault(ss => ss.Number == episode.GetSeasonNumber()); if (syncSeason == null) { syncSeason = new TraktShowCollected.TraktSeasonCollected { Number = episode.GetSeasonNumber(), Episodes = new List<TraktEpisodeCollected>() }; syncShow.Seasons.Add(syncSeason); } syncSeason.Episodes.AddRange(Enumerable.Range(episode.IndexNumber.Value, ((episode.IndexNumberEnd ?? episode.IndexNumber).Value - episode.IndexNumber.Value) + 1) .Select(number => new TraktEpisodeCollected { Number = number, CollectedAt = episode.DateCreated.ToISO8601(), Ids = new TraktEpisodeId { Tvdb = episode.GetProviderId(MetadataProviders.Tvdb).ConvertToInt() }, Is3D = episode.Is3D, AudioChannels = audioStream.GetAudioChannels(), Audio = audioStream.GetCodecRepresetation(), Resolution = episode.GetDefaultVideoStream().GetResolution() }) .ToList()); } } var data = new TraktSyncCollected { Episodes = episodesPayload.ToList(), Shows = showPayload.ToList() }; var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove; var response = await PostToTrakt(url, data, cancellationToken, traktUser); return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response); }
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); } } }
/// <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; var response = await PostToTrakt(url, data, cancellationToken, traktUser); return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response); }