Example #1
0
 private void TriggerOnChange(LibraryChange change)
 {
     this.OnChange?.Invoke(change);
 }
Example #2
0
 protected virtual void OnLibraryChange()
 {
     LibraryChange?.Invoke(this, EventArgs.Empty);
 }
Example #3
0
        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);
        }