From 729d65c45e8b2518c0be9f62994c983db01e3efd Mon Sep 17 00:00:00 2001 From: Alun Jones Date: Tue, 31 May 2016 19:25:26 +0100 Subject: [PATCH 1/7] Remote Trailers support at episode Level --- .../Entities/TV/Episode.cs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index a4b0b3082..7ca09d9b2 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -11,13 +11,25 @@ namespace MediaBrowser.Controller.Entities.TV /// /// Class Episode /// - public class Episode : Video, IHasLookupInfo, IHasSeries - { - /// - /// Gets the season in which it aired. - /// - /// The aired season. - public int? AirsBeforeSeasonNumber { get; set; } + public class Episode : Video, IHasTrailers, IHasLookupInfo, IHasSeries + { + + public Episode() + { + RemoteTrailers = new List(); + LocalTrailerIds = new List(); + RemoteTrailerIds = new List(); + } + + public List LocalTrailerIds { get; set; } + public List RemoteTrailerIds { get; set; } + public List RemoteTrailers { get; set; } + + /// + /// Gets the season in which it aired. + /// + /// The aired season. + public int? AirsBeforeSeasonNumber { get; set; } public int? AirsAfterSeasonNumber { get; set; } public int? AirsBeforeEpisodeNumber { get; set; } From 0915d1f3836844afcb432b1a9ed8386f7d1b63c0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 31 May 2016 16:17:16 -0400 Subject: [PATCH 2/7] update nuget --- .../MediaBrowser.Server.Implementations.csproj | 4 ++-- MediaBrowser.Server.Implementations/packages.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 28edbfcc4..385a94aa5 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -46,8 +46,8 @@ ..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll - False - ..\packages\Emby.XmlTv.1.0.0.48\lib\net45\Emby.XmlTv.dll + ..\packages\Emby.XmlTv.1.0.0.49\lib\net45\Emby.XmlTv.dll + True ..\packages\ini-parser.2.2.4\lib\net20\INIFileParser.dll diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 31b1454d5..9b050672c 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,7 +1,7 @@  - + From e1f562e16ff585b440a43029efe9db314b4de965 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 1 Jun 2016 01:50:00 -0400 Subject: [PATCH 3/7] calculate similarity at database level --- MediaBrowser.Api/GamesService.cs | 43 ++++- MediaBrowser.Api/Movies/MoviesService.cs | 114 ++++-------- MediaBrowser.Api/TvShowsService.cs | 43 ++++- .../Entities/InternalItemsQuery.cs | 2 + .../Persistence/IDbConnector.cs | 1 + .../Persistence/SqliteExtensions.cs | 175 ++++++++++++++++++ .../Persistence/SqliteItemRepository.cs | 80 +++++--- .../MediaBrowser.Server.Mono.csproj | 5 +- .../Native/DbConnector.cs | 29 +++ .../Native/SqliteExtensions.cs | 62 ------- .../MediaBrowser.ServerApplication.csproj | 5 +- .../Native/DbConnector.cs | 38 ++++ .../Native/SqliteExtensions.cs | 71 ------- 13 files changed, 411 insertions(+), 257 deletions(-) create mode 100644 MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs create mode 100644 MediaBrowser.Server.Mono/Native/DbConnector.cs delete mode 100644 MediaBrowser.Server.Mono/Native/SqliteExtensions.cs create mode 100644 MediaBrowser.ServerApplication/Native/DbConnector.cs delete mode 100644 MediaBrowser.ServerApplication/Native/SqliteExtensions.cs diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 387771b6d..cb77e62ad 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Api { @@ -187,18 +188,40 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetSimilarGames request) { - var dtoOptions = GetDtoOptions(request); - - var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, - _itemRepo, - _libraryManager, - _userDataRepository, - _dtoService, - Logger, - request, new[] { typeof(Game) }, - SimilarItemsHelper.GetSimiliarityScore); + var result = GetSimilarItemsResult(request); return ToOptimizedSerializedResultUsingCache(result); } + + private QueryResult GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) + { + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + + var item = string.IsNullOrEmpty(request.Id) ? + (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : + _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + + var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) + { + Limit = request.Limit, + IncludeItemTypes = new[] + { + typeof(Game).Name + }, + SimilarTo = item + + }).ToList(); + + var dtoOptions = GetDtoOptions(request); + + var result = new QueryResult + { + Items = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ToArray(), + + TotalRecordCount = itemsResult.Count + }; + + return result; + } } } diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index ce36dd2ac..ff18d440c 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -111,18 +111,16 @@ namespace MediaBrowser.Api.Movies /// /// The request. /// System.Object. - public async Task Get(GetSimilarMovies request) + public object Get(GetSimilarMovies request) { - var result = await GetSimilarItemsResult( - request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + var result = GetSimilarItemsResult(request); return ToOptimizedSerializedResultUsingCache(result); } - public async Task Get(GetSimilarTrailers request) + public object Get(GetSimilarTrailers request) { - var result = await GetSimilarItemsResult( - request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + var result = GetSimilarItemsResult(request); return ToOptimizedSerializedResultUsingCache(result); } @@ -131,42 +129,16 @@ namespace MediaBrowser.Api.Movies { var user = _userManager.GetUserById(request.UserId); - var query = new InternalItemsQuery(user) - { - IncludeItemTypes = new[] - { - typeof(Movie).Name, - typeof(Trailer).Name, - //typeof(LiveTvProgram).Name - }, - // IsMovie = true - }; - - var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId }; - var movies = _libraryManager.GetItemList(query, parentIds) - .OrderBy(i => (int)i.SourceType); - - var listEligibleForSuggestion = new List(); - - var list = movies.ToList(); - - listEligibleForSuggestion.AddRange(list); - - listEligibleForSuggestion = listEligibleForSuggestion - .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase) - .ToList(); - var dtoOptions = GetDtoOptions(request); dtoOptions.Fields = request.GetItemFields().ToList(); - var result = GetRecommendationCategories(user, request.ParentId, listEligibleForSuggestion, request.CategoryLimit, request.ItemLimit, dtoOptions); + var result = GetRecommendationCategories(user, request.ParentId, request.CategoryLimit, request.ItemLimit, dtoOptions); return ToOptimizedResult(result); } - private async Task GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func, List, BaseItem, int> getSimilarityScore) + private QueryResult GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; @@ -174,57 +146,32 @@ namespace MediaBrowser.Api.Movies (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - var query = new InternalItemsQuery(user) + var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { + Limit = request.Limit, IncludeItemTypes = new[] { - typeof(Movie).Name, - typeof(Trailer).Name, - //typeof(LiveTvProgram).Name + typeof(Movie).Name, + typeof(Trailer).Name, + typeof(LiveTvProgram).Name }, - //IsMovie = true - }; - - var list = _libraryManager.GetItemList(query) - .OrderBy(i => (int)i.SourceType) - .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) - .ToList(); - - if (item is Video) - { - var imdbId = item.GetProviderId(MetadataProviders.Imdb); - - // Use imdb id to try to filter duplicates of the same item - if (!string.IsNullOrWhiteSpace(imdbId)) - { - list = list - .Where(i => !string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)) - .ToList(); - } - } - - var items = SimilarItemsHelper.GetSimilaritems(item, _libraryManager, list, getSimilarityScore).ToList(); - - IEnumerable returnItems = items; - - if (request.Limit.HasValue) - { - returnItems = returnItems.Take(request.Limit.Value); - } + IsMovie = true, + SimilarTo = item + }).ToList(); var dtoOptions = GetDtoOptions(request); - var result = new ItemsResult + var result = new QueryResult { - Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(), + Items = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ToArray(), - TotalRecordCount = items.Count + TotalRecordCount = itemsResult.Count }; return result; } - private IEnumerable GetRecommendationCategories(User user, string parentId, List allMovies, int categoryLimit, int itemLimit, DtoOptions dtoOptions) + private IEnumerable GetRecommendationCategories(User user, string parentId, int categoryLimit, int itemLimit, DtoOptions dtoOptions) { var categories = new List(); @@ -260,7 +207,7 @@ namespace MediaBrowser.Api.Movies IsFavoriteOrLiked = true, ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray() - }, parentIds); + }, parentIds).ToList(); var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList(); // Get recently played directors @@ -273,8 +220,8 @@ namespace MediaBrowser.Api.Movies .OrderBy(i => Guid.NewGuid()) .ToList(); - var similarToRecentlyPlayed = GetSimilarTo(user, allMovies, recentlyPlayedMovies.Take(7).OrderBy(i => Guid.NewGuid()), itemLimit, dtoOptions, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator(); - var similarToLiked = GetSimilarTo(user, allMovies, likedMovies, itemLimit, dtoOptions, RecommendationType.SimilarToLikedItem).GetEnumerator(); + var similarToRecentlyPlayed = GetSimilarTo(user, recentlyPlayedMovies, itemLimit, dtoOptions, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator(); + var similarToLiked = GetSimilarTo(user, likedMovies, itemLimit, dtoOptions, RecommendationType.SimilarToLikedItem).GetEnumerator(); var hasDirectorFromRecentlyPlayed = GetWithDirector(user, recentDirectors, itemLimit, dtoOptions, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator(); var hasActorFromRecentlyPlayed = GetWithActor(user, recentActors, itemLimit, dtoOptions, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator(); @@ -389,14 +336,23 @@ namespace MediaBrowser.Api.Movies } } - private IEnumerable GetSimilarTo(User user, List allMovies, IEnumerable baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) + private IEnumerable GetSimilarTo(User user, List baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { foreach (var item in baselineItems) { - var similar = SimilarItemsHelper - .GetSimilaritems(item, _libraryManager, allMovies, SimilarItemsHelper.GetSimiliarityScore) - .Take(itemLimit) - .ToList(); + var similar = _libraryManager.GetItemList(new InternalItemsQuery(user) + { + Limit = itemLimit, + IncludeItemTypes = new[] + { + typeof(Movie).Name, + typeof(Trailer).Name, + typeof(LiveTvProgram).Name + }, + IsMovie = true, + SimilarTo = item + + }).ToList(); if (similar.Count > 0) { diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index aa0485d57..5ccfede1e 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -12,6 +12,7 @@ using ServiceStack; using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Api { @@ -273,20 +274,42 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetSimilarShows request) { - var dtoOptions = GetDtoOptions(request); - - var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, - _itemRepo, - _libraryManager, - _userDataManager, - _dtoService, - Logger, - request, new[] { typeof(Series) }, - SimilarItemsHelper.GetSimiliarityScore); + var result = GetSimilarItemsResult(request); return ToOptimizedSerializedResultUsingCache(result); } + private QueryResult GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) + { + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + + var item = string.IsNullOrEmpty(request.Id) ? + (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : + _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + + var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) + { + Limit = request.Limit, + IncludeItemTypes = new[] + { + typeof(Series).Name + }, + SimilarTo = item + + }).ToList(); + + var dtoOptions = GetDtoOptions(request); + + var result = new QueryResult + { + Items = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ToArray(), + + TotalRecordCount = itemsResult.Count + }; + + return result; + } + public object Get(GetUpcomingEpisodes request) { var user = _userManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index f3f05a08f..0047a13b2 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -19,6 +19,8 @@ namespace MediaBrowser.Controller.Entities public User User { get; set; } + public BaseItem SimilarTo { get; set; } + public bool? IsFolder { get; set; } public bool? IsFavorite { get; set; } public bool? IsFavoriteOrLiked { get; set; } diff --git a/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs b/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs index cac9fe983..985d79a0a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs +++ b/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs @@ -6,5 +6,6 @@ namespace MediaBrowser.Server.Implementations.Persistence public interface IDbConnector { Task Connect(string dbPath); + void BindSimilarityScoreFunction(IDbConnection connection); } } diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs new file mode 100644 index 000000000..5e07bac31 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SQLite; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Server.Implementations.Persistence +{ + /// + /// Class SQLiteExtensions + /// + public static class SqliteExtensions + { + /// + /// Connects to db. + /// + /// The db path. + /// The logger. + /// Task{IDbConnection}. + /// dbPath + public static async Task ConnectToDb(string dbPath, ILogger logger) + { + if (string.IsNullOrEmpty(dbPath)) + { + throw new ArgumentNullException("dbPath"); + } + + logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath); + + var connectionstr = new SQLiteConnectionStringBuilder + { + PageSize = 4096, + CacheSize = 2000, + SyncMode = SynchronizationModes.Normal, + DataSource = dbPath, + JournalMode = SQLiteJournalModeEnum.Wal + }; + + var connection = new SQLiteConnection(connectionstr.ConnectionString); + + await connection.OpenAsync().ConfigureAwait(false); + + return connection; + } + + public static void BindGetSimilarityScore(IDbConnection connection, ILogger logger) + { + var sqlConnection = (SQLiteConnection) connection; + SimiliarToFunction.Logger = logger; + sqlConnection.BindFunction(new SimiliarToFunction()); + } + + public static void BindFunction(this SQLiteConnection connection, SQLiteFunction function) + { + var attributes = function.GetType().GetCustomAttributes(typeof(SQLiteFunctionAttribute), true).Cast().ToArray(); + if (attributes.Length == 0) + { + throw new InvalidOperationException("SQLiteFunction doesn't have SQLiteFunctionAttribute"); + } + connection.BindFunction(attributes[0], function); + } + } + + [SQLiteFunction(Name = "GetSimilarityScore", Arguments = 12, FuncType = FunctionType.Scalar)] + public class SimiliarToFunction : SQLiteFunction + { + internal static ILogger Logger; + + public override object Invoke(object[] args) + { + var score = 0; + + var inputOfficialRating = args[0] as string; + var rowOfficialRating = args[1] as string; + if (!string.IsNullOrWhiteSpace(inputOfficialRating) && string.Equals(inputOfficialRating, rowOfficialRating)) + { + score += 10; + } + + long? inputYear = args[2] == null ? (long?)null : (long)args[2]; + long? rowYear = args[3] == null ? (long?)null : (long)args[3]; + + if (inputYear.HasValue && rowYear.HasValue) + { + var diff = Math.Abs(inputYear.Value - rowYear.Value); + + // Add if they came out within the same decade + if (diff < 10) + { + score += 2; + } + + // And more if within five years + if (diff < 5) + { + score += 2; + } + } + + // genres + score += GetListScore(args, 4, 5); + + // tags + score += GetListScore(args, 6, 7); + + // keywords + score += GetListScore(args, 8, 9); + + // studios + score += GetListScore(args, 10, 11, 3); + + + // TODO: People + // var item2PeopleNames = allPeople.Where(i => i.ItemId == item2.Id) + //.Select(i => i.Name) + //.Where(i => !string.IsNullOrWhiteSpace(i)) + //.DistinctNames() + //.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + + // points += item1People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => + // { + // if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) + // { + // return 5; + // } + // if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) + // { + // return 3; + // } + // if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase)) + // { + // return 3; + // } + // if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) + // { + // return 3; + // } + // if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) + // { + // return 2; + // } + + // return 1; + // }); + + // return points; + + //Logger.Debug("Returning score {0}", score); + return score; + } + + private int GetListScore(object[] args, int index1, int index2, int value = 10) + { + var score = 0; + + var inputGenres = args[index1] as string; + var rowGenres = args[index2] as string; + var inputGenreList = string.IsNullOrWhiteSpace(inputGenres) ? new string[] { } : inputGenres.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + var rowGenresList = string.IsNullOrWhiteSpace(rowGenres) ? new string[] { } : rowGenres.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var genre in inputGenreList) + { + if (rowGenresList.Contains(genre, StringComparer.OrdinalIgnoreCase)) + { + score += value; + } + } + + return score; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 3149352a9..131158dd2 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -15,6 +15,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Runtime.Serialization; +using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; @@ -258,6 +259,8 @@ namespace MediaBrowser.Server.Implementations.Persistence new MediaStreamColumns(_connection, Logger).AddColumns(); DataExtensions.Attach(_connection, Path.Combine(_config.ApplicationPaths.DataPath, "userdata_v2.db"), "UserDataDb"); + + dbConnector.BindSimilarityScoreFunction(_connection); } private readonly string[] _retriveItemColumns = @@ -1575,7 +1578,7 @@ namespace MediaBrowser.Server.Implementations.Persistence return false; } - private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns) + private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns, IDbCommand cmd) { var list = startColumns.ToList(); @@ -1590,6 +1593,45 @@ namespace MediaBrowser.Server.Implementations.Persistence list.Add("UserDataDb.UserData.rating"); } + if (query.SimilarTo != null) + { + var item = query.SimilarTo; + + var builder = new StringBuilder(); + builder.Append("GetSimilarityScore("); + + builder.Append("@ItemOfficialRating,"); + builder.Append("OfficialRating,"); + + builder.Append("@ItemProductionYear,"); + builder.Append("ProductionYear,"); + + builder.Append("@ItemGenres,"); + builder.Append("Genres,"); + + builder.Append("@ItemTags,"); + builder.Append("Tags,"); + + builder.Append("@ItemKeywords,"); + builder.Append("(select group_concat((Select Value from ItemValues where ItemId=Guid and Type=5), '|')),"); + + builder.Append("@ItemStudios,"); + builder.Append("Studios"); + builder.Append(") as SimilarityScore"); + + list.Add(builder.ToString()); + cmd.Parameters.Add(cmd, "@ItemOfficialRating", DbType.String).Value = item.OfficialRating; + cmd.Parameters.Add(cmd, "@ItemProductionYear", DbType.Int32).Value = item.ProductionYear ?? -1; + cmd.Parameters.Add(cmd, "@ItemGenres", DbType.String).Value = string.Join("|", item.Genres.ToArray()); + cmd.Parameters.Add(cmd, "@ItemTags", DbType.String).Value = string.Join("|", item.Tags.ToArray()); + cmd.Parameters.Add(cmd, "@ItemKeywords", DbType.String).Value = string.Join("|", item.Keywords.ToArray()); + cmd.Parameters.Add(cmd, "@ItemStudios", DbType.String).Value = string.Join("|", item.Studios.ToArray()); + + var excludeIds = query.ExcludeItemIds.ToList(); + excludeIds.Add(item.Id.ToString("N")); + query.ExcludeItemIds = excludeIds.ToArray(); + } + return list.ToArray(); } @@ -1616,7 +1658,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns)) + " from TypedBaseItems"; + cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + " from TypedBaseItems"; cmd.CommandText += GetJoinUserDataText(query); if (EnableJoinUserData(query)) @@ -1706,7 +1748,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns)) + " from TypedBaseItems"; + cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + " from TypedBaseItems"; cmd.CommandText += GetJoinUserDataText(query); if (EnableJoinUserData(query)) @@ -1789,6 +1831,15 @@ namespace MediaBrowser.Server.Implementations.Persistence private string GetOrderByText(InternalItemsQuery query) { + if (query.SimilarTo != null) + { + if (query.SortBy == null || query.SortBy.Length == 0) + { + query.SortBy = new[] { "SimilarityScore", "Random" }; + query.SortOrder = SortOrder.Descending; + } + } + if (query.SortBy == null || query.SortBy.Length == 0) { return string.Empty; @@ -1879,7 +1930,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" })) + " from TypedBaseItems"; + cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + " from TypedBaseItems"; cmd.CommandText += GetJoinUserDataText(query); if (EnableJoinUserData(query)) @@ -2022,7 +2073,7 @@ namespace MediaBrowser.Server.Implementations.Persistence using (var cmd = _connection.CreateCommand()) { - cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" })) + " from TypedBaseItems"; + cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + " from TypedBaseItems"; var whereClauses = GetWhereClauses(query, cmd); cmd.CommandText += GetJoinUserDataText(query); @@ -2148,24 +2199,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } else { - if (query.IsMovie.Value) - { - var typeClauses = new List(); - var typeIndex = 0; - foreach (var type in alternateTypes) - { - var paramName = "@AlternateType" + typeIndex.ToString(CultureInfo.InvariantCulture); - typeClauses.Add("Type=" + paramName); - cmd.Parameters.Add(cmd, paramName, DbType.String).Value = type; - typeIndex++; - } - - whereClauses.Add("(IsMovie=@IsMovie OR " + string.Join(" OR ", typeClauses.ToArray()) + ")"); - } - else - { - whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)"); - } + whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)"); } cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie; } diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index b71877e17..45071c9d9 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -82,11 +82,14 @@ + + Native\SqliteExtensions.cs + Properties\SharedVersion.cs - + diff --git a/MediaBrowser.Server.Mono/Native/DbConnector.cs b/MediaBrowser.Server.Mono/Native/DbConnector.cs new file mode 100644 index 000000000..536cd7322 --- /dev/null +++ b/MediaBrowser.Server.Mono/Native/DbConnector.cs @@ -0,0 +1,29 @@ +using System; +using System.Data; +using System.Data.SQLite; +using System.Threading.Tasks; +using MediaBrowser.Model.Logging; +using MediaBrowser.Server.Implementations.Persistence; + +namespace MediaBrowser.Server.Mono.Native +{ + public class DbConnector : IDbConnector + { + private readonly ILogger _logger; + + public DbConnector(ILogger logger) + { + _logger = logger; + } + + public void BindSimilarityScoreFunction(IDbConnection connection) + { + SqliteExtensions.BindGetSimilarityScore(connection, _logger); + } + + public Task Connect(string dbPath) + { + return SqliteExtensions.ConnectToDb(dbPath, _logger); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Server.Mono/Native/SqliteExtensions.cs b/MediaBrowser.Server.Mono/Native/SqliteExtensions.cs deleted file mode 100644 index ca2327282..000000000 --- a/MediaBrowser.Server.Mono/Native/SqliteExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Data; -using System.Data.SQLite; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Implementations.Persistence; - -namespace MediaBrowser.Server.Mono.Native -{ - /// - /// Class SQLiteExtensions - /// - static class SqliteExtensions - { - /// - /// Connects to db. - /// - /// The db path. - /// The logger. - /// Task{IDbConnection}. - /// dbPath - public static async Task ConnectToDb(string dbPath, ILogger logger) - { - if (string.IsNullOrEmpty(dbPath)) - { - throw new ArgumentNullException("dbPath"); - } - - logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath); - - var connectionstr = new SQLiteConnectionStringBuilder - { - PageSize = 4096, - CacheSize = 2000, - SyncMode = SynchronizationModes.Normal, - DataSource = dbPath, - JournalMode = SQLiteJournalModeEnum.Wal - }; - - var connection = new SQLiteConnection(connectionstr.ConnectionString); - - await connection.OpenAsync().ConfigureAwait(false); - - return connection; - } - } - - public class DbConnector : IDbConnector - { - private readonly ILogger _logger; - - public DbConnector(ILogger logger) - { - _logger = logger; - } - - public Task Connect(string dbPath) - { - return SqliteExtensions.ConnectToDb(dbPath, _logger); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 366d4b608..35660b2b1 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -97,6 +97,9 @@ + + Native\SqliteExtensions.cs + Properties\SharedVersion.cs @@ -114,7 +117,7 @@ - + diff --git a/MediaBrowser.ServerApplication/Native/DbConnector.cs b/MediaBrowser.ServerApplication/Native/DbConnector.cs new file mode 100644 index 000000000..f93cad62c --- /dev/null +++ b/MediaBrowser.ServerApplication/Native/DbConnector.cs @@ -0,0 +1,38 @@ +using System; +using System.Data; +using System.Data.SQLite; +using System.Threading.Tasks; +using MediaBrowser.Model.Logging; +using MediaBrowser.Server.Implementations.Persistence; + +namespace MediaBrowser.ServerApplication.Native +{ + public class DbConnector : IDbConnector + { + private readonly ILogger _logger; + + public DbConnector(ILogger logger) + { + _logger = logger; + } + + public void BindSimilarityScoreFunction(IDbConnection connection) + { + SqliteExtensions.BindGetSimilarityScore(connection, _logger); + } + + public async Task Connect(string dbPath) + { + try + { + return await SqliteExtensions.ConnectToDb(dbPath, _logger).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error opening database {0}", ex, dbPath); + + throw; + } + } + } +} \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/Native/SqliteExtensions.cs b/MediaBrowser.ServerApplication/Native/SqliteExtensions.cs deleted file mode 100644 index bdf5c3323..000000000 --- a/MediaBrowser.ServerApplication/Native/SqliteExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Data; -using System.Data.SQLite; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Implementations.Persistence; - -namespace MediaBrowser.ServerApplication.Native -{ - /// - /// Class SQLiteExtensions - /// - static class SqliteExtensions - { - /// - /// Connects to db. - /// - /// The db path. - /// The logger. - /// Task{IDbConnection}. - /// dbPath - public static async Task ConnectToDb(string dbPath, ILogger logger) - { - if (string.IsNullOrEmpty(dbPath)) - { - throw new ArgumentNullException("dbPath"); - } - - logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath); - - var connectionstr = new SQLiteConnectionStringBuilder - { - PageSize = 4096, - CacheSize = 2000, - SyncMode = SynchronizationModes.Normal, - DataSource = dbPath, - JournalMode = SQLiteJournalModeEnum.Wal - }; - - var connection = new SQLiteConnection(connectionstr.ConnectionString); - - await connection.OpenAsync().ConfigureAwait(false); - - return connection; - } - } - - public class DbConnector : IDbConnector - { - private readonly ILogger _logger; - - public DbConnector(ILogger logger) - { - _logger = logger; - } - - public async Task Connect(string dbPath) - { - try - { - return await SqliteExtensions.ConnectToDb(dbPath, _logger).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error opening database {0}", ex, dbPath); - - throw; - } - } - } -} \ No newline at end of file From 669af870148a92db4a851868781e2e3af4111a26 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 1 Jun 2016 02:01:43 -0400 Subject: [PATCH 4/7] support querying more fields --- MediaBrowser.Controller/Entities/Folder.cs | 16 -------------- .../Persistence/SqliteItemRepository.cs | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index cbbd0ad68..0d307c35b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -793,11 +793,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.Metascore"); return true; } - if (query.SortBy.Contains(ItemSortBy.OfficialRating, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.OfficialRating"); - return true; - } if (query.SortBy.Contains(ItemSortBy.Players, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.Players"); @@ -813,11 +808,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName"); return true; } - if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio"); - return true; - } if (query.SortBy.Contains(ItemSortBy.VideoBitRate, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.VideoBitRate"); @@ -962,12 +952,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.OfficialRatings.Length > 0) - { - Logger.Debug("Query requires post-filtering due to OfficialRatings"); - return true; - } - if (query.IsMissing.HasValue) { Logger.Debug("Query requires post-filtering due to IsMissing"); diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 131158dd2..9b815bc0b 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -1913,6 +1913,14 @@ namespace MediaBrowser.Server.Implementations.Persistence { return new Tuple("(select value from itemvalues where ItemId=Guid and Type=1 LIMIT 1)", false); } + if (string.Equals(name, ItemSortBy.OfficialRating, StringComparison.OrdinalIgnoreCase)) + { + return new Tuple("ParentalRatingValue", false); + } + if (string.Equals(name, ItemSortBy.Studio, StringComparison.OrdinalIgnoreCase)) + { + return new Tuple("(select value from itemvalues where ItemId=Guid and Type=3 LIMIT 1)", false); + } return new Tuple(name, false); } @@ -2573,6 +2581,20 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add(clause); } + if (query.OfficialRatings.Length > 0) + { + var clauses = new List(); + var index = 0; + foreach (var item in query.OfficialRatings) + { + clauses.Add("OfficialRating=@OfficialRating" + index); + cmd.Parameters.Add(cmd, "@OfficialRating" + index, DbType.String).Value = item; + index++; + } + var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; + whereClauses.Add(clause); + } + if (query.MinParentalRating.HasValue) { whereClauses.Add("InheritedParentalRatingValue<=@MinParentalRating"); From 900cf09e0329af915b36b203b4a1e03477507e04 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 1 Jun 2016 02:20:21 -0400 Subject: [PATCH 5/7] use similar query for intros --- .../Intros/DefaultIntroProvider.cs | 182 +----------------- .../Persistence/SqliteItemRepository.cs | 14 +- 2 files changed, 21 insertions(+), 175 deletions(-) diff --git a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs index df128a90b..7c7a535cd 100644 --- a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs +++ b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs @@ -63,16 +63,8 @@ namespace MediaBrowser.Server.Implementations.Intros ? null : _localization.GetRatingLevel(item.OfficialRating); - var random = new Random(Environment.TickCount + Guid.NewGuid().GetHashCode()); - var candidates = new List(); - var itemPeople = _libraryManager.GetPeople(item); - var allPeople = _libraryManager.GetPeople(new InternalPeopleQuery - { - AppearsInItemId = item.Id - }); - var trailerTypes = new List(); if (config.EnableIntrosFromMoviesInLibrary) @@ -105,26 +97,25 @@ namespace MediaBrowser.Server.Implementations.Intros var trailerResult = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Trailer).Name }, - TrailerTypes = trailerTypes.ToArray() + TrailerTypes = trailerTypes.ToArray(), + SimilarTo = item, + IsPlayed = config.EnableIntrosForWatchedContent ? (bool?) null : false, + MaxParentalRating = config.EnableIntrosParentalControl ? ratingLevel : null, + Limit = config.TrailerLimit }); candidates.AddRange(trailerResult.Select(i => new ItemWithTrailer { Item = i, Type = i.SourceType == SourceType.Channel ? ItemWithTrailerType.ChannelTrailer : ItemWithTrailerType.ItemWithTrailer, - User = user, - WatchingItem = item, - WatchingItemPeople = itemPeople, - AllPeople = allPeople, - Random = random, LibraryManager = _libraryManager })); } - return GetResult(item, candidates, config, ratingLevel); + return GetResult(item, candidates, config); } - private IEnumerable GetResult(BaseItem item, IEnumerable candidates, CinemaModeConfiguration config, int? ratingLevel) + private IEnumerable GetResult(BaseItem item, IEnumerable candidates, CinemaModeConfiguration config) { var customIntros = !string.IsNullOrWhiteSpace(config.CustomIntroPath) ? GetCustomIntros(config) : @@ -134,48 +125,12 @@ namespace MediaBrowser.Server.Implementations.Intros GetMediaInfoIntros(config, item) : new List(); - var trailerLimit = config.TrailerLimit; - // Avoid implicitly captured closure - return candidates.Where(i => - { - if (config.EnableIntrosParentalControl && !FilterByParentalRating(ratingLevel, i.Item)) - { - return false; - } - - if (!config.EnableIntrosForWatchedContent && i.IsPlayed) - { - return false; - } - return !IsDuplicate(item, i.Item); - }) - .OrderByDescending(i => i.Score) - .ThenBy(i => Guid.NewGuid()) - .ThenByDescending(i => i.IsPlayed ? 0 : 1) - .Select(i => i.IntroInfo) - .Take(trailerLimit) + return candidates.Select(i => i.IntroInfo) .Concat(customIntros.Take(1)) .Concat(mediaInfoIntros); } - private bool IsDuplicate(BaseItem playingContent, BaseItem test) - { - var id = playingContent.GetProviderId(MetadataProviders.Imdb); - if (!string.IsNullOrWhiteSpace(id) && string.Equals(id, test.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - id = playingContent.GetProviderId(MetadataProviders.Tmdb); - if (!string.IsNullOrWhiteSpace(id) && string.Equals(id, test.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - return false; - } - private CinemaModeConfiguration GetOptions() { return _serverConfig.GetConfiguration("cinemamode"); @@ -346,96 +301,6 @@ namespace MediaBrowser.Server.Implementations.Intros return list.Distinct(StringComparer.OrdinalIgnoreCase); } - private bool FilterByParentalRating(int? ratingLevel, BaseItem item) - { - // Only content rated same or lower - if (ratingLevel.HasValue) - { - var level = string.IsNullOrWhiteSpace(item.OfficialRating) - ? (int?)null - : _localization.GetRatingLevel(item.OfficialRating); - - return level.HasValue && level.Value <= ratingLevel.Value; - } - - return true; - } - - internal static int GetSimiliarityScore(BaseItem item1, List item1People, List allPeople, BaseItem item2, Random random, ILibraryManager libraryManager) - { - var points = 0; - - if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase)) - { - points += 10; - } - - // Find common genres - points += item1.Genres.Where(i => item2.Genres.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); - - // Find common tags - points += GetTags(item1).Where(i => GetTags(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); - - // Find common keywords - points += GetKeywords(item1).Where(i => GetKeywords(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); - - // Find common studios - points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 5); - - var item2PeopleNames = allPeople.Where(i => i.ItemId == item2.Id) - .Select(i => i.Name) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .DistinctNames() - .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - - points += item1People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => - { - if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) - { - return 5; - } - if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - - return 1; - }); - - // Add some randomization so that you're not always seeing the same ones for a given movie - points += random.Next(0, 50); - - return points; - } - - private static IEnumerable GetTags(BaseItem item) - { - var hasTags = item as IHasTags; - if (hasTags != null) - { - return hasTags.Tags; - } - - return new List(); - } - - private static IEnumerable GetKeywords(BaseItem item) - { - return item.Keywords; - } - public IEnumerable GetAllIntroFiles() { return GetCustomIntroFiles(GetOptions(), true, true); @@ -455,39 +320,8 @@ namespace MediaBrowser.Server.Implementations.Intros { internal BaseItem Item; internal ItemWithTrailerType Type; - internal User User; - internal BaseItem WatchingItem; - internal List WatchingItemPeople; - internal List AllPeople; - internal Random Random; internal ILibraryManager LibraryManager; - private bool? _isPlayed; - public bool IsPlayed - { - get - { - if (!_isPlayed.HasValue) - { - _isPlayed = Item.IsPlayed(User); - } - return _isPlayed.Value; - } - } - - private int? _score; - public int Score - { - get - { - if (!_score.HasValue) - { - _score = GetSimiliarityScore(WatchingItem, WatchingItemPeople, AllPeople, Item, Random, LibraryManager); - } - return _score.Value; - } - } - public IntroInfo IntroInfo { get diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 9b815bc0b..c00c4bf30 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -1526,6 +1526,11 @@ namespace MediaBrowser.Server.Implementations.Persistence return false; } + if (query.SimilarTo != null) + { + return true; + } + if (query.SortBy != null && query.SortBy.Length > 0) { if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)) @@ -1835,7 +1840,14 @@ namespace MediaBrowser.Server.Implementations.Persistence { if (query.SortBy == null || query.SortBy.Length == 0) { - query.SortBy = new[] { "SimilarityScore", "Random" }; + if (query.User != null) + { + query.SortBy = new[] { "SimilarityScore", "IsUnplayed", "Random" }; + } + else + { + query.SortBy = new[] { "SimilarityScore", "Random" }; + } query.SortOrder = SortOrder.Descending; } } From df3cd3e2f688c8397f2ed07eff36d4894bc0804e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 1 Jun 2016 11:21:22 -0400 Subject: [PATCH 6/7] updated nuget --- MediaBrowser.Providers/Movies/MovieExternalIds.cs | 7 +++++++ .../MediaBrowser.Server.Implementations.csproj | 7 ++++--- MediaBrowser.Server.Implementations/packages.config | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs index 3bceb976e..a6b7bde6f 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/MediaBrowser.Providers/Movies/MovieExternalIds.cs @@ -148,6 +148,13 @@ namespace MediaBrowser.Providers.Movies public bool Supports(IHasProviderIds item) { + // Supports images for tv movies + var tvProgram = item as LiveTvProgram; + if (tvProgram != null && tvProgram.IsMovie) + { + return true; + } + return item is Movie || item is MusicVideo || item is Series || item is Episode || item is Trailer; } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 385a94aa5..0df6c338c 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -46,11 +46,12 @@ ..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll - ..\packages\Emby.XmlTv.1.0.0.49\lib\net45\Emby.XmlTv.dll + ..\packages\Emby.XmlTv.1.0.0.50\lib\net45\Emby.XmlTv.dll True - - ..\packages\ini-parser.2.2.4\lib\net20\INIFileParser.dll + + ..\packages\ini-parser.2.3.0\lib\net20\INIFileParser.dll + True ..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 9b050672c..9d1aa2c33 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,8 +1,8 @@  - - + + From a8c11c0fd2c6517205e3b47a7ec63b825c1b85c8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 1 Jun 2016 11:54:11 -0400 Subject: [PATCH 7/7] support trailers for episodes --- MediaBrowser.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 56d3bd4de..032f8dbb7 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2309,7 +2309,7 @@ namespace MediaBrowser.Server.Implementations.Library public IEnumerable