예제 #1
0
        private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, List<Movie> allMovies, int categoryLimit, int itemLimit, List<ItemFields> fields)
        {
            var categories = new List<RecommendationDto>();

            var recentlyPlayedMovies = allMovies
                .Select(i =>
                {
                    var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
                    return new Tuple<Movie, bool, DateTime>(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue);
                })
                .Where(i => i.Item2)
                .OrderByDescending(i => i.Item3)
                .Select(i => i.Item1)
                .ToList();

            var excludeFromLiked = recentlyPlayedMovies.Take(10);
            var likedMovies = allMovies
                .Select(i =>
                {
                    var score = 0;
                    var userData = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    if (userData.IsFavorite)
                    {
                        score = 2;
                    }
                    else
                    {
                        score = userData.Likes.HasValue ? userData.Likes.Value ? 1 : -1 : 0;
                    }

                    return new Tuple<Movie, int>(i, score);
                })
                .OrderByDescending(i => i.Item2)
                .ThenBy(i => Guid.NewGuid())
                .Where(i => i.Item2 > 0)
                .Select(i => i.Item1)
                .Where(i => !excludeFromLiked.Contains(i));

            var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList();
            // Get recently played directors
            var recentDirectors = GetDirectors(mostRecentMovies)
                .OrderBy(i => Guid.NewGuid())
                .ToList();

            // Get recently played actors
            var recentActors = GetActors(mostRecentMovies)
                .OrderBy(i => Guid.NewGuid())
                .ToList();

            var similarToRecentlyPlayed = GetSimilarTo(user, allMovies, recentlyPlayedMovies.Take(7).OrderBy(i => Guid.NewGuid()), itemLimit, fields, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator();
            var similarToLiked = GetSimilarTo(user, allMovies, likedMovies, itemLimit, fields, RecommendationType.SimilarToLikedItem).GetEnumerator();

            var hasDirectorFromRecentlyPlayed = GetWithDirector(user, allMovies, recentDirectors, itemLimit, fields, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator();
            var hasActorFromRecentlyPlayed = GetWithActor(user, allMovies, recentActors, itemLimit, fields, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator();

            var categoryTypes = new List<IEnumerator<RecommendationDto>>
            {
                // Give this extra weight
                similarToRecentlyPlayed,
                similarToRecentlyPlayed,

                // Give this extra weight
                similarToLiked,
                similarToLiked,

                hasDirectorFromRecentlyPlayed,
                hasActorFromRecentlyPlayed
            };

            while (categories.Count < categoryLimit)
            {
                var allEmpty = true;

                foreach (var category in categoryTypes)
                {
                    if (category.MoveNext())
                    {
                        categories.Add(category.Current);
                        allEmpty = false;

                        if (categories.Count >= categoryLimit)
                        {
                            break;
                        }
                    }
                }

                if (allEmpty)
                {
                    break;
                }
            }

            return categories.OrderBy(i => i.RecommendationType).ThenBy(i => Guid.NewGuid());
        }
예제 #2
0
        public async Task <bool> StartStreamer(string twitterId, string clientId, string[] tracks)
        {
            var user = await _userDataManager.GetUserData(twitterId);

            if (user == null || tracks == null)
            {
                return(false);
            }

            var credentials = await _userDataManager.GetUserCredentials(twitterId);

            if (credentials == null || credentials.ValidUntil < DateTime.UtcNow)
            {
                //reroute for authentication
            }

            var userStream = new Models.TwitterUserStreams()
            {
                TwitterId     = twitterId,
                ClientId      = clientId,
                FilteredSteam = Tweetinvi.Stream.CreateFilteredStream(new Tweetinvi.Models.TwitterCredentials()
                {
                    AccessToken       = credentials.AccessToken,
                    AccessTokenSecret = credentials.AccessTokenSecret,
                    ConsumerKey       = _twitterConfigurations.ConsumerKey,
                    ConsumerSecret    = _twitterConfigurations.ConsumerSecret,
                })
            };

            var processCancellationTokenSource  = new CancellationTokenSource();
            CancellationToken cancellationToken = processCancellationTokenSource.Token;

            if (!(await _taskManager.TryUpdateClientStream(clientId, processCancellationTokenSource) == true))
            {
                return(false);
            }

            foreach (var track in tracks)
            {
                userStream.FilteredSteam.AddTrack(track);
            }

            var newTask = Task.Run(() =>
            {
                userStream.FilteredSteam.MatchingTweetReceived += (sender, args) =>
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        userStream?.FilteredSteam?.StopStream();
                        userStream = null;
                    }

                    _signalRcontext.Clients.Client(clientId).SendAsync("GetStream", "Linus", args.Tweet.FullText);
                };

                userStream.FilteredSteam.StartStreamMatchingAllConditionsAsync();
            }, cancellationToken);

            userStreams.Add(userStream);

            return(true);
        }
예제 #3
0
        public object Get(GetFavoritesView request)
        {
            var user = _userManager.GetUserById(request.UserId);

            var allItems = user.RootFolder.GetRecursiveChildren(user)
                           .ToList();

            var allFavoriteItems = allItems.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite)
                                   .ToList();

            var itemsWithImages = allFavoriteItems.Where(i => !string.IsNullOrEmpty(i.PrimaryImagePath))
                                  .ToList();

            var itemsWithBackdrops = allFavoriteItems.Where(i => i.BackdropImagePaths.Count > 0)
                                     .ToList();

            var view = new FavoritesView();

            var fields = new List <ItemFields>();

            view.BackdropItems = FilterItemsForBackdropDisplay(itemsWithBackdrops.OrderBy(i => Guid.NewGuid()))
                                 .Take(10)
                                 .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                                 .ToList();

            var spotlightItems = itemsWithBackdrops.OrderBy(i => Guid.NewGuid())
                                 .Take(10)
                                 .ToList();

            view.SpotlightItems = spotlightItems
                                  .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                                  .ToList();

            fields.Add(ItemFields.PrimaryImageAspectRatio);

            view.Albums = itemsWithImages
                          .OfType <MusicAlbum>()
                          .OrderBy(i => Guid.NewGuid())
                          .Take(4)
                          .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                          .ToList();

            view.Books = itemsWithImages
                         .OfType <Book>()
                         .OrderBy(i => Guid.NewGuid())
                         .Take(6)
                         .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                         .ToList();

            view.Episodes = itemsWithImages
                            .OfType <Episode>()
                            .OrderBy(i => Guid.NewGuid())
                            .Take(6)
                            .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                            .ToList();

            view.Games = itemsWithImages
                         .OfType <Game>()
                         .OrderBy(i => Guid.NewGuid())
                         .Take(6)
                         .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                         .ToList();

            view.Movies = itemsWithImages
                          .OfType <Movie>()
                          .OrderBy(i => Guid.NewGuid())
                          .Take(6)
                          .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                          .ToList();

            view.Series = itemsWithImages
                          .OfType <Series>()
                          .OrderBy(i => Guid.NewGuid())
                          .Take(6)
                          .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                          .ToList();

            view.Songs = itemsWithImages
                         .OfType <Audio>()
                         .OrderBy(i => Guid.NewGuid())
                         .Take(4)
                         .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                         .ToList();

            view.MiniSpotlights = itemsWithBackdrops
                                  .Except(spotlightItems)
                                  .OrderBy(i => Guid.NewGuid())
                                  .Take(5)
                                  .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                                  .ToList();

            var artists = allItems.OfType <Audio>()
                          .SelectMany(i =>
            {
                var list = new List <string>();

                if (!string.IsNullOrEmpty(i.AlbumArtist))
                {
                    list.Add(i.AlbumArtist);
                }
                list.AddRange(i.Artists);

                return(list);
            })
                          .Distinct(StringComparer.OrdinalIgnoreCase)
                          .OrderBy(i => Guid.NewGuid())
                          .Select(i =>
            {
                try
                {
                    return(_libraryManager.GetArtist(i));
                }
                catch
                {
                    return(null);
                }
            })
                          .Where(i => i != null && _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite)
                          .Take(4)
                          .ToList();

            view.Artists = artists
                           .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                           .ToList();

            return(ToOptimizedResult(view));
        }
        /// <summary>
        /// Filters the items.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="items">The items.</param>
        /// <param name="user">The user.</param>
        /// <returns>IEnumerable{`0}.</returns>
        private IEnumerable <TItemType> FilterItems(GetItemsByName request, IEnumerable <TItemType> items, User user)
        {
            if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
            {
                items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
            }
            if (!string.IsNullOrEmpty(request.NameStartsWith))
            {
                items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
            }

            if (!string.IsNullOrEmpty(request.NameLessThan))
            {
                items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
            }

            var imageTypes = request.GetImageTypes().ToList();

            if (imageTypes.Count > 0)
            {
                items = items.Where(item => imageTypes.Any(item.HasImage));
            }

            var filters = request.GetFilters().ToList();

            if (filters.Contains(ItemFilter.Dislikes))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    return(userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value);
                });
            }

            if (filters.Contains(ItemFilter.Likes))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    return(userdata != null && userdata.Likes.HasValue && userdata.Likes.Value);
                });
            }

            if (filters.Contains(ItemFilter.IsFavoriteOrLikes))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    var likes    = userdata.Likes ?? false;
                    var favorite = userdata.IsFavorite;

                    return(likes || favorite);
                });
            }

            if (filters.Contains(ItemFilter.IsFavorite))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    return(userdata != null && userdata.IsFavorite);
                });
            }

            // Avoid implicitly captured closure
            var currentRequest = request;

            return(items.Where(i => ApplyAdditionalFilters(currentRequest, i, user, false)));
        }
예제 #5
0
        public void Fetch_Valid_Success()
        {
            var result = new MetadataResult <Video>()
            {
                Item = new Movie()
            };

            _parser.Fetch(result, "Test Data/Justice League.nfo", CancellationToken.None);
            var item = (Movie)result.Item;

            Assert.Equal("Justice League", item.OriginalTitle);
            Assert.Equal("Justice for all.", item.Tagline);
            Assert.Equal("tt0974015", item.ProviderIds[MetadataProvider.Imdb.ToString()]);
            Assert.Equal("141052", item.ProviderIds[MetadataProvider.Tmdb.ToString()]);

            Assert.Equal(4, item.Genres.Length);
            Assert.Contains("Action", item.Genres);
            Assert.Contains("Adventure", item.Genres);
            Assert.Contains("Fantasy", item.Genres);
            Assert.Contains("Sci-Fi", item.Genres);

            Assert.Equal(new DateTime(2017, 11, 15), item.PremiereDate);
            Assert.Equal(new DateTime(2017, 11, 16), item.EndDate);
            Assert.Single(item.Studios);
            Assert.Contains("DC Comics", item.Studios);

            Assert.Equal("1.777778", item.AspectRatio);
            Assert.Equal(Video3DFormat.HalfSideBySide, item.Video3DFormat);
            Assert.Equal(1920, item.Width);
            Assert.Equal(1080, item.Height);
            Assert.Equal(new TimeSpan(0, 0, 6268).Ticks, item.RunTimeTicks);
            Assert.True(item.HasSubtitles);
            Assert.Equal(7.6f, item.CriticRating);
            Assert.Equal("8.7", item.CustomRating);
            Assert.Equal("en", item.PreferredMetadataLanguage);
            Assert.Equal("us", item.PreferredMetadataCountryCode);
            Assert.Single(item.RemoteTrailers);
            Assert.Equal("https://www.youtube.com/watch?v=dQw4w9WgXcQ", item.RemoteTrailers[0].Url);

            Assert.Equal(20, result.People.Count);

            var writers = result.People.Where(x => x.Type == PersonType.Writer).ToArray();

            Assert.Equal(3, writers.Length);
            var writerNames = writers.Select(x => x.Name);

            Assert.Contains("Jerry Siegel", writerNames);
            Assert.Contains("Joe Shuster", writerNames);
            Assert.Contains("Test", writerNames);

            var directors = result.People.Where(x => x.Type == PersonType.Director).ToArray();

            Assert.Single(directors);
            Assert.Equal("Zack Snyder", directors[0].Name);

            var actors = result.People.Where(x => x.Type == PersonType.Actor).ToArray();

            Assert.Equal(15, actors.Length);

            // Only test one actor
            var aquaman = actors.FirstOrDefault(x => x.Role.Equals("Aquaman", StringComparison.Ordinal));

            Assert.NotNull(aquaman);
            Assert.Equal("Jason Momoa", aquaman !.Name);
            Assert.Equal(5, aquaman !.SortOrder);
            Assert.Equal("https://m.media-amazon.com/images/M/MV5BMTI5MTU5NjM1MV5BMl5BanBnXkFtZTcwODc4MDk0Mw@@._V1_SX1024_SY1024_.jpg", aquaman !.ImageUrl);

            var lyricist = result.People.FirstOrDefault(x => x.Type == PersonType.Lyricist);

            Assert.NotNull(lyricist);
            Assert.Equal("Test Lyricist", lyricist !.Name);

            Assert.Equal(new DateTime(2019, 8, 6, 9, 1, 18), item.DateCreated);

            // userData
            var userData = _userDataManager.GetUserData(_testUser, item);

            Assert.Equal(2, userData.PlayCount);
            Assert.True(userData.Played);
            Assert.Equal(new DateTime(2021, 02, 11, 07, 47, 23), userData.LastPlayedDate);

            // Movie set
            Assert.Equal("702342", item.ProviderIds[MetadataProvider.TmdbCollection.ToString()]);
            Assert.Equal("Justice League Collection", item.CollectionName);

            // Images
            Assert.Equal(7, result.RemoteImages.Count);

            var posters = result.RemoteImages.Where(x => x.Type == ImageType.Primary).ToList();

            Assert.Single(posters);
            Assert.Equal("http://image.tmdb.org/t/p/original/9rtrRGeRnL0JKtu9IMBWsmlmmZz.jpg", posters[0].Url);

            var logos = result.RemoteImages.Where(x => x.Type == ImageType.Logo).ToList();

            Assert.Single(logos);
            Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png", logos[0].Url);

            var banners = result.RemoteImages.Where(x => x.Type == ImageType.Banner).ToList();

            Assert.Single(banners);
            Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-586017e95adbd.jpg", banners[0].Url);

            var thumbs = result.RemoteImages.Where(x => x.Type == ImageType.Thumb).ToList();

            Assert.Single(thumbs);
            Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585fb155c3743.jpg", thumbs[0].Url);

            var art = result.RemoteImages.Where(x => x.Type == ImageType.Art).ToList();

            Assert.Single(art);
            Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovieclearart/justice-league-5865c23193041.png", art[0].Url);

            var discArt = result.RemoteImages.Where(x => x.Type == ImageType.Disc).ToList();

            Assert.Single(discArt);
            Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].Url);

            var backdrop = result.RemoteImages.Where(x => x.Type == ImageType.Backdrop).ToList();

            Assert.Single(backdrop);
            Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg", backdrop[0].Url);

            // Local Image - contains only one item depending on operating system
            Assert.Single(result.Images);
            Assert.Equal(_localImageFileMetadata.Name, result.Images[0].FileInfo.Name);
        }
예제 #6
0
        protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult <T> itemResult)
        {
            var item = itemResult.Item;

            var          nfoConfiguration = _config.GetNfoConfiguration();
            UserItemData?userData         = null;

            if (!string.IsNullOrWhiteSpace(nfoConfiguration.UserId))
            {
                var user = _userManager.GetUserById(Guid.Parse(nfoConfiguration.UserId));
                userData = _userDataManager.GetUserData(user, item);
            }

            switch (reader.Name)
            {
            // DateCreated
            case "dateadded":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var added))
                    {
                        item.DateCreated = added;
                    }
                    else
                    {
                        Logger.LogWarning("Invalid Added value found: {Value}", val);
                    }
                }

                break;
            }

            case "originaltitle":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrEmpty(val))
                {
                    item.OriginalTitle = val;
                }

                break;
            }

            case "name":
            case "title":
            case "localtitle":
                item.Name = reader.ReadElementContentAsString();
                break;

            case "sortname":
                item.SortName = reader.ReadElementContentAsString();
                break;

            case "criticrating":
            {
                var text = reader.ReadElementContentAsString();

                if (!string.IsNullOrEmpty(text))
                {
                    if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
                    {
                        item.CriticRating = value;
                    }
                }

                break;
            }

            case "sorttitle":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.ForcedSortName = val;
                }

                break;
            }

            case "biography":
            case "plot":
            case "review":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.Overview = val;
                }

                break;
            }

            case "language":
            {
                var val = reader.ReadElementContentAsString();

                item.PreferredMetadataLanguage = val;

                break;
            }

            case "watched":
            {
                var val = reader.ReadElementContentAsBoolean();

                if (userData != null)
                {
                    userData.Played = val;
                }

                break;
            }

            case "playcount":
            {
                var val = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(val) && userData != null)
                {
                    if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var count))
                    {
                        userData.PlayCount = count;
                    }
                }

                break;
            }

            case "lastplayed":
            {
                var val = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(val) && userData != null)
                {
                    if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var added))
                    {
                        userData.LastPlayedDate = added;
                    }
                    else
                    {
                        Logger.LogWarning("Invalid lastplayed value found: {Value}", val);
                    }
                }

                break;
            }

            case "countrycode":
            {
                var val = reader.ReadElementContentAsString();

                item.PreferredMetadataCountryCode = val;

                break;
            }

            case "lockedfields":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.LockedFields = val.Split('|').Select(i =>
                        {
                            if (Enum.TryParse(i, true, out MetadataField field))
                            {
                                return((MetadataField?)field);
                            }

                            return(null);
                        }).OfType <MetadataField>().ToArray();
                }

                break;
            }

            case "tagline":
                item.Tagline = reader.ReadElementContentAsString();
                break;

            case "country":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.ProductionLocations = val.Split('/')
                                               .Select(i => i.Trim())
                                               .Where(i => !string.IsNullOrWhiteSpace(i))
                                               .ToArray();
                }

                break;
            }

            case "mpaa":
            {
                var rating = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(rating))
                {
                    item.OfficialRating = rating;
                }

                break;
            }

            case "customrating":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.CustomRating = val;
                }

                break;
            }

            case "runtime":
            {
                var text = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(text))
                {
                    if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, CultureInfo.InvariantCulture, out var runtime))
                    {
                        item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
                    }
                }

                break;
            }

            case "aspectratio":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val) &&
                    item is IHasAspectRatio hasAspectRatio)
                {
                    hasAspectRatio.AspectRatio = val;
                }

                break;
            }

            case "lockdata":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
                }

                break;
            }

            case "studio":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.AddStudio(val);
                }

                break;
            }

            case "director":
            {
                var val = reader.ReadElementContentAsString();
                foreach (var p in SplitNames(val).Select(v => new PersonInfo {
                        Name = v.Trim(), Type = PersonType.Director
                    }))
                {
                    if (string.IsNullOrWhiteSpace(p.Name))
                    {
                        continue;
                    }

                    itemResult.AddPerson(p);
                }

                break;
            }

            case "credits":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    var parts = val.Split('/').Select(i => i.Trim())
                                .Where(i => !string.IsNullOrEmpty(i));

                    foreach (var p in parts.Select(v => new PersonInfo {
                            Name = v.Trim(), Type = PersonType.Writer
                        }))
                    {
                        if (string.IsNullOrWhiteSpace(p.Name))
                        {
                            continue;
                        }

                        itemResult.AddPerson(p);
                    }
                }

                break;
            }

            case "writer":
            {
                var val = reader.ReadElementContentAsString();
                foreach (var p in SplitNames(val).Select(v => new PersonInfo {
                        Name = v.Trim(), Type = PersonType.Writer
                    }))
                {
                    if (string.IsNullOrWhiteSpace(p.Name))
                    {
                        continue;
                    }

                    itemResult.AddPerson(p);
                }

                break;
            }

            case "actor":
            {
                if (!reader.IsEmptyElement)
                {
                    using (var subtree = reader.ReadSubtree())
                    {
                        var person = GetPersonFromXmlNode(subtree);

                        if (!string.IsNullOrWhiteSpace(person.Name))
                        {
                            itemResult.AddPerson(person);
                        }
                    }
                }
                else
                {
                    reader.Read();
                }

                break;
            }

            case "trailer":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", BaseNfoSaver.YouTubeWatchUrl, StringComparison.OrdinalIgnoreCase);

                    item.AddTrailerUrl(val);
                }

                break;
            }

            case "displayorder":
            {
                var val = reader.ReadElementContentAsString();

                var hasDisplayOrder = item as IHasDisplayOrder;
                if (hasDisplayOrder != null)
                {
                    if (!string.IsNullOrWhiteSpace(val))
                    {
                        hasDisplayOrder.DisplayOrder = val;
                    }
                }

                break;
            }

            case "year":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (int.TryParse(val, out var productionYear) && productionYear > 1850)
                    {
                        item.ProductionYear = productionYear;
                    }
                }

                break;
            }

            case "rating":
            {
                var rating = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(rating))
                {
                    // All external meta is saving this as '.' for decimal I believe...but just to be sure
                    if (float.TryParse(rating.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var val))
                    {
                        item.CommunityRating = val;
                    }
                }

                break;
            }

            case "ratings":
            {
                if (!reader.IsEmptyElement)
                {
                    using var subtree = reader.ReadSubtree();
                    FetchFromRatingsNode(subtree, item);
                }
                else
                {
                    reader.Read();
                }

                break;
            }

            case "aired":
            case "formed":
            case "premiered":
            case "releasedate":
            {
                var formatString = nfoConfiguration.ReleaseDateFormat;

                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var date) && date.Year > 1850)
                    {
                        item.PremiereDate   = date;
                        item.ProductionYear = date.Year;
                    }
                }

                break;
            }

            case "enddate":
            {
                var formatString = nfoConfiguration.ReleaseDateFormat;

                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var date) && date.Year > 1850)
                    {
                        item.EndDate = date;
                    }
                }

                break;
            }

            case "genre":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    var parts = val.Split('/')
                                .Select(i => i.Trim())
                                .Where(i => !string.IsNullOrWhiteSpace(i));

                    foreach (var p in parts)
                    {
                        item.AddGenre(p);
                    }
                }

                break;
            }

            case "style":
            case "tag":
            {
                var val = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.AddTag(val);
                }

                break;
            }

            case "fileinfo":
            {
                if (!reader.IsEmptyElement)
                {
                    using (var subtree = reader.ReadSubtree())
                    {
                        FetchFromFileInfoNode(subtree, item);
                    }
                }
                else
                {
                    reader.Read();
                }

                break;
            }

            case "uniqueid":
            {
                if (reader.IsEmptyElement)
                {
                    reader.Read();
                    break;
                }

                var provider = reader.GetAttribute("type");
                var id       = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(provider) && !string.IsNullOrWhiteSpace(id))
                {
                    item.SetProviderId(provider, id);
                }

                break;
            }

            case "thumb":
            {
                FetchThumbNode(reader, itemResult);
                break;
            }

            case "fanart":
            {
                if (reader.IsEmptyElement)
                {
                    reader.Read();
                    break;
                }

                using var subtree = reader.ReadSubtree();
                if (!subtree.ReadToDescendant("thumb"))
                {
                    break;
                }

                FetchThumbNode(subtree, itemResult);
                break;
            }

            default:
                string readerName = reader.Name;
                if (_validProviderIds.TryGetValue(readerName, out string?providerIdValue))
                {
                    var id = reader.ReadElementContentAsString();
                    if (!string.IsNullOrWhiteSpace(providerIdValue) && !string.IsNullOrWhiteSpace(id))
                    {
                        item.SetProviderId(providerIdValue, id);
                    }
                }
                else
                {
                    reader.Skip();
                }

                break;
            }
        }
예제 #7
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).ConfigureAwait(false);

            var traktCollectedMovies = await _traktApi.SendGetAllCollectedMoviesRequest(traktUser).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 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.Id, child);

                if (traktUser.SynchronizeCollections)
                {
                    // if movie is not collected, or (export media info setting is enabled and every collected matching movie has different metadata), collect it
                    var collectedMathingMovies = SyncFromTraktTask.FindMatches(libraryMovie, traktCollectedMovies).ToList();
                    if (!collectedMathingMovies.Any() ||
                        (traktUser.ExportMediaInfo &&
                         collectedMathingMovies.All(
                             collectedMovie => collectedMovie.MetadataIsDifferent(libraryMovie))))
                    {
                        collectedMovies.Add(libraryMovie);
                    }
                }

                var movieWatched = SyncFromTraktTask.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.Id,
                                    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 && traktUser.PostUnwatchedHistory)
                    {
                        unplayedMovies.Add(libraryMovie);
                    }
                }

                decisionProgress.Report(100);
            }

            // send movies to mark collected
            if (traktUser.SynchronizeCollections)
            {
                await SendMovieCollectionUpdates(true, traktUser, collectedMovies, 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);
        }
예제 #8
0
        /// <summary>
        /// Filters the items.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="items">The items.</param>
        /// <param name="user">The user.</param>
        /// <returns>IEnumerable{`0}.</returns>
        private IEnumerable <TItemType> FilterItems(GetItemsByName request, IEnumerable <TItemType> items, User user)
        {
            if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
            {
                items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
            }
            if (!string.IsNullOrEmpty(request.NameStartsWith))
            {
                items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0);
            }

            if (!string.IsNullOrEmpty(request.NameLessThan))
            {
                items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
            }

            var imageTypes = request.GetImageTypes().ToList();

            if (imageTypes.Count > 0)
            {
                items = items.Where(item => imageTypes.Any(imageType => ItemsService.HasImage(item, imageType)));
            }

            var filters = request.GetFilters().ToList();

            if (filters.Count == 0)
            {
                return(items);
            }

            items = items.AsParallel();

            if (filters.Contains(ItemFilter.Dislikes))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    return(userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value);
                });
            }

            if (filters.Contains(ItemFilter.Likes))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    return(userdata != null && userdata.Likes.HasValue && userdata.Likes.Value);
                });
            }

            if (filters.Contains(ItemFilter.IsFavoriteOrLikes))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    var likes    = userdata.Likes ?? false;
                    var favorite = userdata.IsFavorite;

                    return(likes || favorite);
                });
            }

            if (filters.Contains(ItemFilter.IsFavorite))
            {
                items = items.Where(i =>
                {
                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());

                    return(userdata != null && userdata.IsFavorite);
                });
            }

            return(items.AsEnumerable());
        }
예제 #9
0
        public async Task Execute(CancellationToken cancellationToken, IProgress <double> progress)
        {
            var users = _userManager.Users.Where(u =>
            {
                var traktUser = UserHelper.GetTraktUser(u);

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

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

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

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

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

                /*
                 * In order to sync watched status to trakt.tv we need to know what's been watched on Trakt already. This
                 * will stop us from endlessly incrementing the watched values on the site.
                 */
                IEnumerable <TraktMovieDataContract> tMovies = await _traktApi.SendGetAllMoviesRequest(traktUser).ConfigureAwait(false);

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

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

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

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

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

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

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

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

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

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

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

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

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

                                    if (dataContract != null)
                                    {
                                        LogTraktResponseDataContract(dataContract);
                                    }
                                }
                                catch (ArgumentNullException argNullEx)
                                {
                                    _logger.ErrorException("ArgumentNullException handled sending movies to trakt.tv", argNullEx);
                                }
                                catch (Exception e)
                                {
                                    _logger.ErrorException("Exception handled sending movies to trakt.tv", e);
                                }
                                movies.Clear();

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

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

                                    playedMovies.Clear();
                                }

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

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

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

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

                            var isPlayedTraktTv = false;

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

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

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

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

                                    if (dataContract != null)
                                    {
                                        LogTraktResponseDataContract(dataContract);
                                    }
                                }
                                catch (ArgumentNullException argNullEx)
                                {
                                    _logger.ErrorException("ArgumentNullException handled sending episodes to trakt.tv", argNullEx);
                                }
                                catch (Exception e)
                                {
                                    _logger.ErrorException("Exception handled sending episodes to trakt.tv", e);
                                }

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

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

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

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

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

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

                            // if the show has been played locally and is unplayed on trakt.tv then add it to the list
                            if (userData != null && userData.Played && !isPlayedTraktTv)
                            {
                                playedEpisodes.Add(ep);
                            }
                            // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list
                            else if (userData != null && !userData.Played && isPlayedTraktTv)
                            {
                                unPlayedEpisodes.Add(ep);
                            }
                        }
                    }

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

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

                        if (dataContract != null)
                        {
                            LogTraktResponseDataContract(dataContract);
                        }
                    }
                    catch (ArgumentNullException argNullEx)
                    {
                        _logger.ErrorException("ArgumentNullException handled sending movies to trakt.tv", argNullEx);
                    }
                    catch (Exception e)
                    {
                        _logger.ErrorException("Exception handled sending movies to trakt.tv", e);
                    }
                }

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

                        if (dataContract != null)
                        {
                            LogTraktResponseDataContract(dataContract);
                        }
                    }
                    catch (ArgumentNullException argNullEx)
                    {
                        _logger.ErrorException("ArgumentNullException handled sending episodes to trakt.tv", argNullEx);
                    }
                    catch (Exception e)
                    {
                        _logger.ErrorException("Exception handled sending episodes to trakt.tv", e);
                    }
                }

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

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

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

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

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

                        if (dataContracts != null)
                        {
                            foreach (var dataContract in dataContracts)
                            {
                                LogTraktResponseDataContract(dataContract);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        _logger.ErrorException("Error updating episode play states", e);
                    }
                }
            }
        }
예제 #10
0
        private void AddUserData(BaseItem item, XmlWriter writer, IUserManager userManager, IUserDataManager userDataRepo, XbmcMetadataOptions options)
        {
            var userId = options.UserId;

            if (string.IsNullOrWhiteSpace(userId))
            {
                return;
            }

            var user = userManager.GetUserById(Guid.Parse(userId));

            if (user == null)
            {
                return;
            }

            if (item.IsFolder)
            {
                return;
            }

            var userdata = userDataRepo.GetUserData(user, item);

            writer.WriteElementString(
                "isuserfavorite",
                userdata.IsFavorite.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());

            if (userdata.Rating.HasValue)
            {
                writer.WriteElementString(
                    "userrating",
                    userdata.Rating.Value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
            }

            if (!item.IsFolder)
            {
                writer.WriteElementString(
                    "playcount",
                    userdata.PlayCount.ToString(CultureInfo.InvariantCulture));
                writer.WriteElementString(
                    "watched",
                    userdata.Played.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());

                if (userdata.LastPlayedDate.HasValue)
                {
                    writer.WriteElementString(
                        "lastplayed",
                        userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture).ToLowerInvariant());
                }

                writer.WriteStartElement("resume");

                var runTimeTicks = item.RunTimeTicks ?? 0;

                writer.WriteElementString(
                    "position",
                    TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture));
                writer.WriteElementString(
                    "total",
                    TimeSpan.FromTicks(runTimeTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture));
            }

            writer.WriteEndElement();
        }
예제 #11
0
        private async Task SyncTraktDataForUser(User user, double currentProgress, CancellationToken cancellationToken, IProgress <double> progress, double percentPerUser)
        {
            var libraryRoot = user.RootFolder;
            var traktUser   = UserHelper.GetTraktUser(user);

            IEnumerable <TraktMovieWatched> traktWatchedMovies;
            IEnumerable <TraktShowWatched>  traktWatchedShows;

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

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


            _logger.Info("Trakt.tv watched Movies count = " + traktWatchedMovies.Count());
            _logger.Info("Trakt.tv watched Shows count = " + traktWatchedShows.Count());


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

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

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

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

                if (matchedMovie != null)
                {
                    _logger.Debug("Movie is in Watched list " + movie.Name);

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

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

                    // Set last played to whichever is most recent, remote or local time...
                    if (!string.IsNullOrEmpty(matchedMovie.LastWatchedAt))
                    {
                        var tLastPlayed = DateTime.Parse(matchedMovie.LastWatchedAt);
                        userData.LastPlayedDate = tLastPlayed > userData.LastPlayedDate
                            ? tLastPlayed
                            : userData.LastPlayedDate;
                    }
                    await
                    _userDataManager.SaveUserData(user.Id, movie, userData, UserDataSaveReason.Import,
                                                  cancellationToken);
                }
                else
                {
                    _logger.Info("Failed to match " + movie.Name);
                }

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

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

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

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

                        var matchedEpisode = matchedSeason.Episodes.FirstOrDefault(x => x.Number == (episode.IndexNumber ?? -1));

                        if (matchedEpisode != null)
                        {
                            _logger.Debug("Episode is in Watched list " + GetVerboseEpisodeData(episode));

                            userData.Played    = true;
                            userData.PlayCount = Math.Max(matchedEpisode.Plays, userData.PlayCount);
                        }
                        else if (!traktUser.SkipUnwatchedImportFromTrakt)
                        {
                            userData.Played         = false;
                            userData.PlayCount      = 0;
                            userData.LastPlayedDate = null;
                        }

                        await
                        _userDataManager.SaveUserData(user.Id, episode, userData, UserDataSaveReason.Import,
                                                      cancellationToken);
                    }
                    else
                    {
                        _logger.Debug("No Season match in Watched shows list " + GetVerboseEpisodeData(episode));
                    }
                }
                else
                {
                    _logger.Debug("No Show match in Watched shows list " + GetVerboseEpisodeData(episode));
                }

                // purely for progress reporting
                currentProgress += percentPerItem;
                progress.Report(currentProgress);
            }
            //_logger.Info(syncItemFailures + " items not parsed");
        }
예제 #12
0
        private IEnumerable <BaseItem> ApplyFilter(IEnumerable <BaseItem> items, ItemFilter filter, User user)
        {
            // Avoid implicitly captured closure
            var currentUser = user;

            switch (filter)
            {
            case ItemFilter.IsFavoriteOrLikes:
                return(items.Where(item =>
                {
                    var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());

                    if (userdata == null)
                    {
                        return false;
                    }

                    var likes = userdata.Likes ?? false;
                    var favorite = userdata.IsFavorite;

                    return likes || favorite;
                }));

            case ItemFilter.Likes:
                return(items.Where(item =>
                {
                    var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
                }));

            case ItemFilter.Dislikes:
                return(items.Where(item =>
                {
                    var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
                }));

            case ItemFilter.IsFavorite:
                return(items.Where(item =>
                {
                    var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.IsFavorite;
                }));

            case ItemFilter.IsResumable:
                return(items.Where(item =>
                {
                    var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.PlaybackPositionTicks > 0;
                }));

            case ItemFilter.IsPlayed:
                return(items.Where(item => item.IsPlayed(currentUser)));

            case ItemFilter.IsUnplayed:
                return(items.Where(item => item.IsUnplayed(currentUser)));

            case ItemFilter.IsFolder:
                return(items.Where(item => item.IsFolder));

            case ItemFilter.IsNotFolder:
                return(items.Where(item => !item.IsFolder));
            }

            return(items);
        }
예제 #13
0
        public void Fetch_Valid_Succes()
        {
            var result = new MetadataResult <Video>()
            {
                Item = new Movie()
            };

            _parser.Fetch(result, "Test Data/Justice League.nfo", CancellationToken.None);
            var item = (Movie)result.Item;

            Assert.Equal("Justice League", item.OriginalTitle);
            Assert.Equal("Justice for all.", item.Tagline);
            Assert.Equal("tt0974015", item.ProviderIds[MetadataProvider.Imdb.ToString()]);
            Assert.Equal("141052", item.ProviderIds[MetadataProvider.Tmdb.ToString()]);

            Assert.Equal(4, item.Genres.Length);
            Assert.Contains("Action", item.Genres);
            Assert.Contains("Adventure", item.Genres);
            Assert.Contains("Fantasy", item.Genres);
            Assert.Contains("Sci-Fi", item.Genres);

            Assert.Equal(new DateTime(2017, 11, 15), item.PremiereDate);
            Assert.Equal(new DateTime(2017, 11, 16), item.EndDate);
            Assert.Single(item.Studios);
            Assert.Contains("DC Comics", item.Studios);

            Assert.Equal("1.777778", item.AspectRatio);
            Assert.Equal(Video3DFormat.HalfSideBySide, item.Video3DFormat);
            Assert.Equal(1920, item.Width);
            Assert.Equal(1080, item.Height);
            Assert.Equal(new TimeSpan(0, 0, 6268).Ticks, item.RunTimeTicks);
            Assert.True(item.HasSubtitles);
            Assert.Equal(7.6f, item.CriticRating);
            Assert.Equal("8.7", item.CustomRating);
            Assert.Equal("en", item.PreferredMetadataLanguage);
            Assert.Equal("us", item.PreferredMetadataCountryCode);
            Assert.Single(item.RemoteTrailers);
            Assert.Equal("https://www.youtube.com/watch?v=dQw4w9WgXcQ", item.RemoteTrailers[0].Url);

            Assert.Equal(20, result.People.Count);

            var writers = result.People.Where(x => x.Type == PersonType.Writer).ToArray();

            Assert.Equal(3, writers.Length);
            var writerNames = writers.Select(x => x.Name);

            Assert.Contains("Jerry Siegel", writerNames);
            Assert.Contains("Joe Shuster", writerNames);
            Assert.Contains("Test", writerNames);

            var directors = result.People.Where(x => x.Type == PersonType.Director).ToArray();

            Assert.Single(directors);
            Assert.Equal("Zack Snyder", directors[0].Name);

            var actors = result.People.Where(x => x.Type == PersonType.Actor).ToArray();

            Assert.Equal(15, actors.Length);

            // Only test one actor
            var aquaman = actors.FirstOrDefault(x => x.Role.Equals("Aquaman", StringComparison.Ordinal));

            Assert.NotNull(aquaman);
            Assert.Equal("Jason Momoa", aquaman !.Name);
            Assert.Equal(5, aquaman !.SortOrder);
            Assert.Equal("https://m.media-amazon.com/images/M/MV5BMTI5MTU5NjM1MV5BMl5BanBnXkFtZTcwODc4MDk0Mw@@._V1_SX1024_SY1024_.jpg", aquaman !.ImageUrl);

            var lyricist = result.People.FirstOrDefault(x => x.Type == PersonType.Lyricist);

            Assert.NotNull(lyricist);
            Assert.Equal("Test Lyricist", lyricist !.Name);

            Assert.Equal(new DateTime(2019, 8, 6, 9, 1, 18), item.DateCreated);

            // userData
            var userData = _userDataManager.GetUserData(_testUser, item);

            Assert.Equal(2, userData.PlayCount);
            Assert.True(userData.Played);
            Assert.Equal(new DateTime(2021, 02, 11, 07, 47, 23), userData.LastPlayedDate);

            // Movie set
            Assert.Equal("702342", item.ProviderIds[MetadataProvider.TmdbCollection.ToString()]);
            Assert.Equal("Justice League Collection", item.CollectionName);
        }
예제 #14
0
        public RecordingInfoDto GetRecordingInfoDto(ILiveTvRecording recording, LiveTvChannel channel, ILiveTvService service, User user = null)
        {
            var info = recording.RecordingInfo;

            var dto = new RecordingInfoDto
            {
                Id              = GetInternalRecordingId(service.Name, info.Id).ToString("N"),
                SeriesTimerId   = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"),
                Type            = recording.GetClientTypeName(),
                Overview        = info.Overview,
                EndDate         = info.EndDate,
                Name            = info.Name,
                StartDate       = info.StartDate,
                ExternalId      = info.Id,
                ChannelId       = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"),
                Status          = info.Status,
                StatusName      = GetStatusName(info.Status),
                Path            = info.Path,
                Genres          = info.Genres,
                IsRepeat        = info.IsRepeat,
                EpisodeTitle    = info.EpisodeTitle,
                ChannelType     = info.ChannelType,
                MediaType       = info.ChannelType == ChannelType.Radio ? MediaType.Audio : MediaType.Video,
                CommunityRating = GetClientCommunityRating(info.CommunityRating),
                OfficialRating  = info.OfficialRating,
                Audio           = info.Audio,
                IsHD            = info.IsHD,
                ServiceName     = service.Name,
                IsMovie         = info.IsMovie,
                IsSeries        = info.IsSeries,
                IsSports        = info.IsSports,
                IsLive          = info.IsLive,
                IsNews          = info.IsNews,
                IsKids          = info.IsKids,
                IsPremiere      = info.IsPremiere,
                RunTimeTicks    = (info.EndDate - info.StartDate).Ticks,
                OriginalAirDate = info.OriginalAirDate,

                MediaSources = _dtoService.GetMediaSources((BaseItem)recording)
            };

            dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToList();

            if (info.Status == RecordingStatus.InProgress)
            {
                var now   = DateTime.UtcNow.Ticks;
                var start = info.StartDate.Ticks;
                var end   = info.EndDate.Ticks;

                var pct = now - start;
                pct /= end;
                pct *= 100;
                dto.CompletionPercentage = pct;
            }

            var imageTag = GetImageTag(recording);

            if (imageTag.HasValue)
            {
                dto.ImageTags[ImageType.Primary] = imageTag.Value;
            }

            if (user != null)
            {
                dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, recording.GetUserDataKey()));

                dto.PlayAccess = recording.GetPlayAccess(user);
            }

            if (!string.IsNullOrEmpty(info.ProgramId))
            {
                dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
            }

            if (channel != null)
            {
                dto.ChannelName = channel.Name;

                if (!string.IsNullOrEmpty(channel.PrimaryImagePath))
                {
                    dto.ChannelPrimaryImageTag = GetImageTag(channel);
                }
            }

            return(dto);
        }
예제 #15
0
        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);
            }
        }
예제 #16
0
        protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult <T> itemResult)
        {
            var item = itemResult.Item;

            var          nfoConfiguration = _config.GetNfoConfiguration();
            UserItemData?userData         = null;

            if (!string.IsNullOrWhiteSpace(nfoConfiguration.UserId))
            {
                var user = _userManager.GetUserById(Guid.Parse(nfoConfiguration.UserId));
                userData = _userDataManager.GetUserData(user, item);
            }

            switch (reader.Name)
            {
            // DateCreated
            case "dateadded":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var added))
                    {
                        item.DateCreated = added.ToUniversalTime();
                    }
                    else
                    {
                        Logger.LogWarning("Invalid Added value found: {Value}", val);
                    }
                }

                break;
            }

            case "originaltitle":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrEmpty(val))
                {
                    item.OriginalTitle = val;
                }

                break;
            }

            case "name":
            case "title":
            case "localtitle":
                item.Name = reader.ReadElementContentAsString();
                break;

            case "sortname":
                item.SortName = reader.ReadElementContentAsString();
                break;

            case "criticrating":
            {
                var text = reader.ReadElementContentAsString();

                if (!string.IsNullOrEmpty(text))
                {
                    if (float.TryParse(text, NumberStyles.Any, UsCulture, out var value))
                    {
                        item.CriticRating = value;
                    }
                }

                break;
            }

            case "sorttitle":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.ForcedSortName = val;
                }

                break;
            }

            case "biography":
            case "plot":
            case "review":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.Overview = val;
                }

                break;
            }

            case "language":
            {
                var val = reader.ReadElementContentAsString();

                item.PreferredMetadataLanguage = val;

                break;
            }

            case "watched":
            {
                var val = reader.ReadElementContentAsBoolean();

                if (userData != null)
                {
                    userData.Played = val;
                }

                break;
            }

            case "playcount":
            {
                var val = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(val) && userData != null)
                {
                    if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var count))
                    {
                        userData.PlayCount = count;
                    }
                }

                break;
            }

            case "lastplayed":
            {
                var val = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(val) && userData != null)
                {
                    if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var added))
                    {
                        userData.LastPlayedDate = added.ToUniversalTime();
                    }
                    else
                    {
                        Logger.LogWarning("Invalid lastplayed value found: {Value}", val);
                    }
                }

                break;
            }

            case "countrycode":
            {
                var val = reader.ReadElementContentAsString();

                item.PreferredMetadataCountryCode = val;

                break;
            }

            case "lockedfields":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.LockedFields = val.Split('|').Select(i =>
                        {
                            if (Enum.TryParse(i, true, out MetadataField field))
                            {
                                return((MetadataField?)field);
                            }

                            return(null);
                        }).OfType <MetadataField>().ToArray();
                }

                break;
            }

            case "tagline":
                item.Tagline = reader.ReadElementContentAsString();
                break;

            case "country":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.ProductionLocations = val.Split('/')
                                               .Select(i => i.Trim())
                                               .Where(i => !string.IsNullOrWhiteSpace(i))
                                               .ToArray();
                }

                break;
            }

            case "mpaa":
            {
                var rating = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(rating))
                {
                    item.OfficialRating = rating;
                }

                break;
            }

            case "customrating":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.CustomRating = val;
                }

                break;
            }

            case "runtime":
            {
                var text = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(text))
                {
                    if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, UsCulture, out var runtime))
                    {
                        item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
                    }
                }

                break;
            }

            case "aspectratio":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val) &&
                    item is IHasAspectRatio hasAspectRatio)
                {
                    hasAspectRatio.AspectRatio = val;
                }

                break;
            }

            case "lockdata":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
                }

                break;
            }

            case "studio":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.AddStudio(val);
                }

                break;
            }

            case "director":
            {
                var val = reader.ReadElementContentAsString();
                foreach (var p in SplitNames(val).Select(v => new PersonInfo {
                        Name = v.Trim(), Type = PersonType.Director
                    }))
                {
                    if (string.IsNullOrWhiteSpace(p.Name))
                    {
                        continue;
                    }

                    itemResult.AddPerson(p);
                }

                break;
            }

            case "credits":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    var parts = val.Split('/').Select(i => i.Trim())
                                .Where(i => !string.IsNullOrEmpty(i));

                    foreach (var p in parts.Select(v => new PersonInfo {
                            Name = v.Trim(), Type = PersonType.Writer
                        }))
                    {
                        if (string.IsNullOrWhiteSpace(p.Name))
                        {
                            continue;
                        }

                        itemResult.AddPerson(p);
                    }
                }

                break;
            }

            case "writer":
            {
                var val = reader.ReadElementContentAsString();
                foreach (var p in SplitNames(val).Select(v => new PersonInfo {
                        Name = v.Trim(), Type = PersonType.Writer
                    }))
                {
                    if (string.IsNullOrWhiteSpace(p.Name))
                    {
                        continue;
                    }

                    itemResult.AddPerson(p);
                }

                break;
            }

            case "actor":
            {
                if (!reader.IsEmptyElement)
                {
                    using (var subtree = reader.ReadSubtree())
                    {
                        var person = GetPersonFromXmlNode(subtree);

                        if (!string.IsNullOrWhiteSpace(person.Name))
                        {
                            itemResult.AddPerson(person);
                        }
                    }
                }
                else
                {
                    reader.Read();
                }

                break;
            }

            case "trailer":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", BaseNfoSaver.YouTubeWatchUrl, StringComparison.OrdinalIgnoreCase);

                    item.AddTrailerUrl(val);
                }

                break;
            }

            case "displayorder":
            {
                var val = reader.ReadElementContentAsString();

                var hasDisplayOrder = item as IHasDisplayOrder;
                if (hasDisplayOrder != null)
                {
                    if (!string.IsNullOrWhiteSpace(val))
                    {
                        hasDisplayOrder.DisplayOrder = val;
                    }
                }

                break;
            }

            case "year":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (int.TryParse(val, out var productionYear) && productionYear > 1850)
                    {
                        item.ProductionYear = productionYear;
                    }
                }

                break;
            }

            case "rating":
            {
                var rating = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(rating))
                {
                    // All external meta is saving this as '.' for decimal I believe...but just to be sure
                    if (float.TryParse(rating.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var val))
                    {
                        item.CommunityRating = val;
                    }
                }

                break;
            }

            case "ratings":
            {
                if (!reader.IsEmptyElement)
                {
                    using var subtree = reader.ReadSubtree();
                    FetchFromRatingsNode(subtree, item);
                }
                else
                {
                    reader.Read();
                }

                break;
            }

            case "aired":
            case "formed":
            case "premiered":
            case "releasedate":
            {
                var formatString = nfoConfiguration.ReleaseDateFormat;

                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var date) && date.Year > 1850)
                    {
                        item.PremiereDate   = date.ToUniversalTime();
                        item.ProductionYear = date.Year;
                    }
                }

                break;
            }

            case "enddate":
            {
                var formatString = nfoConfiguration.ReleaseDateFormat;

                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var date) && date.Year > 1850)
                    {
                        item.EndDate = date.ToUniversalTime();
                    }
                }

                break;
            }

            case "genre":
            {
                var val = reader.ReadElementContentAsString();

                if (!string.IsNullOrWhiteSpace(val))
                {
                    var parts = val.Split('/')
                                .Select(i => i.Trim())
                                .Where(i => !string.IsNullOrWhiteSpace(i));

                    foreach (var p in parts)
                    {
                        item.AddGenre(p);
                    }
                }

                break;
            }

            case "style":
            case "tag":
            {
                var val = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(val))
                {
                    item.AddTag(val);
                }

                break;
            }

            case "fileinfo":
            {
                if (!reader.IsEmptyElement)
                {
                    using (var subtree = reader.ReadSubtree())
                    {
                        FetchFromFileInfoNode(subtree, item);
                    }
                }
                else
                {
                    reader.Read();
                }

                break;
            }

            case "uniqueid":
            {
                if (reader.IsEmptyElement)
                {
                    reader.Read();
                    break;
                }

                var provider = reader.GetAttribute("type");
                var id       = reader.ReadElementContentAsString();
                if (!string.IsNullOrWhiteSpace(provider) && !string.IsNullOrWhiteSpace(id))
                {
                    item.SetProviderId(provider, id);
                }

                break;
            }

            case "thumb":
            {
                var artType = reader.GetAttribute("aspect");
                var val     = reader.ReadElementContentAsString();

                // skip:
                // - empty aspect tag
                // - empty uri
                // - tag containing '.' because we can't set images for seasons, episodes or movie sets within series or movies
                if (string.IsNullOrEmpty(artType) || string.IsNullOrEmpty(val) || artType.Contains('.', StringComparison.Ordinal))
                {
                    break;
                }

                ImageType imageType = GetImageType(artType);

                if (!Uri.TryCreate(val, UriKind.Absolute, out var uri))
                {
                    Logger.LogError("Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, item.Name);
                    break;
                }

                if (uri.IsFile)
                {
                    // only allow one item of each type
                    if (itemResult.Images.Any(x => x.Type == imageType))
                    {
                        break;
                    }

                    var fileSystemMetadata = _directoryService.GetFile(val);
                    // non existing file returns null
                    if (fileSystemMetadata == null || !fileSystemMetadata.Exists)
                    {
                        Logger.LogWarning("Artwork file {Path} specified in nfo file for {ItemName} does not exist.", uri, item.Name);
                        break;
                    }

                    itemResult.Images.Add(new LocalImageInfo()
                        {
                            FileInfo = fileSystemMetadata,
                            Type     = imageType
                        });
                }
                else
                {
                    // only allow one item of each type
                    if (itemResult.RemoteImages.Any(x => x.type == imageType))
                    {
                        break;
                    }

                    itemResult.RemoteImages.Add((uri.ToString(), imageType));
                }

                break;
            }

            default:
                string readerName = reader.Name;
                if (_validProviderIds.TryGetValue(readerName, out string?providerIdValue))
                {
                    var id = reader.ReadElementContentAsString();
                    if (!string.IsNullOrWhiteSpace(providerIdValue) && !string.IsNullOrWhiteSpace(id))
                    {
                        item.SetProviderId(providerIdValue, id);
                    }
                }
                else
                {
                    reader.Skip();
                }

                break;
            }
        }
예제 #17
0
        /// <summary>
        /// Gets the next up.
        /// </summary>
        /// <returns>Task{Episode}.</returns>
        private Tuple <DateTime, Func <Episode> > GetNextUp(string seriesKey, User user, DtoOptions dtoOptions)
        {
            var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
            {
                AncestorWithPresentationUniqueKey = null,
                SeriesPresentationUniqueKey       = seriesKey,
                IncludeItemTypes           = new[] { nameof(Episode) },
                OrderBy                    = new[] { new ValueTuple <string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
                IsPlayed                   = true,
                Limit                      = 1,
                ParentIndexNumberNotEquals = 0,
                DtoOptions                 = new DtoOptions
                {
                    Fields = new ItemFields[]
                    {
                        ItemFields.SortName
                    },
                    EnableImages = false
                }
            }).FirstOrDefault();

            Func <Episode> getEpisode = () =>
            {
                var nextEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
                {
                    AncestorWithPresentationUniqueKey = null,
                    SeriesPresentationUniqueKey       = seriesKey,
                    IncludeItemTypes           = new[] { nameof(Episode) },
                    OrderBy                    = new[] { new ValueTuple <string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
                    Limit                      = 1,
                    IsPlayed                   = false,
                    IsVirtualItem              = false,
                    ParentIndexNumberNotEquals = 0,
                    MinSortName                = lastWatchedEpisode?.SortName,
                    DtoOptions                 = dtoOptions
                }).Cast <Episode>().FirstOrDefault();

                if (nextEpisode != null)
                {
                    var userData = _userDataManager.GetUserData(user, nextEpisode);

                    if (userData.PlaybackPositionTicks > 0)
                    {
                        return(null);
                    }
                }

                return(nextEpisode);
            };

            if (lastWatchedEpisode != null)
            {
                var userData = _userDataManager.GetUserData(user, lastWatchedEpisode);

                var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1);

                return(new Tuple <DateTime, Func <Episode> >(lastWatchedDate, getEpisode));
            }

            // Return the first episode
            return(new Tuple <DateTime, Func <Episode> >(DateTime.MinValue, getEpisode));
        }
예제 #18
0
        /// <summary>
        /// Applies filtering
        /// </summary>
        /// <param name="items">The items.</param>
        /// <param name="filter">The filter.</param>
        /// <param name="user">The user.</param>
        /// <param name="repository">The repository.</param>
        /// <returns>IEnumerable{BaseItem}.</returns>
        internal static IEnumerable <BaseItem> ApplyFilter(IEnumerable <BaseItem> items, ItemFilter filter, User user, IUserDataManager repository)
        {
            switch (filter)
            {
            case ItemFilter.IsFavoriteOrLikes:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    if (userdata == null)
                    {
                        return false;
                    }

                    var likes = userdata.Likes ?? false;
                    var favorite = userdata.IsFavorite;

                    return likes || favorite;
                }));

            case ItemFilter.Likes:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
                }));

            case ItemFilter.Dislikes:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
                }));

            case ItemFilter.IsFavorite:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.IsFavorite;
                }));

            case ItemFilter.IsRecentlyAdded:
                return(items.Where(item => item.IsRecentlyAdded()));

            case ItemFilter.IsResumable:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.PlaybackPositionTicks > 0;
                }));

            case ItemFilter.IsPlayed:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Played;
                }));

            case ItemFilter.IsUnplayed:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata == null || !userdata.Played;
                }));

            case ItemFilter.IsFolder:
                return(items.Where(item => item.IsFolder));

            case ItemFilter.IsNotFolder:
                return(items.Where(item => !item.IsFolder));
            }

            return(items);
        }
예제 #19
0
        public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
        {
            if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
            {
                return(false);
            }

            if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
            {
                return(false);
            }

            if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
            {
                return(false);
            }

            if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value)
            {
                return(false);
            }

            if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
            {
                return(false);
            }

            UserItemData userData = null;

            if (query.IsLiked.HasValue)
            {
                userData = userDataManager.GetUserData(user, item);

                if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
                {
                    return(false);
                }
            }

            if (query.IsFavoriteOrLiked.HasValue)
            {
                userData = userData ?? userDataManager.GetUserData(user, item);
                var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);

                if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
                {
                    return(false);
                }
            }

            if (query.IsFavorite.HasValue)
            {
                userData = userData ?? userDataManager.GetUserData(user, item);

                if (userData.IsFavorite != query.IsFavorite.Value)
                {
                    return(false);
                }
            }

            if (query.IsResumable.HasValue)
            {
                userData = userData ?? userDataManager.GetUserData(user, item);
                var isResumable = userData.PlaybackPositionTicks > 0;

                if (isResumable != query.IsResumable.Value)
                {
                    return(false);
                }
            }

            if (query.IsPlayed.HasValue)
            {
                if (item.IsPlayed(user) != query.IsPlayed.Value)
                {
                    return(false);
                }
            }

            // Filter by Video3DFormat
            if (query.Is3D.HasValue)
            {
                var val   = query.Is3D.Value;
                var video = item as Video;

                if (video == null || val != video.Video3DFormat.HasValue)
                {
                    return(false);
                }
            }

            /*
             * f**k - fix this
             * if (query.IsHD.HasValue)
             * {
             *  if (item.IsHD != query.IsHD.Value)
             *  {
             *      return false;
             *  }
             * }
             */

            if (query.IsLocked.HasValue)
            {
                var val = query.IsLocked.Value;
                if (item.IsLocked != val)
                {
                    return(false);
                }
            }

            if (query.HasOverview.HasValue)
            {
                var filterValue = query.HasOverview.Value;

                var hasValue = !string.IsNullOrEmpty(item.Overview);

                if (hasValue != filterValue)
                {
                    return(false);
                }
            }

            if (query.HasImdbId.HasValue)
            {
                var filterValue = query.HasImdbId.Value;

                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Imdb));

                if (hasValue != filterValue)
                {
                    return(false);
                }
            }

            if (query.HasTmdbId.HasValue)
            {
                var filterValue = query.HasTmdbId.Value;

                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tmdb));

                if (hasValue != filterValue)
                {
                    return(false);
                }
            }

            if (query.HasTvdbId.HasValue)
            {
                var filterValue = query.HasTvdbId.Value;

                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tvdb));

                if (hasValue != filterValue)
                {
                    return(false);
                }
            }

            if (query.HasOfficialRating.HasValue)
            {
                var filterValue = query.HasOfficialRating.Value;

                var hasValue = !string.IsNullOrEmpty(item.OfficialRating);

                if (hasValue != filterValue)
                {
                    return(false);
                }
            }

            if (query.IsPlaceHolder.HasValue)
            {
                var filterValue = query.IsPlaceHolder.Value;

                var isPlaceHolder = false;

                var hasPlaceHolder = item as ISupportsPlaceHolders;

                if (hasPlaceHolder != null)
                {
                    isPlaceHolder = hasPlaceHolder.IsPlaceHolder;
                }

                if (isPlaceHolder != filterValue)
                {
                    return(false);
                }
            }

            if (query.HasSpecialFeature.HasValue)
            {
                var filterValue = query.HasSpecialFeature.Value;

                var movie = item as IHasSpecialFeatures;

                if (movie != null)
                {
                    var ok = filterValue
                        ? movie.SpecialFeatureIds.Length > 0
                        : movie.SpecialFeatureIds.Length == 0;

                    if (!ok)
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }

            if (query.HasSubtitles.HasValue)
            {
                var val = query.HasSubtitles.Value;

                var video = item as Video;

                if (video == null || val != video.HasSubtitles)
                {
                    return(false);
                }
            }

            if (query.HasParentalRating.HasValue)
            {
                var val = query.HasParentalRating.Value;

                var rating = item.CustomRating;

                if (string.IsNullOrEmpty(rating))
                {
                    rating = item.OfficialRating;
                }

                if (val)
                {
                    if (string.IsNullOrEmpty(rating))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(rating))
                    {
                        return(false);
                    }
                }
            }

            if (query.HasTrailer.HasValue)
            {
                var val          = query.HasTrailer.Value;
                var trailerCount = 0;

                var hasTrailers = item as IHasTrailers;
                if (hasTrailers != null)
                {
                    trailerCount = hasTrailers.GetTrailerIds().Count;
                }

                var ok = val ? trailerCount > 0 : trailerCount == 0;

                if (!ok)
                {
                    return(false);
                }
            }

            if (query.HasThemeSong.HasValue)
            {
                var filterValue = query.HasThemeSong.Value;

                var themeCount = item.ThemeSongIds.Length;
                var ok         = filterValue ? themeCount > 0 : themeCount == 0;

                if (!ok)
                {
                    return(false);
                }
            }

            if (query.HasThemeVideo.HasValue)
            {
                var filterValue = query.HasThemeVideo.Value;

                var themeCount = item.ThemeVideoIds.Length;
                var ok         = filterValue ? themeCount > 0 : themeCount == 0;

                if (!ok)
                {
                    return(false);
                }
            }

            // Apply genre filter
            if (query.Genres.Length > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))
            {
                return(false);
            }

            // Filter by VideoType
            if (query.VideoTypes.Length > 0)
            {
                var video = item as Video;
                if (video == null || !query.VideoTypes.Contains(video.VideoType))
                {
                    return(false);
                }
            }

            if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage))
            {
                return(false);
            }

            // Apply studio filter
            if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
            {
                var studioItem = libraryManager.GetItemById(id);
                return(studioItem != null && item.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase));
            }))
            {
                return(false);
            }

            // Apply genre filter
            if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id =>
            {
                var genreItem = libraryManager.GetItemById(id);
                return(genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase));
            }))
            {
                return(false);
            }

            // Apply year filter
            if (query.Years.Length > 0)
            {
                if (!(item.ProductionYear.HasValue && query.Years.Contains(item.ProductionYear.Value)))
                {
                    return(false);
                }
            }

            // Apply official rating filter
            if (query.OfficialRatings.Length > 0 && !query.OfficialRatings.Contains(item.OfficialRating ?? string.Empty))
            {
                return(false);
            }

            if (query.ItemIds.Length > 0)
            {
                if (!query.ItemIds.Contains(item.Id))
                {
                    return(false);
                }
            }

            // Apply tag filter
            var tags = query.Tags;

            if (tags.Length > 0)
            {
                if (!tags.Any(v => item.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
                {
                    return(false);
                }
            }

            if (query.MinCommunityRating.HasValue)
            {
                var val = query.MinCommunityRating.Value;

                if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
                {
                    return(false);
                }
            }

            if (query.MinCriticRating.HasValue)
            {
                var val = query.MinCriticRating.Value;

                if (!(item.CriticRating.HasValue && item.CriticRating >= val))
                {
                    return(false);
                }
            }

            if (query.MinIndexNumber.HasValue)
            {
                var val = query.MinIndexNumber.Value;

                if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
                {
                    return(false);
                }
            }

            if (query.MinPremiereDate.HasValue)
            {
                var val = query.MinPremiereDate.Value;

                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
                {
                    return(false);
                }
            }

            if (query.MaxPremiereDate.HasValue)
            {
                var val = query.MaxPremiereDate.Value;

                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
                {
                    return(false);
                }
            }

            if (query.ParentIndexNumber.HasValue)
            {
                var filterValue = query.ParentIndexNumber.Value;

                if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
                {
                    return(false);
                }
            }

            if (query.SeriesStatuses.Length > 0)
            {
                var ok = new[] { item }.OfType <Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value));
                if (!ok)
                {
                    return(false);
                }
            }

            if (query.AiredDuringSeason.HasValue)
            {
                var episode = item as Episode;

                if (episode == null)
                {
                    return(false);
                }

                if (!Series.FilterEpisodesBySeason(new[] { episode }, query.AiredDuringSeason.Value, true).Any())
                {
                    return(false);
                }
            }

            return(true);
        }
예제 #20
0
        public QueryResult <Channel> GetChannelsInternal(ChannelQuery query)
        {
            var user = query.UserId.Equals(Guid.Empty)
                ? null
                : _userManager.GetUserById(query.UserId);

            var channels = GetAllChannels()
                           .Select(GetChannelEntity)
                           .OrderBy(i => i.SortName)
                           .ToList();

            if (query.IsRecordingsFolder.HasValue)
            {
                var val = query.IsRecordingsFolder.Value;
                channels = channels.Where(i =>
                {
                    try
                    {
                        return((GetChannelProvider(i) is IHasFolderAttributes hasAttributes &&
                                hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val);
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            if (query.SupportsLatestItems.HasValue)
            {
                var val = query.SupportsLatestItems.Value;
                channels = channels.Where(i =>
                {
                    try
                    {
                        return(GetChannelProvider(i) is ISupportsLatestMedia == val);
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            if (query.SupportsMediaDeletion.HasValue)
            {
                var val = query.SupportsMediaDeletion.Value;
                channels = channels.Where(i =>
                {
                    try
                    {
                        return(GetChannelProvider(i) is ISupportsDelete == val);
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            if (query.IsFavorite.HasValue)
            {
                var val = query.IsFavorite.Value;
                channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val)
                           .ToList();
            }

            if (user != null)
            {
                channels = channels.Where(i =>
                {
                    if (!i.IsVisible(user))
                    {
                        return(false);
                    }

                    try
                    {
                        return(GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture)));
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            var all        = channels;
            var totalCount = all.Count;

            if (query.StartIndex.HasValue || query.Limit.HasValue)
            {
                int startIndex = query.StartIndex ?? 0;
                int count      = query.Limit == null ? totalCount - startIndex : Math.Min(query.Limit.Value, totalCount - startIndex);
                all = all.GetRange(startIndex, count);
            }

            if (query.RefreshLatestChannelItems)
            {
                foreach (var item in all)
                {
                    RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).GetAwaiter().GetResult();
                }
            }

            return(new QueryResult <Channel>
            {
                Items = all,
                TotalRecordCount = totalCount
            });
        }
예제 #21
0
        private async Task SyncTraktDataForUser(User user, double currentProgress, CancellationToken cancellationToken, IProgress <double> progress, double percentPerUser)
        {
            var traktUser = UserHelper.GetTraktUser(user);

            List <TraktMovieWatched> traktWatchedMovies;
            List <TraktShowWatched>  traktWatchedShows;

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

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

            _logger.Info("Trakt.tv watched Movies count = " + traktWatchedMovies.Count);
            _logger.Info("Trakt.tv watched Shows count = " + traktWatchedShows.Count);

            var mediaItems =
                _libraryManager.GetItemList(
                    new InternalItemsQuery(user)
            {
                IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Episode).Name },
                IsVirtualItem    = false,
                OrderBy          = new []
                {
                    new ValueTuple <string, SortOrder>(ItemSortBy.SeriesSortName, SortOrder.Ascending),
                    new ValueTuple <string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending)
                }
            })
                .Where(i => _traktApi.CanSync(i, traktUser)).ToList();

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

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

                if (matchedMovie != null)
                {
                    _logger.Debug("Movie is in Watched list " + movie.Name);

                    var  userData = _userDataManager.GetUserData(user.InternalId, movie);
                    bool changed  = false;

                    // set movie as watched
                    if (!userData.Played)
                    {
                        userData.Played         = true;
                        userData.LastPlayedDate = DateTimeOffset.UtcNow;
                        changed = true;
                    }

                    // keep the highest play count
                    int playcount = Math.Max(matchedMovie.plays, userData.PlayCount);

                    // set movie playcount
                    if (userData.PlayCount != playcount)
                    {
                        userData.PlayCount = playcount;
                        changed            = true;
                    }

                    // Set last played to whichever is most recent, remote or local time...
                    if (!string.IsNullOrEmpty(matchedMovie.last_watched_at))
                    {
                        var tLastPlayed  = DateTimeOffset.Parse(matchedMovie.last_watched_at).ToUniversalTime();
                        var latestPlayed = tLastPlayed > userData.LastPlayedDate ? tLastPlayed : userData.LastPlayedDate;
                        if (userData.LastPlayedDate != latestPlayed)
                        {
                            userData.LastPlayedDate = latestPlayed;
                            changed = true;
                        }
                    }

                    // Only process if there's a change
                    if (changed)
                    {
                        _userDataManager.SaveUserData(
                            user.InternalId,
                            movie,
                            userData,
                            UserDataSaveReason.Import,
                            cancellationToken);
                    }
                }
                else
                {
                    //_logger.Info("Failed to match " + movie.Name);
                }

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

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

                if (matchedShow != null)
                {
                    var matchedSeason =
                        matchedShow.seasons.FirstOrDefault(
                            tSeason =>
                            tSeason.number
                            == (episode.ParentIndexNumber == 0
                                        ? 0
                                        : ((episode.ParentIndexNumber ?? 1))));

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

                        var matchedEpisode =
                            matchedSeason.episodes.FirstOrDefault(x => x.number == (episode.IndexNumber ?? -1));

                        if (matchedEpisode != null)
                        {
                            _logger.Debug("Episode is in Watched list " + GetVerboseEpisodeData(episode));

                            // Set episode as watched
                            if (!userData.Played)
                            {
                                userData.Played         = true;
                                userData.LastPlayedDate = DateTimeOffset.UtcNow;
                                changed = true;
                            }

                            // keep the highest play count
                            int playcount = Math.Max(matchedEpisode.plays, userData.PlayCount);

                            // set episode playcount
                            if (userData.PlayCount != playcount)
                            {
                                userData.PlayCount = playcount;
                                changed            = true;
                            }
                        }
                        else if (!traktUser.SkipUnwatchedImportFromTrakt)
                        {
                            userData.Played         = false;
                            userData.PlayCount      = 0;
                            userData.LastPlayedDate = null;
                            changed = true;
                        }

                        // only process if changed
                        if (changed)
                        {
                            _userDataManager.SaveUserData(
                                user.InternalId,
                                episode,
                                userData,
                                UserDataSaveReason.Import,
                                cancellationToken);
                        }
                    }
                    else
                    {
                        _logger.Debug("No Season match in Watched shows list " + GetVerboseEpisodeData(episode));
                    }
                }
                else
                {
                    _logger.Debug("No Show match in Watched shows list " + GetVerboseEpisodeData(episode));
                }

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

            // _logger.Info(syncItemFailures + " items not parsed");
        }
예제 #22
0
        /// <summary> Applies filtering. </summary>
        /// <param name="items"> The items. </param>
        /// <param name="filter"> The filter. </param>
        /// <param name="user"> The user. </param>
        /// <param name="repository"> The repository. </param>
        /// <returns> IEnumerable{BaseItem}. </returns>
        internal static IEnumerable <BaseItem> ApplyFilter(IEnumerable <BaseItem> items, ItemFilter filter, User user, IUserDataManager repository)
        {
            // Avoid implicitly captured closure
            var currentUser = user;

            switch (filter)
            {
            case ItemFilter.IsFavoriteOrLikes:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    if (userdata == null)
                    {
                        return false;
                    }

                    var likes = userdata.Likes ?? false;
                    var favorite = userdata.IsFavorite;

                    return likes || favorite;
                }));

            case ItemFilter.Likes:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
                }));

            case ItemFilter.Dislikes:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
                }));

            case ItemFilter.IsFavorite:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.IsFavorite;
                }));

            case ItemFilter.IsResumable:
                return(items.Where(item =>
                {
                    var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

                    return userdata != null && userdata.PlaybackPositionTicks > 0;
                }));

            case ItemFilter.IsPlayed:
                return(items.Where(item => item.IsPlayed(currentUser)));

            case ItemFilter.IsUnplayed:
                return(items.Where(item => item.IsUnplayed(currentUser)));

            case ItemFilter.IsFolder:
                return(items.Where(item => item.IsFolder));

            case ItemFilter.IsNotFolder:
                return(items.Where(item => !item.IsFolder));

            case ItemFilter.IsRecentlyAdded:
                return(items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10));
            }

            return(items);
        }
예제 #23
0
        public QueryResult <Channel> GetChannelsInternal(ChannelQuery query)
        {
            var user = query.UserId.Equals(Guid.Empty)
                ? null
                : _userManager.GetUserById(query.UserId);

            var channels = GetAllChannels()
                           .Select(GetChannelEntity)
                           .OrderBy(i => i.SortName)
                           .ToList();

            if (query.IsRecordingsFolder.HasValue)
            {
                var val = query.IsRecordingsFolder.Value;
                channels = channels.Where(i =>
                {
                    try
                    {
                        var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes;

                        return((hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val);
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            if (query.SupportsLatestItems.HasValue)
            {
                var val = query.SupportsLatestItems.Value;
                channels = channels.Where(i =>
                {
                    try
                    {
                        return(GetChannelProvider(i) is ISupportsLatestMedia == val);
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            if (query.SupportsMediaDeletion.HasValue)
            {
                var val = query.SupportsMediaDeletion.Value;
                channels = channels.Where(i =>
                {
                    try
                    {
                        return(GetChannelProvider(i) is ISupportsDelete == val);
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }
            if (query.IsFavorite.HasValue)
            {
                var val = query.IsFavorite.Value;
                channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val)
                           .ToList();
            }

            if (user != null)
            {
                channels = channels.Where(i =>
                {
                    if (!i.IsVisible(user))
                    {
                        return(false);
                    }

                    try
                    {
                        return(GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N")));
                    }
                    catch
                    {
                        return(false);
                    }
                }).ToList();
            }

            var all        = channels;
            var totalCount = all.Count;

            if (query.StartIndex.HasValue)
            {
                all = all.Skip(query.StartIndex.Value).ToList();
            }
            if (query.Limit.HasValue)
            {
                all = all.Take(query.Limit.Value).ToList();
            }

            var returnItems = all.ToArray();

            if (query.RefreshLatestChannelItems)
            {
                foreach (var item in returnItems)
                {
                    var task = RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None);
                    Task.WaitAll(task);
                }
            }

            return(new QueryResult <Channel>
            {
                Items = returnItems,
                TotalRecordCount = totalCount
            });
        }
예제 #24
0
        private async Task SyncDataforUserByArtistBulk(User user, IProgress <double> progress, CancellationToken cancellationToken, double maxProgress, double progressOffset)
        {
            var artists = _libraryManager.GetArtists(new InternalItemsQuery(user))
                          .Items
                          .Select(i => i.Item1)
                          .Cast <MusicArtist>()
                          .ToList();

            var lastFmUser = UserHelpers.GetUser(user);

            var totalSongs   = 0;
            var matchedSongs = 0;

            //Get loved tracks
            var lovedTracksReponse = await _apiClient.GetLovedTracks(lastFmUser).ConfigureAwait(false);

            var hasLovedTracks = lovedTracksReponse.HasLovedTracks();

            //Get entire library
            var usersTracks = await GetUsersLibrary(lastFmUser, progress, cancellationToken, maxProgress, progressOffset);

            if (usersTracks.Count == 0)
            {
                Plugin.Logger.Info("User {0} has no tracks in last.fm", user.Name);
                return;
            }

            //Group the library by artist
            var userLibrary = usersTracks.GroupBy(t => t.Artist.MusicBrainzId).ToList();

            //Loop through each artist
            foreach (var artist in artists)
            {
                cancellationToken.ThrowIfCancellationRequested();

                //Get all the tracks by the current artist
                var artistMBid = Helpers.GetMusicBrainzArtistId(artist);

                if (artistMBid == null)
                {
                    continue;
                }

                //Get the tracks from lastfm for the current artist
                var artistTracks = userLibrary.FirstOrDefault(t => t.Key.Equals(artistMBid));

                if (artistTracks == null || !artistTracks.Any())
                {
                    Plugin.Logger.Info("{0} has no tracks in last.fm library for {1}", user.Name, artist.Name);
                    continue;
                }

                var artistTracksList = artistTracks.ToList();

                Plugin.Logger.Info("Found {0} tracks in last.fm library for {1}", artistTracksList.Count, artist.Name);

                //Loop through each song
                foreach (var song in artist.GetRecursiveChildren().OfType <Audio>())
                {
                    totalSongs++;

                    var matchedSong = Helpers.FindMatchedLastfmSong(artistTracksList, song);

                    if (matchedSong == null)
                    {
                        continue;
                    }

                    //We have found a match
                    matchedSongs++;

                    Plugin.Logger.Debug("Found match for {0} = {1}", song.Name, matchedSong.Name);

                    var userData = _userDataManager.GetUserData(user, song);

                    //Check if its a favourite track
                    if (hasLovedTracks && lastFmUser.Options.SyncFavourites)
                    {
                        //Use MBID if set otherwise match on song name
                        var favourited = lovedTracksReponse.LovedTracks.Tracks.Any(
                            t => String.IsNullOrWhiteSpace(t.MusicBrainzId)
                                ? StringHelper.IsLike(t.Name, matchedSong.Name)
                                : t.MusicBrainzId.Equals(matchedSong.MusicBrainzId)
                            );

                        userData.IsFavorite = favourited;

                        Plugin.Logger.Debug("{0} Favourite: {1}", song.Name, favourited);
                    }

                    //Update the play count
                    if (matchedSong.PlayCount > 0)
                    {
                        userData.Played    = true;
                        userData.PlayCount = Math.Max(userData.PlayCount, matchedSong.PlayCount);
                    }
                    else
                    {
                        userData.Played         = false;
                        userData.PlayCount      = 0;
                        userData.LastPlayedDate = null;
                    }

                    _userDataManager.SaveUserData(user.InternalId, song, userData, UserDataSaveReason.UpdateUserRating, cancellationToken);
                }
            }

            //The percentage might not actually be correct but I'm pretty tired and don't want to think about it
            Plugin.Logger.Info("Finished import Last.fm library for {0}. Local Songs: {1} | Last.fm Songs: {2} | Matched Songs: {3} | {4}% match rate",
                               user.Name, totalSongs, usersTracks.Count, matchedSongs, Math.Round(((double)matchedSongs / Math.Min(usersTracks.Count, totalSongs)) * 100));
        }
예제 #25
0
		/// <summary> Applies filtering. </summary>
		/// <param name="items"> The items. </param>
		/// <param name="filter"> The filter. </param>
		/// <param name="user"> The user. </param>
		/// <param name="repository"> The repository. </param>
		/// <returns> IEnumerable{BaseItem}. </returns>
		internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user, IUserDataManager repository)
		{
			// Avoid implicitly captured closure
			var currentUser = user;

			switch (filter)
			{
				case ItemFilter.IsFavoriteOrLikes:
					return items.Where(item =>
					{
						var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

						if (userdata == null)
						{
							return false;
						}

						var likes = userdata.Likes ?? false;
						var favorite = userdata.IsFavorite;

						return likes || favorite;
					});

				case ItemFilter.Likes:
					return items.Where(item =>
					{
						var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

						return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
					});

				case ItemFilter.Dislikes:
					return items.Where(item =>
					{
						var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

						return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
					});

				case ItemFilter.IsFavorite:
					return items.Where(item =>
					{
						var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

						return userdata != null && userdata.IsFavorite;
					});

				case ItemFilter.IsResumable:
					return items.Where(item =>
					{
						var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());

						return userdata != null && userdata.PlaybackPositionTicks > 0;
					});

				case ItemFilter.IsPlayed:
					return items.Where(item => item.IsPlayed(currentUser));

				case ItemFilter.IsUnplayed:
					return items.Where(item => item.IsUnplayed(currentUser));

				case ItemFilter.IsFolder:
					return items.Where(item => item.IsFolder);

				case ItemFilter.IsNotFolder:
					return items.Where(item => !item.IsFolder);

				case ItemFilter.IsRecentlyAdded:
					return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10);
			}

			return items;
		}
예제 #26
0
        public object Get(GetFavoritesView request)
        {
            var user = _userManager.GetUserById(request.UserId);

            var allItems = user.RootFolder.GetRecursiveChildren(user)
                           .ToList();

            var allFavoriteItems = allItems.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite)
                                   .ToList();

            var itemsWithImages = allFavoriteItems.Where(i => !string.IsNullOrEmpty(i.PrimaryImagePath))
                                  .ToList();

            var itemsWithBackdrops = allFavoriteItems.Where(i => i.GetImages(ImageType.Backdrop).Any())
                                     .ToList();

            var view = new FavoritesView();

            var fields = new List <ItemFields>();

            view.BackdropItems = FilterItemsForBackdropDisplay(itemsWithBackdrops)
                                 .Randomize("backdrop")
                                 .Take(10)
                                 .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                                 .ToList();

            var spotlightItems = itemsWithBackdrops.Randomize("spotlight")
                                 .Take(10)
                                 .ToList();

            view.SpotlightItems = spotlightItems
                                  .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                                  .ToList();

            fields.Add(ItemFields.PrimaryImageAspectRatio);

            view.Albums = itemsWithImages
                          .OfType <MusicAlbum>()
                          .Randomize()
                          .Take(4)
                          .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                          .ToList();

            view.Books = itemsWithImages
                         .OfType <Book>()
                         .Randomize()
                         .Take(6)
                         .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                         .ToList();

            view.Episodes = itemsWithImages
                            .OfType <Episode>()
                            .Randomize()
                            .Take(6)
                            .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                            .ToList();

            view.Games = itemsWithImages
                         .OfType <Game>()
                         .Randomize()
                         .Take(6)
                         .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                         .ToList();

            view.Movies = itemsWithImages
                          .OfType <Movie>()
                          .Randomize()
                          .Take(6)
                          .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                          .ToList();

            view.Series = itemsWithImages
                          .OfType <Series>()
                          .Randomize()
                          .Take(6)
                          .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                          .ToList();

            view.Songs = itemsWithImages
                         .OfType <Audio>()
                         .Randomize()
                         .Take(4)
                         .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                         .ToList();

            view.MiniSpotlights = itemsWithBackdrops
                                  .Except(spotlightItems)
                                  .Randomize()
                                  .Take(5)
                                  .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                                  .ToList();

            var artists = _libraryManager.GetAllArtists(allItems)
                          .Randomize()
                          .Select(i =>
            {
                try
                {
                    return(_libraryManager.GetArtist(i));
                }
                catch
                {
                    return(null);
                }
            })
                          .Where(i => i != null && _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite)
                          .Take(4)
                          .ToList();

            view.Artists = artists
                           .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                           .ToList();

            return(ToOptimizedSerializedResultUsingCache(view));
        }