From 85c508bcd1e09355b9881bdc243c511e81bc621c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 2 Jun 2016 12:17:35 -0400 Subject: [PATCH 01/25] update provider setting --- .../Persistence/SqliteItemRepository.cs | 5 ++--- MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 13f7a0d379..494e1b12f3 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _updateInheritedRatingCommand; private IDbCommand _updateInheritedTagsCommand; - public const int LatestSchemaVersion = 83; + public const int LatestSchemaVersion = 84; /// /// Initializes a new instance of the class. @@ -132,7 +132,6 @@ namespace MediaBrowser.Server.Implementations.Persistence string[] queries = { "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)", - "create index if not exists idx_TypedBaseItems on TypedBaseItems(guid)", "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)", "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)", @@ -146,7 +145,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT)", "create index if not exists idx_ItemValues on ItemValues(ItemId)", - "create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT)", + "create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT, PRIMARY KEY (ItemId, Name))", "create index if not exists Idx_ProviderIds on ProviderIds(ItemId)", "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index d528686db8..1d860ed29f 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -137,9 +137,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest From ae168bc56394057c42fa50a36e54e52617f84244 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 2 Jun 2016 13:43:29 -0400 Subject: [PATCH 02/25] update tag saving --- MediaBrowser.Api/FilterService.cs | 2 +- MediaBrowser.Api/ItemUpdateService.cs | 6 +----- MediaBrowser.Api/SimilarItemsHelper.cs | 8 +------- .../UserLibrary/BaseItemsByNameService.cs | 7 +------ .../Entities/Audio/Audio.cs | 1 - MediaBrowser.Controller/Entities/BaseItem.cs | 11 +++-------- MediaBrowser.Controller/Entities/Book.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/Entities/Game.cs | 2 +- MediaBrowser.Controller/Entities/Photo.cs | 2 +- MediaBrowser.Controller/Entities/Studio.cs | 2 +- .../Entities/{IHasTags.cs => TagExtensions.cs} | 15 +-------------- .../Entities/UserViewBuilder.cs | 7 +------ MediaBrowser.Controller/Entities/Video.cs | 1 - .../MediaBrowser.Controller.csproj | 2 +- .../Providers/BaseItemXmlParser.cs | 8 ++------ .../Savers/XmlSaverHelpers.cs | 18 +++++++----------- .../Manager/ProviderUtils.cs | 10 ++-------- .../Dto/DtoService.cs | 11 +---------- .../Parsers/BaseNfoParser.cs | 6 +----- .../Savers/BaseNfoSaver.cs | 18 +++++++----------- 21 files changed, 35 insertions(+), 106 deletions(-) rename MediaBrowser.Controller/Entities/{IHasTags.cs => TagExtensions.cs} (54%) diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs index c4419531c5..b3b75359aa 100644 --- a/MediaBrowser.Api/FilterService.cs +++ b/MediaBrowser.Api/FilterService.cs @@ -80,7 +80,7 @@ namespace MediaBrowser.Api .OrderBy(i => i) .ToArray(); - result.Tags = items.OfType() + result.Tags = items .SelectMany(i => i.Tags) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(i => i) diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 36e8a504c8..6cb23a140d 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -280,11 +280,7 @@ namespace MediaBrowser.Api episode.AbsoluteEpisodeNumber = request.AbsoluteEpisodeNumber; } - var hasTags = item as IHasTags; - if (hasTags != null) - { - hasTags.Tags = request.Tags; - } + item.Tags = request.Tags; var hasTaglines = item as IHasTaglines; if (hasTaglines != null) diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 76bc16a96f..537ffc913c 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -116,13 +116,7 @@ namespace MediaBrowser.Api private static IEnumerable GetTags(BaseItem item) { - var hasTags = item as IHasTags; - if (hasTags != null) - { - return hasTags.Tags; - } - - return new List(); + return item.Tags; } private static IEnumerable GetKeywords(BaseItem item) diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 565bed0531..18dec3253a 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -333,12 +333,7 @@ namespace MediaBrowser.Api.UserLibrary var tags = request.GetTags(); if (tags.Length > 0) { - var hasTags = i as IHasTags; - if (hasTags == null) - { - return false; - } - if (!tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) + if (!tags.Any(v => i.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) { return false; } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index c34a884ff5..06710b0301 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities.Audio IHasArtist, IHasMusicGenres, IHasLookupInfo, - IHasTags, IHasMediaSources, IThemeMedia, IArchivable diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 5101a9f289..a45a462df4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1397,15 +1397,10 @@ namespace MediaBrowser.Controller.Entities private bool IsVisibleViaTags(User user) { - var hasTags = this as IHasTags; - - if (hasTags != null) + var policy = user.Policy; + if (policy.BlockedTags.Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) { - var policy = user.Policy; - if (policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) - { - return false; - } + return false; } return true; diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index 1c86a53f0d..96fad670e1 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities { - public class Book : BaseItem, IHasTags, IHasLookupInfo, IHasSeries + public class Book : BaseItem, IHasLookupInfo, IHasSeries { [IgnoreDataMember] public override string MediaType diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index a4cbd34b2c..ba94c99d2a 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class Folder /// - public class Folder : BaseItem, IHasThemeMedia, IHasTags + public class Folder : BaseItem, IHasThemeMedia { public static IUserManager UserManager { get; set; } public static IUserViewManager UserViewManager { get; set; } diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index 9ed2400469..317c71529e 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -7,7 +7,7 @@ using System.Linq; namespace MediaBrowser.Controller.Entities { - public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo + public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo { public List ThemeSongIds { get; set; } public List ThemeVideoIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 3358ccc6fc..de756563d8 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -5,7 +5,7 @@ using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities { - public class Photo : BaseItem, IHasTags, IHasTaglines + public class Photo : BaseItem, IHasTaglines { public List Taglines { get; set; } diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 48ca7bbccc..e46978af3e 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class Studio /// - public class Studio : BaseItem, IItemByName, IHasTags + public class Studio : BaseItem, IItemByName { public override List GetUserDataKeys() { diff --git a/MediaBrowser.Controller/Entities/IHasTags.cs b/MediaBrowser.Controller/Entities/TagExtensions.cs similarity index 54% rename from MediaBrowser.Controller/Entities/IHasTags.cs rename to MediaBrowser.Controller/Entities/TagExtensions.cs index 45a56009d0..0e1df72cd0 100644 --- a/MediaBrowser.Controller/Entities/IHasTags.cs +++ b/MediaBrowser.Controller/Entities/TagExtensions.cs @@ -1,24 +1,11 @@ using System; -using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Controller.Entities { - /// - /// Interface IHasTags - /// - public interface IHasTags - { - /// - /// Gets or sets the tags. - /// - /// The tags. - List Tags { get; set; } - } - public static class TagExtensions { - public static void AddTag(this IHasTags item, string name) + public static void AddTag(this BaseItem item, string name) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 655dff06aa..a7b23112e6 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1646,12 +1646,7 @@ namespace MediaBrowser.Controller.Entities var tags = query.Tags; if (tags.Length > 0) { - var hasTags = item as IHasTags; - if (hasTags == null) - { - return false; - } - if (!tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) + if (!tags.Any(v => item.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) { return false; } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 6a9d7cb511..2502033c86 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -21,7 +21,6 @@ namespace MediaBrowser.Controller.Entities /// public class Video : BaseItem, IHasAspectRatio, - IHasTags, ISupportsPlaceHolders, IHasMediaSources, IHasShortOverview, diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index b15bb94c78..4cfdc641c0 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -154,7 +154,6 @@ - @@ -177,6 +176,7 @@ + diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index aaa440060c..a783910e34 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -803,11 +803,7 @@ namespace MediaBrowser.Controller.Providers { using (var subtree = reader.ReadSubtree()) { - var hasTags = item as IHasTags; - if (hasTags != null) - { - FetchFromTagsNode(subtree, hasTags); - } + FetchFromTagsNode(subtree, item); } break; } @@ -1066,7 +1062,7 @@ namespace MediaBrowser.Controller.Providers } } - private void FetchFromTagsNode(XmlReader reader, IHasTags item) + private void FetchFromTagsNode(XmlReader reader, BaseItem item) { reader.MoveToContent(); diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index ca19b403ab..2b3f53aeb5 100644 --- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs @@ -593,20 +593,16 @@ namespace MediaBrowser.LocalMetadata.Savers builder.Append(""); } - var hasTags = item as IHasTags; - if (hasTags != null) + if (item.Tags.Count > 0) { - if (hasTags.Tags.Count > 0) + builder.Append(""); + + foreach (var tag in item.Tags) { - builder.Append(""); - - foreach (var tag in hasTags.Tags) - { - builder.Append("" + SecurityElement.Escape(tag) + ""); - } - - builder.Append(""); + builder.Append("" + SecurityElement.Escape(tag) + ""); } + + builder.Append(""); } if (item.Keywords.Count > 0) diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index a6f02f3f74..5f23cf69c9 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -151,15 +151,9 @@ namespace MediaBrowser.Providers.Manager if (!lockedFields.Contains(MetadataFields.Tags)) { - var sourceHasTags = source as IHasTags; - var targetHasTags = target as IHasTags; - - if (sourceHasTags != null && targetHasTags != null) + if (replaceData || target.Tags.Count == 0) { - if (replaceData || targetHasTags.Tags.Count == 0) - { - targetHasTags.Tags = sourceHasTags.Tags; - } + target.Tags = source.Tags; } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index bfcdb2a264..5588405e36 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -969,16 +969,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (fields.Contains(ItemFields.Tags)) { - var hasTags = item as IHasTags; - if (hasTags != null) - { - dto.Tags = hasTags.Tags; - } - - if (dto.Tags == null) - { - dto.Tags = new List(); - } + dto.Tags = item.Tags; } if (fields.Contains(ItemFields.Keywords)) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index ad1c6802d2..2e34135a62 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -919,11 +919,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { - var hasTags = item as IHasTags; - if (hasTags != null) - { - hasTags.AddTag(val); - } + item.AddTag(val); } break; } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 5bb9577ff3..42f0a3364f 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -736,19 +736,15 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("studio", studio); } - var hasTags = item as IHasTags; - if (hasTags != null) + foreach (var tag in item.Tags) { - foreach (var tag in hasTags.Tags) + if (item is MusicAlbum || item is MusicArtist) { - if (item is MusicAlbum || item is MusicArtist) - { - writer.WriteElementString("style", tag); - } - else - { - writer.WriteElementString("tag", tag); - } + writer.WriteElementString("style", tag); + } + else + { + writer.WriteElementString("tag", tag); } } From 05acd63f8f0019e46ea57ab895bf54372c14161b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 2 Jun 2016 15:32:15 -0400 Subject: [PATCH 03/25] add people to similar queries --- .../Persistence/SqliteExtensions.cs | 59 ++++++++----------- .../Persistence/SqliteItemRepository.cs | 6 +- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs index 5e07bac310..b819fb9790 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs @@ -5,6 +5,8 @@ using System.Data.SQLite; using System.Linq; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; namespace MediaBrowser.Server.Implementations.Persistence @@ -48,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.Persistence public static void BindGetSimilarityScore(IDbConnection connection, ILogger logger) { - var sqlConnection = (SQLiteConnection) connection; + var sqlConnection = (SQLiteConnection)connection; SimiliarToFunction.Logger = logger; sqlConnection.BindFunction(new SimiliarToFunction()); } @@ -64,11 +66,23 @@ namespace MediaBrowser.Server.Implementations.Persistence } } - [SQLiteFunction(Name = "GetSimilarityScore", Arguments = 12, FuncType = FunctionType.Scalar)] + [SQLiteFunction(Name = "GetSimilarityScore", Arguments = 13, FuncType = FunctionType.Scalar)] public class SimiliarToFunction : SQLiteFunction { internal static ILogger Logger; + private readonly Dictionary _personTypeScores = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { PersonType.Actor, 3}, + { PersonType.Director, 5}, + { PersonType.Composer, 2}, + { PersonType.GuestStar, 3}, + { PersonType.Writer, 2}, + { PersonType.Conductor, 2}, + { PersonType.Producer, 2}, + { PersonType.Lyricist, 2} + }; + public override object Invoke(object[] args) { var score = 0; @@ -112,41 +126,14 @@ namespace MediaBrowser.Server.Implementations.Persistence // studios score += GetListScore(args, 10, 11, 3); + var rowPeopleNamesText = (args[12] as string) ?? string.Empty; + var rowPeopleNames = rowPeopleNamesText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - // 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; + foreach (var name in rowPeopleNames) + { + // TODO: Send along person types + score += 3; + } //Logger.Debug("Returning score {0}", score); return score; diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 494e1b12f3..bf50f9a0e4 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -1638,7 +1638,10 @@ namespace MediaBrowser.Server.Implementations.Persistence builder.Append("(select group_concat((Select Value from ItemValues where ItemId=Guid and Type=5), '|')),"); builder.Append("@ItemStudios,"); - builder.Append("Studios"); + builder.Append("Studios,"); + + builder.Append("(select group_concat((Select Name from People where ItemId=Guid and Name in (Select Name from People where ItemId=@SimilarItemId)), '|'))"); + builder.Append(") as SimilarityScore"); list.Add(builder.ToString()); @@ -1648,6 +1651,7 @@ namespace MediaBrowser.Server.Implementations.Persistence 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()); + cmd.Parameters.Add(cmd, "@SimilarItemId", DbType.Guid).Value = item.Id; var excludeIds = query.ExcludeItemIds.ToList(); excludeIds.Add(item.Id.ToString("N")); From 53afb1e1e54a383f9fdda9b9c5fb21a6af50962e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Jun 2016 12:24:04 -0400 Subject: [PATCH 04/25] add images table --- MediaBrowser.Api/StartupWizardService.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 6 - .../Configuration/UserConfiguration.cs | 3 +- MediaBrowser.Model/Entities/ImageType.cs | 24 ++-- .../FileOrganization/EpisodeFileOrganizer.cs | 7 +- .../Persistence/SqliteExtensions.cs | 43 +++--- .../Persistence/SqliteItemRepository.cs | 132 +++++++++++++++--- 7 files changed, 150 insertions(+), 67 deletions(-) diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 8bb840697e..0a8d39ee5c 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -113,7 +113,7 @@ namespace MediaBrowser.Api config.EnableCustomPathSubFolders = true; config.EnableStandaloneMusicKeys = true; config.EnableCaseSensitiveItemIds = true; - config.SchemaVersion = 79; + config.SchemaVersion = 87; } public void Post(UpdateStartupConfiguration request) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index ba94c99d2a..2cec15d511 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -889,12 +889,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.ImageTypes.Length > 0) - { - Logger.Debug("Query requires post-filtering due to ImageTypes"); - return true; - } - // Apply studio filter if (query.StudioIds.Length > 0) { diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 6195e3e70f..69dc23b21c 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -48,7 +48,8 @@ namespace MediaBrowser.Model.Configuration public bool RememberAudioSelections { get; set; } public bool RememberSubtitleSelections { get; set; } public bool EnableNextEpisodeAutoPlay { get; set; } - + public bool DisplayFoldersView { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Model/Entities/ImageType.cs b/MediaBrowser.Model/Entities/ImageType.cs index 18097abb44..6e0ba717f0 100644 --- a/MediaBrowser.Model/Entities/ImageType.cs +++ b/MediaBrowser.Model/Entities/ImageType.cs @@ -9,50 +9,50 @@ namespace MediaBrowser.Model.Entities /// /// The primary /// - Primary, + Primary = 0, /// /// The art /// - Art, + Art = 1, /// /// The backdrop /// - Backdrop, + Backdrop = 2, /// /// The banner /// - Banner, + Banner = 3, /// /// The logo /// - Logo, + Logo = 4, /// /// The thumb /// - Thumb, + Thumb = 5, /// /// The disc /// - Disc, + Disc = 6, /// /// The box /// - Box, + Box = 7, /// /// The screenshot /// - Screenshot, + Screenshot = 8, /// /// The menu /// - Menu, + Menu = 9, /// /// The chapter image /// - Chapter, + Chapter = 10, /// /// The box rear /// - BoxRear + BoxRear = 11 } } diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 83801b3e76..2109f8d59e 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -562,9 +562,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery { IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true - }).Cast() - .FirstOrDefault(i => string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase)); + Recursive = true, + Name = info.ItemName + + }).Cast().FirstOrDefault(); } } diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs index b819fb9790..519731a5cc 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } } - [SQLiteFunction(Name = "GetSimilarityScore", Arguments = 13, FuncType = FunctionType.Scalar)] + [SQLiteFunction(Name = "GetSimilarityScore", Arguments = 6, FuncType = FunctionType.Scalar)] public class SimiliarToFunction : SQLiteFunction { internal static ILogger Logger; @@ -87,19 +87,17 @@ namespace MediaBrowser.Server.Implementations.Persistence { var score = 0; - var inputOfficialRating = args[0] as string; - var rowOfficialRating = args[1] as string; - if (!string.IsNullOrWhiteSpace(inputOfficialRating) && string.Equals(inputOfficialRating, rowOfficialRating)) + // Official rating equals + if ((long)args[0] == 1) { 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) + // Year difference + long? yearDifference = args[1] == null ? (long?)null : (long)args[1]; + if (yearDifference.HasValue) { - var diff = Math.Abs(inputYear.Value - rowYear.Value); + var diff = Math.Abs(yearDifference.Value); // Add if they came out within the same decade if (diff < 10) @@ -115,25 +113,28 @@ namespace MediaBrowser.Server.Implementations.Persistence } // genres - score += GetListScore(args, 4, 5); + score += Convert.ToInt32((long)args[2]) * 10; // tags - score += GetListScore(args, 6, 7); + score += Convert.ToInt32((long)args[3]) * 10; - // keywords - score += GetListScore(args, 8, 9); + // # of common keywords + score += Convert.ToInt32((long)args[4]) *10; + + // # of common studios + score += Convert.ToInt32((long)args[5]) * 3; // studios - score += GetListScore(args, 10, 11, 3); + //score += GetListScore(args, 7, 8, 3); - var rowPeopleNamesText = (args[12] as string) ?? string.Empty; - var rowPeopleNames = rowPeopleNamesText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + //var rowPeopleNamesText = (args[12] as string) ?? string.Empty; + //var rowPeopleNames = rowPeopleNamesText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var name in rowPeopleNames) - { - // TODO: Send along person types - score += 3; - } + //foreach (var name in rowPeopleNames) + //{ + // // TODO: Send along person types + // score += 3; + //} //Logger.Debug("Returning score {0}", score); return score; diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index bf50f9a0e4..9f827c553a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -88,10 +88,13 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _deleteProviderIdsCommand; private IDbCommand _saveProviderIdsCommand; + private IDbCommand _deleteImagesCommand; + private IDbCommand _saveImagesCommand; + private IDbCommand _updateInheritedRatingCommand; private IDbCommand _updateInheritedTagsCommand; - public const int LatestSchemaVersion = 84; + public const int LatestSchemaVersion = 87; /// /// Initializes a new instance of the class. @@ -144,10 +147,14 @@ namespace MediaBrowser.Server.Implementations.Persistence "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT)", "create index if not exists idx_ItemValues on ItemValues(ItemId)", + "create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)", "create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT, PRIMARY KEY (ItemId, Name))", "create index if not exists Idx_ProviderIds on ProviderIds(ItemId)", + "create table if not exists Images (ItemId GUID NOT NULL, Path TEXT NOT NULL, ImageType INT NOT NULL, DateModified DATETIME, IsPlaceHolder BIT NOT NULL, SortOrder INT)", + "create index if not exists idx_Images on Images(ItemId)", + "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)", "create index if not exists idxPeopleItemId on People(ItemId)", "create index if not exists idxPeopleName on People(Name)", @@ -564,6 +571,19 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@Name"); _saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@Value"); + // images + _deleteImagesCommand = _connection.CreateCommand(); + _deleteImagesCommand.CommandText = "delete from Images where ItemId=@Id"; + _deleteImagesCommand.Parameters.Add(_deleteImagesCommand, "@Id"); + + _saveImagesCommand = _connection.CreateCommand(); + _saveImagesCommand.CommandText = "insert into Images (ItemId, ImageType, Path, DateModified, IsPlaceHolder, SortOrder) values (@ItemId, @ImageType, @Path, @DateModified, @IsPlaceHolder, @SortOrder)"; + _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@ItemId"); + _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@ImageType"); + _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@Path"); + _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@DateModified"); + _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@IsPlaceHolder"); + _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@SortOrder"); } /// @@ -878,6 +898,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction); + UpdateImages(item.Id, item.ImageInfos, transaction); UpdateProviderIds(item.Id, item.ProviderIds, transaction); UpdateItemValues(item.Id, GetItemValues(item), transaction); } @@ -1620,37 +1641,33 @@ namespace MediaBrowser.Server.Implementations.Persistence var item = query.SimilarTo; var builder = new StringBuilder(); - builder.Append("GetSimilarityScore("); + builder.Append("("); - builder.Append("@ItemOfficialRating,"); - builder.Append("OfficialRating,"); + builder.Append("((OfficialRating=@ItemOfficialRating) * 10)"); + //builder.Append("+ ((ProductionYear=@ItemProductionYear) * 10)"); - builder.Append("@ItemProductionYear,"); - builder.Append("ProductionYear,"); + builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 10 Then 2 Else 0 End )"); + builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 5 Then 2 Else 0 End )"); - builder.Append("@ItemGenres,"); - builder.Append("Genres,"); + //// genres + builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=2 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=2)) * 10)"); - builder.Append("@ItemTags,"); - builder.Append("Tags,"); + //// tags + builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=4 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=4)) * 10)"); - builder.Append("@ItemKeywords,"); - builder.Append("(select group_concat((Select Value from ItemValues where ItemId=Guid and Type=5), '|')),"); + builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=5 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=5)) * 10)"); - builder.Append("@ItemStudios,"); - builder.Append("Studios,"); + builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=3 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=3)) * 3)"); - builder.Append("(select group_concat((Select Name from People where ItemId=Guid and Name in (Select Name from People where ItemId=@SimilarItemId)), '|'))"); + //builder.Append("+ ((Select count(Name) from People where ItemId=Guid and Name in (select Name from People where ItemId=@SimilarItemId)) * 3)"); + + ////builder.Append("(select group_concat((Select Name from People where ItemId=Guid and Name in (Select Name from People where ItemId=@SimilarItemId)), '|'))"); 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()); + cmd.Parameters.Add(cmd, "@ItemProductionYear", DbType.Int32).Value = item.ProductionYear ?? 0; cmd.Parameters.Add(cmd, "@SimilarItemId", DbType.Guid).Value = item.Id; var excludeIds = query.ExcludeItemIds.ToList(); @@ -1865,7 +1882,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { if (query.User != null) { - query.SortBy = new[] { "SimilarityScore", "IsUnplayed", "Random" }; + query.SortBy = new[] { "SimilarityScore", "IsPlayed", "Random" }; } else { @@ -2478,6 +2495,19 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@NameLessThan", DbType.String).Value = query.NameLessThan.ToLower(); } + if (query.ImageTypes.Length > 0 && _config.Configuration.SchemaVersion >= 87) + { + var requiredImageIndex = 0; + + foreach (var requiredImage in query.ImageTypes) + { + var paramName = "@RequiredImageType" + requiredImageIndex; + whereClauses.Add("(select path from images where ItemId=Guid and ImageType=" + paramName + " limit 1) not null"); + cmd.Parameters.Add(cmd, paramName, DbType.Int32).Value = (int)requiredImage; + requiredImageIndex++; + } + } + if (query.IsLiked.HasValue) { if (query.IsLiked.Value) @@ -2741,8 +2771,13 @@ namespace MediaBrowser.Server.Implementations.Persistence var index = 0; foreach (var pair in query.ExcludeProviderIds) { + if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var paramName = "@ExcludeProviderId" + index; - excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = 'Imdb'), '') <> " + paramName + ")"); + excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")"); cmd.Parameters.Add(cmd, paramName, DbType.String).Value = pair.Value; index++; } @@ -3183,6 +3218,11 @@ namespace MediaBrowser.Server.Implementations.Persistence _deleteProviderIdsCommand.Transaction = transaction; _deleteProviderIdsCommand.ExecuteNonQuery(); + // Delete images + _deleteImagesCommand.GetParameter(0).Value = id; + _deleteImagesCommand.Transaction = transaction; + _deleteImagesCommand.ExecuteNonQuery(); + // Delete the item _deleteItemCommand.GetParameter(0).Value = id; _deleteItemCommand.Transaction = transaction; @@ -3399,6 +3439,52 @@ namespace MediaBrowser.Server.Implementations.Persistence return list; } + private void UpdateImages(Guid itemId, List images, IDbTransaction transaction) + { + if (itemId == Guid.Empty) + { + throw new ArgumentNullException("itemId"); + } + + if (images == null) + { + throw new ArgumentNullException("images"); + } + + CheckDisposed(); + + // First delete + _deleteImagesCommand.GetParameter(0).Value = itemId; + _deleteImagesCommand.Transaction = transaction; + + _deleteImagesCommand.ExecuteNonQuery(); + + var index = 0; + foreach (var image in images) + { + _saveImagesCommand.GetParameter(0).Value = itemId; + _saveImagesCommand.GetParameter(1).Value = image.Type; + _saveImagesCommand.GetParameter(2).Value = image.Path; + + if (image.DateModified == default(DateTime)) + { + _saveImagesCommand.GetParameter(3).Value = null; + } + else + { + _saveImagesCommand.GetParameter(3).Value = image.DateModified; + } + + _saveImagesCommand.GetParameter(4).Value = image.IsPlaceholder; + _saveImagesCommand.GetParameter(5).Value = index; + + _saveImagesCommand.Transaction = transaction; + + _saveImagesCommand.ExecuteNonQuery(); + index++; + } + } + private void UpdateProviderIds(Guid itemId, Dictionary values, IDbTransaction transaction) { if (itemId == Guid.Empty) @@ -3408,7 +3494,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (values == null) { - throw new ArgumentNullException("keys"); + throw new ArgumentNullException("values"); } CheckDisposed(); From 3aea4f7ffcbbbb17d0c9cc07148fdb3a9dad9d5f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Jun 2016 15:32:10 -0400 Subject: [PATCH 05/25] add tv genre configuration options --- MediaBrowser.Api/LiveTv/LiveTvService.cs | 11 +++++++++++ MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 6 ++++++ Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index a59b5f351d..d5290959d5 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -439,6 +439,12 @@ namespace MediaBrowser.Api.LiveTv public string Id { get; set; } } + [Route("/LiveTv/ListingProviders/Default", "GET")] + [Authenticated(AllowBeforeStartupWizard = true)] + public class GetDefaultListingProvider : ListingsProviderInfo, IReturn + { + } + [Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")] [Authenticated(AllowBeforeStartupWizard = true)] public class AddListingProvider : ListingsProviderInfo, IReturn @@ -525,6 +531,11 @@ namespace MediaBrowser.Api.LiveTv _dtoService = dtoService; } + public object Get(GetDefaultListingProvider request) + { + return ToOptimizedResult(new ListingsProviderInfo()); + } + public async Task Get(GetSatChannnelScanResult request) { var result = await _liveTvManager.GetSatChannelScanResult(request, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index e00443d191..9e34ff0426 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -73,9 +73,15 @@ namespace MediaBrowser.Model.LiveTv public string[] EnabledTuners { get; set; } public bool EnableAllTuners { get; set; } + public string[] NewsGenres { get; set; } + public string[] SportsGenres { get; set; } + public string[] KidsGenres { get; set; } public ListingsProviderInfo() { + NewsGenres = new string[] { "news" }; + SportsGenres = new string[] { "sports", "basketball", "baseball", "football" }; + KidsGenres = new string[] { "kids", "family", "children" }; EnabledTuners = new string[] { }; EnableAllTuners = true; } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 9d219806c9..a8ef269322 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.649 + 3.0.650 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 2683c42bc8..2bf899ad60 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.649 + 3.0.650 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index c8f164b383..3fdeeaf538 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.649 + 3.0.650 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + From 1910be09768ebe131f79c12317badadb766c606c Mon Sep 17 00:00:00 2001 From: softworkz Date: Fri, 3 Jun 2016 23:46:39 +0200 Subject: [PATCH 06/25] OmdbProvider: Fix for data fields containing "N/A" --- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 26 +++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 44e250350c..df78fdbe79 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -4,8 +4,10 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Serialization; using System; using System.Globalization; +using System.IO; using System.Linq; using System.Net; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -47,7 +49,16 @@ namespace MediaBrowser.Providers.Omdb }).ConfigureAwait(false)) { - var result = _jsonSerializer.DeserializeFromStream(stream); + string resultString; + + using (var reader = new StreamReader(stream, new UTF8Encoding(false))) + { + resultString = reader.ReadToEnd(); + } + + resultString = resultString.Replace("\"N/A\"", string.Empty); + + var result = _jsonSerializer.DeserializeFromString(resultString); // Only take the name and rating if the user's language is set to english, since Omdb has no localization if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) @@ -84,7 +95,6 @@ namespace MediaBrowser.Providers.Omdb } if (!string.IsNullOrEmpty(result.tomatoConsensus) - && !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase) && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) { hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus); @@ -109,14 +119,12 @@ namespace MediaBrowser.Providers.Omdb item.CommunityRating = imdbRating; } - if (!string.IsNullOrEmpty(result.Website) - && !string.Equals(result.Website, "n/a", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(result.Website)) { item.HomePageUrl = result.Website; } - if (!string.IsNullOrWhiteSpace(result.imdbID) - && !string.Equals(result.imdbID, "n/a", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(result.imdbID)) { item.SetProviderId(MetadataProviders.Imdb, result.imdbID); } @@ -130,8 +138,7 @@ namespace MediaBrowser.Providers.Omdb // Grab series genres because imdb data is better than tvdb. Leave movies alone // But only do it if english is the preferred language because this data will not be localized if (ShouldFetchGenres(item) && - !string.IsNullOrWhiteSpace(result.Genre) && - !string.Equals(result.Genre, "n/a", StringComparison.OrdinalIgnoreCase)) + !string.IsNullOrWhiteSpace(result.Genre)) { item.Genres.Clear(); @@ -156,8 +163,7 @@ namespace MediaBrowser.Providers.Omdb } var hasAwards = item as IHasAwards; - if (hasAwards != null && !string.IsNullOrEmpty(result.Awards) && - !string.Equals(result.Awards, "n/a", StringComparison.OrdinalIgnoreCase)) + if (hasAwards != null && !string.IsNullOrEmpty(result.Awards)) { hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards); } From 0e126533a4c0fb098384608ba740faa9ebbb7807 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 00:42:24 +0200 Subject: [PATCH 07/25] OmdbProvider: Fix for empty json fields --- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index df78fdbe79..39da70210d 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.Omdb resultString = reader.ReadToEnd(); } - resultString = resultString.Replace("\"N/A\"", string.Empty); + resultString = resultString.Replace("\"N/A\"", "\"\""); var result = _jsonSerializer.DeserializeFromString(resultString); From ddb6ea6f0576a6ba9c6050cc0799ac6b0e3c2fa8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Jun 2016 20:15:14 -0400 Subject: [PATCH 08/25] rework user data --- MediaBrowser.Controller/Entities/Folder.cs | 69 +------------ .../Entities/TV/Episode.cs | 8 +- MediaBrowser.Controller/Entities/TV/Season.cs | 49 ++-------- MediaBrowser.Controller/Entities/TV/Series.cs | 45 ++++++--- .../Persistence/IUserDataRepository.cs | 2 + .../Library/UserDataManager.cs | 98 +++++-------------- .../Persistence/SqliteUserDataRepository.cs | 48 +++++++++ .../TV/TVSeriesManager.cs | 40 ++++---- 8 files changed, 138 insertions(+), 221 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 2cec15d511..2e4cf3745e 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -164,49 +164,15 @@ namespace MediaBrowser.Controller.Entities item.DateModified = DateTime.UtcNow; } - AddChildInternal(item.Id); - await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); } - protected void AddChildrenInternal(List children) - { - lock (_childrenSyncLock) - { - var newChildren = ChildIds.ToList(); - newChildren.AddRange(children); - _children = newChildren.ToList(); - } - } - protected void AddChildInternal(Guid child) - { - lock (_childrenSyncLock) - { - var childIds = ChildIds.ToList(); - if (!childIds.Contains(child)) - { - childIds.Add(child); - _children = childIds.ToList(); - } - } - } - - protected void RemoveChildrenInternal(List children) - { - lock (_childrenSyncLock) - { - _children = ChildIds.Except(children).ToList(); - } - } - /// /// Removes the child. /// /// The item. public void RemoveChild(BaseItem item) { - RemoveChildrenInternal(new[] { item.Id }.ToList()); - item.SetParent(null); } @@ -241,33 +207,6 @@ namespace MediaBrowser.Controller.Entities #endregion - /// - /// The children - /// - private IReadOnlyList _children; - /// - /// The _children sync lock - /// - private readonly object _childrenSyncLock = new object(); - /// - /// Gets or sets the actual children. - /// - /// The actual children. - protected virtual IEnumerable ChildIds - { - get - { - lock (_childrenSyncLock) - { - if (_children == null) - { - _children = LoadChildren().ToList(); - } - return _children.ToList(); - } - } - } - /// /// Gets the actual children. /// @@ -277,7 +216,7 @@ namespace MediaBrowser.Controller.Entities { get { - return ChildIds.Select(LibraryManager.GetItemById).Where(i => i != null); + return LoadChildren().Select(LibraryManager.GetItemById).Where(i => i != null); } } @@ -479,8 +418,6 @@ namespace MediaBrowser.Controller.Entities if (actualRemovals.Count > 0) { - RemoveChildrenInternal(actualRemovals.Select(i => i.Id).ToList()); - foreach (var item in actualRemovals) { Logger.Debug("Removed item: " + item.Path); @@ -493,8 +430,6 @@ namespace MediaBrowser.Controller.Entities } await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); - - AddChildrenInternal(newItems.Select(i => i.Id).ToList()); } } @@ -766,7 +701,7 @@ namespace MediaBrowser.Controller.Entities { if (!(this is ICollectionFolder)) { - Logger.Debug("Query requires post-filtering due to LinkedChildren"); + Logger.Debug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name); return true; } } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 7ca09d9b28..2dc459239d 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -108,7 +108,13 @@ namespace MediaBrowser.Controller.Entities.TV var series = Series; if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue) { - list.InsertRange(0, series.GetUserDataKeys().Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); + var seriesUserDataKeys = series.GetUserDataKeys(); + var take = seriesUserDataKeys.Count; + if (seriesUserDataKeys.Count > 1) + { + take--; + } + list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); } return list; diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 7fa1b55dea..f07d4be13e 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -196,52 +196,17 @@ namespace MediaBrowser.Controller.Entities.TV { var config = user.Configuration; - return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); + return GetEpisodes(Series, user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } - public IEnumerable GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + public IEnumerable GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) { - var series = Series; + return GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null); + } - if (IndexNumber.HasValue && series != null) - { - return series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes); - } - - var episodes = GetRecursiveChildren(user) - .OfType(); - - if (series != null && series.ContainsEpisodesWithoutSeasonFolders) - { - var seasonNumber = IndexNumber; - var list = episodes.ToList(); - - if (seasonNumber.HasValue) - { - list.AddRange(series.GetRecursiveChildren(user).OfType() - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); - } - else - { - list.AddRange(series.GetRecursiveChildren(user).OfType() - .Where(i => !i.ParentIndexNumber.HasValue)); - } - - episodes = list.DistinctBy(i => i.Id); - } - - if (!includeMissingEpisodes) - { - episodes = episodes.Where(i => !i.IsMissingEpisode); - } - if (!includeVirtualUnairedEpisodes) - { - episodes = episodes.Where(i => !i.IsVirtualUnaired); - } - - return LibraryManager - .Sort(episodes, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) - .Cast(); + public IEnumerable GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable allSeriesEpisodes) + { + return series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes); } public IEnumerable GetEpisodes() diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 600ea173a2..bbb9b30196 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -183,24 +183,20 @@ namespace MediaBrowser.Controller.Entities.TV protected override Task> GetItemsInternal(InternalItemsQuery query) { + if (query.User == null) + { + return base.GetItemsInternal(query); + } + var user = query.User; Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); IEnumerable items; - if (query.User == null) - { - items = query.Recursive - ? GetRecursiveChildren(filter) - : Children.Where(filter); - } - else - { - items = query.Recursive - ? GetSeasons(user).Cast().Concat(GetEpisodes(user)).Where(filter) - : GetSeasons(user).Where(filter); - } + items = query.Recursive + ? GetSeasons(user).Cast().Concat(GetEpisodes(user)).Where(filter) + : GetSeasons(user).Where(filter); var result = PostFilterAndSort(items, query); @@ -260,8 +256,10 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired) { + var allSeriesEpisodes = GetAllEpisodes(user).ToList(); + var allEpisodes = GetSeasons(user, true, true) - .SelectMany(i => i.GetEpisodes(user, includeMissing, includeVirtualUnaired)) + .SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes)) .Reverse() .ToList(); @@ -350,7 +348,7 @@ namespace MediaBrowser.Controller.Entities.TV return false; } - public IEnumerable GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + private IEnumerable GetAllEpisodes(User user) { IEnumerable episodes; @@ -388,7 +386,24 @@ namespace MediaBrowser.Controller.Entities.TV }).Cast(); } - episodes = FilterEpisodesBySeason(episodes, parentSeason, DisplaySpecialsWithSeasons); + return episodes; + } + + public IEnumerable GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + { + IEnumerable episodes = GetAllEpisodes(user); + + return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + } + + public IEnumerable GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable allSeriesEpisodes) + { + if (allSeriesEpisodes == null) + { + return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes); + } + + var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, DisplaySpecialsWithSeasons); if (!includeMissingEpisodes) { diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 2e165f4163..ca4dc97513 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -29,6 +29,8 @@ namespace MediaBrowser.Controller.Persistence /// Task{UserItemData}. UserItemData GetUserData(Guid userId, string key); + UserItemData GetUserData(Guid userId, List keys); + /// /// Return all user data associated with the given user /// diff --git a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs index afbce87a9f..c2606dc4b2 100644 --- a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs @@ -23,7 +23,8 @@ namespace MediaBrowser.Server.Implementations.Library { public event EventHandler UserDataSaved; - private readonly Dictionary _userData = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _userData = + new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly ILogger _logger; private readonly IServerConfigurationManager _config; @@ -64,13 +65,6 @@ namespace MediaBrowser.Server.Implementations.Library try { await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false); - - var newValue = userData; - - lock (_userData) - { - _userData[GetCacheKey(userId, key)] = newValue; - } } catch (Exception ex) { @@ -80,6 +74,9 @@ namespace MediaBrowser.Server.Implementations.Library } } + var cacheKey = GetCacheKey(userId, item.Id); + _userData.AddOrUpdate(cacheKey, userData, (k, v) => userData); + EventHelper.FireEventIfNotNull(UserDataSaved, this, new UserDataSaveEventArgs { Keys = keys, @@ -122,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.Library throw; } - + } /// @@ -140,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Library return Repository.GetAllUserData(userId); } - public UserItemData GetUserData(Guid userId, List keys) + public UserItemData GetUserData(Guid userId, Guid itemId, List keys) { if (userId == Guid.Empty) { @@ -150,26 +147,23 @@ namespace MediaBrowser.Server.Implementations.Library { throw new ArgumentNullException("keys"); } - - lock (_userData) + if (keys.Count == 0) { - foreach (var key in keys) - { - var cacheKey = GetCacheKey(userId, key); - UserItemData value; - if (_userData.TryGetValue(cacheKey, out value)) - { - return value; - } + throw new ArgumentException("UserData keys cannot be empty."); + } - value = Repository.GetUserData(userId, key); + var cacheKey = GetCacheKey(userId, itemId); - if (value != null) - { - _userData[cacheKey] = value; - return value; - } - } + return _userData.GetOrAdd(cacheKey, k => GetUserDataInternal(userId, keys)); + } + + private UserItemData GetUserDataInternal(Guid userId, List keys) + { + var userData = Repository.GetUserData(userId, keys); + + if (userData != null) + { + return userData; } if (keys.Count > 0) @@ -184,57 +178,13 @@ namespace MediaBrowser.Server.Implementations.Library return null; } - /// - /// Gets the user data. - /// - /// The user id. - /// The key. - /// Task{UserItemData}. - public UserItemData GetUserData(Guid userId, string key) - { - if (userId == Guid.Empty) - { - throw new ArgumentNullException("userId"); - } - if (string.IsNullOrEmpty(key)) - { - throw new ArgumentNullException("key"); - } - - lock (_userData) - { - var cacheKey = GetCacheKey(userId, key); - UserItemData value; - if (_userData.TryGetValue(cacheKey, out value)) - { - return value; - } - - value = Repository.GetUserData(userId, key); - - if (value == null) - { - value = new UserItemData - { - UserId = userId, - Key = key - }; - } - - _userData[cacheKey] = value; - return value; - } - } - /// /// Gets the internal key. /// - /// The user id. - /// The key. /// System.String. - private string GetCacheKey(Guid userId, string key) + private string GetCacheKey(Guid userId, Guid itemId) { - return userId + key; + return userId.ToString("N") + itemId.ToString("N"); } public UserItemData GetUserData(IHasUserData user, IHasUserData item) @@ -249,7 +199,7 @@ namespace MediaBrowser.Server.Implementations.Library public UserItemData GetUserData(Guid userId, IHasUserData item) { - return GetUserData(userId, item.GetUserDataKeys()); + return GetUserData(userId, item.Id, item.GetUserDataKeys()); } public UserItemDataDto GetUserDataDto(IHasUserData item, User user) diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs index 7f3b32e062..bfdb9e0c7a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs @@ -5,7 +5,9 @@ using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Data; +using System.Globalization; using System.IO; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -300,6 +302,52 @@ namespace MediaBrowser.Server.Implementations.Persistence } } + public UserItemData GetUserData(Guid userId, List keys) + { + if (userId == Guid.Empty) + { + throw new ArgumentNullException("userId"); + } + if (keys == null) + { + throw new ArgumentNullException("keys"); + } + + using (var cmd = _connection.CreateCommand()) + { + var index = 0; + var excludeIds = new List(); + var builder = new StringBuilder(); + foreach (var key in keys) + { + var paramName = "@Key" + index; + excludeIds.Add("Key =" + paramName); + cmd.Parameters.Add(cmd, paramName, DbType.String).Value = key; + builder.Append(" WHEN Key=" + paramName + " THEN " + index); + index++; + } + + var keyText = string.Join(" OR ", excludeIds.ToArray()); + + cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@userId AND (" + keyText + ") "; + + cmd.CommandText += " ORDER BY (Case " + builder + " Else " + keys.Count.ToString(CultureInfo.InvariantCulture) + " End )"; + cmd.CommandText += " LIMIT 1"; + + cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + return ReadRow(reader); + } + } + + return null; + } + } + /// /// Return all user-data associated with the given user /// diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs index ec91dc1b71..6019d64b4b 100644 --- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs +++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs @@ -111,24 +111,6 @@ namespace MediaBrowser.Server.Implementations.TV .Select(i => GetNextUp(i, currentUser)) // Include if an episode was found, and either the series is not unwatched or the specific series was requested .Where(i => i.Item1 != null && (!i.Item3 || !string.IsNullOrWhiteSpace(request.SeriesId))) - //.OrderByDescending(i => - //{ - // var episode = i.Item1; - - // var seriesUserData = _userDataManager.GetUserData(user, episode.Series); - - // if (seriesUserData.IsFavorite) - // { - // return 2; - // } - - // if (seriesUserData.Likes.HasValue) - // { - // return seriesUserData.Likes.Value ? 1 : -1; - // } - - // return 0; - //}) .OrderByDescending(i => i.Item2) .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue) .Select(i => i.Item1); @@ -143,9 +125,8 @@ namespace MediaBrowser.Server.Implementations.TV private Tuple GetNextUp(Series series, User user) { // Get them in display order, then reverse - var allEpisodes = series.GetSeasons(user, true, true) - .Where(i => !i.IndexNumber.HasValue || i.IndexNumber.Value != 0) - .SelectMany(i => i.GetEpisodes(user)) + var allEpisodes = series.GetEpisodes(user, true, true) + .Where(i => !i.ParentIndexNumber.HasValue || i.ParentIndexNumber.Value != 0) .Reverse() .ToList(); @@ -155,6 +136,8 @@ namespace MediaBrowser.Server.Implementations.TV var includeMissing = user.Configuration.DisplayMissingEpisodes; + var unplayedEpisodes = new List(); + // Go back starting with the most recent episodes foreach (var episode in allEpisodes) { @@ -172,6 +155,8 @@ namespace MediaBrowser.Server.Implementations.TV } else { + unplayedEpisodes.Add(episode); + if (!episode.IsVirtualUnaired && (includeMissing || !episode.IsMissingEpisode)) { nextUp = episode; @@ -184,7 +169,18 @@ namespace MediaBrowser.Server.Implementations.TV return new Tuple(nextUp, lastWatchedDate, false); } - var firstEpisode = allEpisodes.LastOrDefault(i => !i.IsVirtualUnaired && (includeMissing || !i.IsMissingEpisode) && !i.IsPlayed(user)); + Episode firstEpisode = null; + // Find the first unplayed episode. Start from the back of the list since they're in reverse order + for (var i = unplayedEpisodes.Count - 1; i >= 0; i--) + { + var unplayedEpisode = unplayedEpisodes[i]; + + if (!unplayedEpisode.IsVirtualUnaired && (includeMissing || !unplayedEpisode.IsMissingEpisode)) + { + firstEpisode = unplayedEpisode; + break; + } + } // Return the first episode return new Tuple(firstEpisode, DateTime.MinValue, true); From 99528878b76f498f991873d57ee990f55266244d Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 02:23:44 +0200 Subject: [PATCH 09/25] OmdbProvider: Added local caching The lack of caching in the OmdbProvider could result in long library scan time. This commit adds caching for the OmdbProvider similar to the MovieDbProvider. Downloaded metadata is saved locally and only updated if the last refresh occured 3 or more days before --- .../Omdb/OmdbItemProvider.cs | 14 +++- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 79 ++++++++++++++++--- .../TV/Omdb/OmdbEpisodeProvider.cs | 14 +++- 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs index a0d60c1669..7a803eb6ff 100644 --- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Common.Net; +using CommonIO; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; @@ -26,13 +28,17 @@ namespace MediaBrowser.Providers.Omdb private readonly IHttpClient _httpClient; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _configurationManager; - public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager) + public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; _logger = logger; _libraryManager = libraryManager; + _fileSystem = fileSystem; + _configurationManager = configurationManager; } public Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) @@ -220,7 +226,7 @@ namespace MediaBrowser.Providers.Omdb result.Item.SetProviderId(MetadataProviders.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; @@ -259,7 +265,7 @@ namespace MediaBrowser.Providers.Omdb result.Item.SetProviderId(MetadataProviders.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 39da70210d..d3c895b9a0 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -1,4 +1,7 @@ -using MediaBrowser.Common.Net; +using CommonIO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Serialization; @@ -17,15 +20,19 @@ namespace MediaBrowser.Providers.Omdb { internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1); private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _configurationManager; private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); public static OmdbProvider Current; - public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient) + public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; + _fileSystem = fileSystem; + _configurationManager = configurationManager; Current = this; } @@ -37,24 +44,17 @@ namespace MediaBrowser.Providers.Omdb throw new ArgumentNullException("imdbId"); } - var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId; + var path = await EnsureItemInfo(imdbId, cancellationToken); - var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam); + string resultString; - using (var stream = await _httpClient.Get(new HttpRequestOptions + using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072)) { - Url = url, - ResourcePool = ResourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - string resultString; - using (var reader = new StreamReader(stream, new UTF8Encoding(false))) { resultString = reader.ReadToEnd(); } + } resultString = resultString.Replace("\"N/A\"", "\"\""); @@ -130,7 +130,60 @@ namespace MediaBrowser.Providers.Omdb } ParseAdditionalMetadata(item, result); + } + + internal async Task EnsureItemInfo(string imdbId, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(imdbId)) + { + throw new ArgumentNullException("imdbId"); } + + var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId; + + var path = GetDataFilePath(imdbParam); + + var fileInfo = _fileSystem.GetFileSystemInfo(path); + + if (fileInfo.Exists) + { + // If it's recent or automatic updates are enabled, don't re-download + if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3) + { + return path; + } + } + + var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam); + + using (var stream = await _httpClient.Get(new HttpRequestOptions + { + Url = url, + ResourcePool = ResourcePool, + CancellationToken = cancellationToken + + }).ConfigureAwait(false)) + { + var rootObject = _jsonSerializer.DeserializeFromStream(stream); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); + } + + return path; + } + + internal string GetDataFilePath(string imdbId) + { + if (string.IsNullOrEmpty(imdbId)) + { + throw new ArgumentNullException("imdbId"); + } + + var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb"); + + var filename = string.Format("{0}.json", imdbId); + + return Path.Combine(dataPath, filename); } private void ParseAdditionalMetadata(BaseItem item, RootObject result) diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs index 5da1fcf279..ebbefbeb1a 100644 --- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Common.Net; +using CommonIO; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -21,12 +23,16 @@ namespace MediaBrowser.Providers.TV private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; private readonly OmdbItemProvider _itemProvider; + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _configurationManager; - public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager) + public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; - _itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager); + _fileSystem = fileSystem; + _configurationManager = configurationManager; + _itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager, fileSystem, configurationManager); } public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) @@ -58,7 +64,7 @@ namespace MediaBrowser.Providers.TV result.Item.SetProviderId(MetadataProviders.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; From f601614539559ba943bce0bbd0138d39768b2038 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 02:35:35 +0200 Subject: [PATCH 10/25] minifix --- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index d3c895b9a0..823a29d54e 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -53,11 +53,10 @@ namespace MediaBrowser.Providers.Omdb using (var reader = new StreamReader(stream, new UTF8Encoding(false))) { resultString = reader.ReadToEnd(); + resultString = resultString.Replace("\"N/A\"", "\"\""); } } - resultString = resultString.Replace("\"N/A\"", "\"\""); - var result = _jsonSerializer.DeserializeFromString(resultString); // Only take the name and rating if the user's language is set to english, since Omdb has no localization From f7900e77570f87a4a55ff11a087e8a98ae1f5d5d Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 03:08:27 +0200 Subject: [PATCH 11/25] OmdbImageProvider: Check poster field of cached item to determine if an image is available --- .../Omdb/OmdbImageProvider.cs | 35 ++++++++--------- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 38 +++++++++++-------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index a1e038374c..e795d638d8 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -7,6 +7,8 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using System.Collections.Generic; +using System.IO; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -29,22 +31,27 @@ namespace MediaBrowser.Providers.Omdb }; } - public Task> GetImages(IHasImages item, CancellationToken cancellationToken) + public async Task> GetImages(IHasImages item, CancellationToken cancellationToken) { var imdbId = item.GetProviderId(MetadataProviders.Imdb); var list = new List(); - if (!string.IsNullOrWhiteSpace(imdbId)) + if (!string.IsNullOrWhiteSpace(imdbId) && OmdbProvider.Current != null) { - list.Add(new RemoteImageInfo + OmdbProvider.RootObject rootObject = await OmdbProvider.Current.GetRootObject(imdbId, cancellationToken); + + if (!string.IsNullOrEmpty(rootObject.Poster)) { - ProviderName = Name, - Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId) - }); + list.Add(new RemoteImageInfo + { + ProviderName = Name, + Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId) + }); + } } - return Task.FromResult>(list); + return list; } public Task GetImageResponse(string url, CancellationToken cancellationToken) @@ -65,18 +72,6 @@ namespace MediaBrowser.Providers.Omdb public bool Supports(IHasImages item) { - // We'll hammer Omdb if we enable this - if (item is Person) - { - return false; - } - - // Save the http requests since we know it's not currently supported - if (item is Season || item is Episode) - { - return false; - } - // Supports images for tv movies var tvProgram = item as LiveTvProgram; if (tvProgram != null && tvProgram.IsMovie) @@ -84,7 +79,7 @@ namespace MediaBrowser.Providers.Omdb return true; } - return item is Movie || item is Trailer; + return item is Movie || item is Trailer || item is Episode; } public int Order diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 823a29d54e..9873447bec 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -44,20 +44,7 @@ namespace MediaBrowser.Providers.Omdb throw new ArgumentNullException("imdbId"); } - var path = await EnsureItemInfo(imdbId, cancellationToken); - - string resultString; - - using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072)) - { - using (var reader = new StreamReader(stream, new UTF8Encoding(false))) - { - resultString = reader.ReadToEnd(); - resultString = resultString.Replace("\"N/A\"", "\"\""); - } - } - - var result = _jsonSerializer.DeserializeFromString(resultString); + var result = await GetRootObject(imdbId, cancellationToken); // Only take the name and rating if the user's language is set to english, since Omdb has no localization if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) @@ -131,7 +118,26 @@ namespace MediaBrowser.Providers.Omdb ParseAdditionalMetadata(item, result); } - internal async Task EnsureItemInfo(string imdbId, CancellationToken cancellationToken) + internal async Task GetRootObject(string imdbId, CancellationToken cancellationToken) + { + var path = await EnsureItemInfo(imdbId, cancellationToken); + + string resultString; + + using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072)) + { + using (var reader = new StreamReader(stream, new UTF8Encoding(false))) + { + resultString = reader.ReadToEnd(); + resultString = resultString.Replace("\"N/A\"", "\"\""); + } + } + + var result = _jsonSerializer.DeserializeFromString(resultString); + return result; + } + + private async Task EnsureItemInfo(string imdbId, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(imdbId)) { @@ -236,7 +242,7 @@ namespace MediaBrowser.Providers.Omdb return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); } - private class RootObject + internal class RootObject { public string Title { get; set; } public string Year { get; set; } From 7e22ce5f5e145f29c2eafb7596163822797144d5 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 04:56:04 +0200 Subject: [PATCH 12/25] OmdbProvider instantiation --- .../Omdb/OmdbImageProvider.cs | 19 +++++++++++++++---- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 4 ---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index e795d638d8..5631189403 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Common.Net; +using CommonIO; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; @@ -6,6 +8,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Serialization; using System.Collections.Generic; using System.IO; using System.Text; @@ -17,10 +20,16 @@ namespace MediaBrowser.Providers.Omdb public class OmdbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _configurationManager; - public OmdbImageProvider(IHttpClient httpClient) + public OmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { + _jsonSerializer = jsonSerializer; _httpClient = httpClient; + _fileSystem = fileSystem; + _configurationManager = configurationManager; } public IEnumerable GetSupportedImages(IHasImages item) @@ -37,9 +46,11 @@ namespace MediaBrowser.Providers.Omdb var list = new List(); - if (!string.IsNullOrWhiteSpace(imdbId) && OmdbProvider.Current != null) + var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager); + + if (!string.IsNullOrWhiteSpace(imdbId)) { - OmdbProvider.RootObject rootObject = await OmdbProvider.Current.GetRootObject(imdbId, cancellationToken); + OmdbProvider.RootObject rootObject = await provider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(rootObject.Poster)) { diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 9873447bec..4056073f2c 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -25,16 +25,12 @@ namespace MediaBrowser.Providers.Omdb private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public static OmdbProvider Current; - public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; _fileSystem = fileSystem; _configurationManager = configurationManager; - - Current = this; } public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken) From 79128e6d8519c731ccb49f9d982d9eec1d211eeb Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 04:47:30 +0200 Subject: [PATCH 13/25] MovieDbSeasonProvider: Don't fill name from search info --- MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs index 2e51393e39..194af5b99b 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.TV result.HasMetadata = true; result.Item = new Season(); - result.Item.Name = info.Name; + result.Item.Name = seasonInfo.name; result.Item.IndexNumber = seasonNumber; result.Item.Overview = seasonInfo.overview; From 75840c7065e8b16fc5391811a34a06990d592388 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 4 Jun 2016 06:30:06 +0200 Subject: [PATCH 14/25] MovieDbSeriesProvider and MovieDbEpisodeProvider: Add support for trailers --- .../TV/TheMovieDb/MovieDbEpisodeProvider.cs | 16 +++++++++ .../TV/TheMovieDb/MovieDbProviderBase.cs | 14 +++++++- .../TV/TheMovieDb/MovieDbSeriesProvider.cs | 33 +++++++++++++++++-- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs index 9bab3d3801..bc9842b736 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs @@ -114,6 +114,22 @@ namespace MediaBrowser.Providers.TV item.CommunityRating = (float)response.vote_average; item.VoteCount = response.vote_count; + if (response.videos != null && response.videos.results != null) + { + foreach (var video in response.videos.results) + { + if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase) + || video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase)) + { + if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase)) + { + var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key); + item.AddTrailerUrl(videoUrl, true); + } + } + } + } + result.ResetPeople(); var credits = response.credits; diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs index 36800202fd..a6df245b01 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs @@ -210,7 +210,19 @@ namespace MediaBrowser.Providers.TV public class Videos { - public List results { get; set; } + public List