private void TriggerOnChange(LibraryChange change) { this.OnChange?.Invoke(change); }
protected virtual void OnLibraryChange() { LibraryChange?.Invoke(this, EventArgs.Empty); }
public List <LibraryChange> Update() { var changes = new List <LibraryChange>(); // Update library from Trakt lists. var activity = trakt.One <Trakt.LastActivity>(); if (activity.all > LastActivityUpdate) { if (activity.episodes.collected_at > LastActivityUpdate) { // Fetch collection and synchronize with local collection, check for added items and removed items (by comparing every item in trakt collection with local collection) var collectedEpisodes = GetMediaFromTraktCollection <Episode>().ToList(); changes.AddRange(SynchronizeLibrary(collectedEpisodes, x => x.State == Media.MediaState.Collected, x => x.State != Media.MediaState.Collected && !x.CollectedAt.HasValue, UpdateCollected)); } if (activity.movies.collected_at > LastActivityUpdate) { var collectedMovies = GetMediaFromTraktCollection <Movie>().ToList(); changes.AddRange(SynchronizeLibrary(collectedMovies, x => x.State == Media.MediaState.Collected, x => x.State != Media.MediaState.Collected && !x.CollectedAt.HasValue, UpdateCollected)); } if (activity.movies.watchlisted_at > LastActivityUpdate || activity.shows.watchlisted_at > LastActivityUpdate || activity.seasons.watchlisted_at > LastActivityUpdate || activity.episodes.watchlisted_at > LastActivityUpdate) { // Fetch entries from watchlist. var watchlistedMedia = GetMediaFromTraktWatchlist()?.ToList(); changes.AddRange(SynchronizeLibrary(watchlistedMedia.OfType <Movie>().ToList(), x => x.State != Media.MediaState.Collected && x.WatchlistedAt.HasValue)); changes.AddRange(SynchronizeLibrary(watchlistedMedia.OfType <Episode>().ToList(), x => x.State != Media.MediaState.Collected && x.WatchlistedAt.HasValue)); } if (activity.episodes.watched_at > LastActivityUpdate || activity.movies.watched_at > LastActivityUpdate) { var watchedMedia = GetWatchedMedia(new DateTime(Math.Min(Math.Max(LastActivityUpdate.Ticks, activity.episodes.watched_at.Ticks), Math.Max(LastActivityUpdate.Ticks, activity.movies.watched_at.Ticks)))).ToList(); foreach (var showId in watchedMedia.OfType <Episode>().Where(x => this.OfType <Episode>().Any(y => y.ShowId.Equals(x.ShowId) && y.Season <= x.Season && y.Number < x.Number && !y.WatchedAt.HasValue)).Select(x => x.ShowId).Distinct().ToList()) { // If we find any media in the library from the same show that is cronologically before the watched media then we need to update watched status for the whole show. watchedMedia.AddRange(GetWatchedEpisodesForShow(showId)); } Func <Media, Media, LibraryChange?> func = (e, u) => { e.WatchedAt = u.WatchedAt; return(null); }; foreach (var media in watchedMedia) { var existing = this[media]; if (existing != null && !existing.WatchedAt.HasValue) { existing.WatchedAt = media.WatchedAt; } } } this.LastActivityUpdate = activity.all; } if (LastCalendarUpdate.Date < DateTime.UtcNow.Date) { var startDate = LastCalendarUpdate; this.LastCalendarUpdate = DateTime.UtcNow; var episodes = GetMediaFromCalendar <Episode>(startDate).Where(x => !this.IgnoreSpecialSeasons || x.Season != 0).ToList(); var hiddenMediaIds = GetHiddenMediaIds().ToList(); foreach (var hidden in hiddenMediaIds) { Func <Episode, bool> removeCondition = (x) => x.State != Media.MediaState.Collected && x.ShowId.Equals(hidden.ShowId) && (!hidden.Season.HasValue || x.Season == hidden.Season); episodes.RemoveAll(new Predicate <Episode>(removeCondition)); changes.AddRange(this.RemoveAll(removeCondition)); } if (this.ExcludeUnwatchedShowsFromCalendar) { var watchedShowIds = GetWatchedShowIds(); // If we never watched this show, don't any episodes of it from the calendar. (Trakt adds all episodes of a show to the calendar if you've collected/watchlisted 1 episode) episodes.RemoveAll(x => !watchedShowIds.Contains(x.ShowId)); } changes.AddRange(SynchronizeLibrary(episodes, null, x => x.State == Media.MediaState.Awaiting, UpdateFromCalendar)); var movies = GetMediaFromCalendar <Movie>(startDate).ToList(); changes.AddRange(SynchronizeLibrary(movies, null, x => x.State == Media.MediaState.Awaiting, UpdateFromCalendar)); } // Update media states. foreach (var media in this) { var originalState = media.State; if (media.State == Media.MediaState.Registered) { var traktMedia = GetSingleMedia(media); media.Release = traktMedia.Release; media.Genres = traktMedia.Genres; if (media is Episode) { media.Id = media.Id ?? traktMedia.Id; media.Title = traktMedia.Title; } //if (media is Movie movie) //{ // movie.PhysicalRelease = GetPhysicalReleaseDate(movie); //} media.ChangeStateTo(Media.MediaState.Awaiting); } if (media.State == Media.MediaState.Awaiting && media.Release <= DateTime.Now) { media.ChangeStateTo(Media.MediaState.Available); } if (originalState != media.State) { var change = new LibraryChange(media, LibraryChange.Change.State, originalState); changes.Add(change); //TriggerOnChange(new List<LibraryChange> { change }); } if (string.IsNullOrEmpty(media.ImageUrl) && this.FetchImages != ImageFetchingBehavior.Never && (this.FetchImages != ImageFetchingBehavior.ExcludeCollection || media.State != Media.MediaState.Collected)) { List <Exception> assetExceptions = new List <Exception>(); try { var image = assets.GetAsset(media); if (!string.IsNullOrEmpty(image)) { media.ImageUrl = image; if (media is Episode episode) { foreach (var episodeInShow in this.OfType <Episode>().Where(x => x.ShowId.Equals(episode.ShowId))) { episodeInShow.ImageUrl = image; } } } } catch (AggregateException ex) when(ex.InnerException is System.Net.Http.HttpRequestException) { // Ignore this.. } } } //if (LastPhysicalReleaseUpdate.AddDays(1) < DateTime.Now) //{ // foreach (var movie in this.OfType<Movie>().State(Media.MediaState.Available).Where(x=>!x.PhysicalRelease.HasValue)) // { // movie.PhysicalRelease = GetPhysicalReleaseDate(movie); // if (movie.PhysicalRelease.HasValue && movie.PhysicalRelease > DateTime.UtcNow) // { // movie.SetState(Media.MediaState.Awaiting); // changes.Add(new LibraryChange(movie, LibraryChange.Change.State, Media.MediaState.Available)); // } // } // LastPhysicalReleaseUpdate = DateTime.Now; //} // Populate awaiting/available shows with genres. (We have to do this on a show basis since that info isn't on the individual episode. foreach (var groupedEpisodes in this.OfType <Episode>().State(Media.MediaState.Awaiting, Media.MediaState.Available).Where(x => x.Genres == null).GroupBy(x => x.ShowId)) { var genres = GetGenresForShow(groupedEpisodes.Key); foreach (var episode in groupedEpisodes) { episode.Genres = genres?.ToArray() ?? new string[0]; } } TriggerOnChange(changes); return(changes); }