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()); }
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); }
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))); }
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); }
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; } }
/// <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); }
/// <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()); }
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); } } } }
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(); }
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"); }
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); }
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); }
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); }
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); } }
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; } }
/// <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)); }
/// <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); }
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); }
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 }); }
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"); }
/// <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); }
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 }); }
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)); }
/// <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; }
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)); }