diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 331c55f17..7912cba6f 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -288,6 +288,12 @@ namespace MediaBrowser.Api hasTags.Tags = request.Tags; } + var hasKeywords = item as IHasKeywords; + if (hasKeywords != null) + { + hasKeywords.Keywords = request.Keywords; + } + if (request.Studios != null) { item.Studios = request.Studios.Select(x => x.Name).ToList(); diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 683e609d2..d1dc801bc 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -130,6 +130,17 @@ namespace MediaBrowser.Api return new List(); } + private static IEnumerable GetKeywords(BaseItem item) + { + var hasTags = item as IHasKeywords; + if (hasTags != null) + { + return hasTags.Keywords; + } + + return new List(); + } + /// /// Gets the similiarity score. /// @@ -151,6 +162,9 @@ namespace MediaBrowser.Api // 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 => 3); diff --git a/MediaBrowser.Controller/Entities/IHasKeywords.cs b/MediaBrowser.Controller/Entities/IHasKeywords.cs new file mode 100644 index 000000000..ab9eb4aee --- /dev/null +++ b/MediaBrowser.Controller/Entities/IHasKeywords.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Controller.Entities +{ + public interface IHasKeywords + { + /// + /// Gets or sets the keywords. + /// + /// The keywords. + List Keywords { get; set; } + } + + public static class KeywordExtensions + { + public static void AddKeyword(this IHasKeywords item, string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + if (!item.Keywords.Contains(name, StringComparer.OrdinalIgnoreCase)) + { + item.Keywords.Add(name); + } + } + } +} diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 91e7873c8..19d0d6682 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities.Movies /// /// Class BoxSet /// - public class BoxSet : Folder, IHasTrailers, IHasTags, IHasPreferredMetadataLanguage, IHasDisplayOrder + public class BoxSet : Folder, IHasTrailers, IHasTags, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder { public BoxSet() { @@ -18,6 +18,7 @@ namespace MediaBrowser.Controller.Entities.Movies Tags = new List(); DisplayOrder = ItemSortBy.PremiereDate; + Keywords = new List(); } public List LocalTrailerIds { get; set; } @@ -33,6 +34,7 @@ namespace MediaBrowser.Controller.Entities.Movies /// /// The tags. public List Tags { get; set; } + public List Keywords { get; set; } public string PreferredMetadataLanguage { get; set; } diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index f9d3f845c..b1d3a871a 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities.Movies /// /// Class Movie /// - public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasTags, IHasPreferredMetadataLanguage + public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasTags, IHasPreferredMetadataLanguage { public List SpecialFeatureIds { get; set; } @@ -39,10 +39,12 @@ namespace MediaBrowser.Controller.Entities.Movies ThemeVideoIds = new List(); Taglines = new List(); Tags = new List(); + Keywords = new List(); } public List LocalTrailerIds { get; set; } - + public List Keywords { get; set; } + public List RemoteTrailers { get; set; } /// diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 7000d04d3..39869505c 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class Trailer /// - public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasTaglines, IHasTags, IHasPreferredMetadataLanguage + public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasKeywords, IHasTaglines, IHasTags, IHasPreferredMetadataLanguage { public List SoundtrackIds { get; set; } @@ -28,12 +28,15 @@ namespace MediaBrowser.Controller.Entities SoundtrackIds = new List(); LocalTrailerIds = new List(); Tags = new List(); + Keywords = new List(); } public List LocalTrailerIds { get; set; } public List RemoteTrailers { get; set; } + public List Keywords { get; set; } + /// /// Gets or sets the tags. /// diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index c34180eb6..c45c14239 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -35,7 +35,17 @@ namespace MediaBrowser.Controller.Library /// The file system children. public IEnumerable FileSystemChildren { - get { return FileSystemDictionary.Values; } + get + { + var dict = FileSystemDictionary; + + if (dict == null) + { + return new List(); + } + + return dict.Values; + } } /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 14205e668..af3857f17 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -87,6 +87,7 @@ + diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index ea21e18b0..72cb0d73e 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -79,6 +79,11 @@ namespace MediaBrowser.Controller.Providers hasTags.Tags.Clear(); } + var hasKeywords = item as IHasKeywords; + if (hasKeywords != null) + { + hasKeywords.Keywords.Clear(); + } var hasTrailers = item as IHasTrailers; if (hasTrailers != null) @@ -747,6 +752,19 @@ namespace MediaBrowser.Controller.Providers break; } + case "PlotKeywords": + { + using (var subtree = reader.ReadSubtree()) + { + var hasTags = item as IHasKeywords; + if (hasTags != null) + { + FetchFromKeywordsNode(subtree, hasTags); + } + } + break; + } + case "Persons": { using (var subtree = reader.ReadSubtree()) @@ -912,6 +930,35 @@ namespace MediaBrowser.Controller.Providers } } + private void FetchFromKeywordsNode(XmlReader reader, IHasKeywords item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "PlotKeyword": + { + var tag = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(tag)) + { + item.AddKeyword(tag); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + /// /// Fetches the data from persons node. /// diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index af43ef200..cc20cc95f 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -370,6 +370,12 @@ namespace MediaBrowser.Model.Dto /// The tags. public List Tags { get; set; } + /// + /// Gets or sets the keywords. + /// + /// The keywords. + public List Keywords { get; set; } + /// /// Gets or sets the primary image aspect ratio, after image enhancements. /// diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 6d61d8ac7..46123485e 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -56,6 +56,11 @@ namespace MediaBrowser.Model.Querying /// IndexOptions, + /// + /// The keywords + /// + Keywords, + /// /// The metadata settings /// diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 8dd64fd14..650d7e27f 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -847,14 +847,14 @@ namespace MediaBrowser.Providers.Movies } } - //if (movieData.keywords != null && movieData.keywords.keywords != null && !movie.LockedFields.Contains(MetadataFields.Tags)) - //{ - // var hasTags = movie as IHasTags; - // if (hasTags != null) - // { - // hasTags.Tags = movieData.keywords.keywords.Select(i => i.name).ToList(); - // } - //} + if (movieData.keywords != null && movieData.keywords.keywords != null && !movie.LockedFields.Contains(MetadataFields.Tags)) + { + var hasTags = movie as IHasKeywords; + if (hasTags != null) + { + hasTags.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList(); + } + } if (movieData.trailers != null && movieData.trailers.youtube != null && movieData.trailers.youtube.Count > 0) diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index c4bd86a79..c251c139c 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -484,6 +484,22 @@ namespace MediaBrowser.Providers.Savers } } + var hasKeywords = item as IHasKeywords; + if (hasKeywords != null) + { + if (hasKeywords.Keywords.Count > 0) + { + builder.Append(""); + + foreach (var tag in hasKeywords.Keywords) + { + builder.Append("" + SecurityElement.Escape(tag) + ""); + } + + builder.Append(""); + } + } + if (item.People.Count > 0) { builder.Append(""); diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 59accba1f..2490aa249 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -698,6 +698,20 @@ namespace MediaBrowser.Server.Implementations.Dto } } + if (fields.Contains(ItemFields.Keywords)) + { + var hasTags = item as IHasKeywords; + if (hasTags != null) + { + dto.Keywords = hasTags.Keywords; + } + + if (dto.Keywords == null) + { + dto.Keywords = new List(); + } + } + if (fields.Contains(ItemFields.ProductionLocations)) { SetProductionLocations(item, dto); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 93bf768e6..211a3028b 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -567,6 +567,7 @@ namespace MediaBrowser.WebDashboard.Api await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.0/jquery.mobile-1.4.0.min.js", newLineBytes).ConfigureAwait(false); //await AppendResource(memoryStream, "thirdparty/jquery.infinite-scroll-helper.min.js", newLineBytes).ConfigureAwait(false); + await AppendResource(memoryStream, "thirdparty/jquery.hoverIntent.minified.js", newLineBytes).ConfigureAwait(false); var versionString = string.Format("window.dashboardVersion='{0}';", _appHost.ApplicationVersion); var versionBytes = Encoding.UTF8.GetBytes(versionString); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 78164a5af..96a27c28d 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -496,6 +496,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest