Explorar o código

search hints progress

Luke Pulverenti %!s(int64=12) %!d(string=hai) anos
pai
achega
0bfb755a38

+ 35 - 10
MediaBrowser.Api/SearchService.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Api
     /// </summary>
     [Route("/Search/Hints", "GET")]
     [Api(Description = "Gets search hints based on a search term")]
-    public class GetSearchHints : IReturn<List<SearchHintResult>>
+    public class GetSearchHints : IReturn<SearchHintResult>
     {
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
@@ -96,7 +96,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>Task{IEnumerable{SearchHintResult}}.</returns>
-        private async Task<IEnumerable<SearchHintResult>> GetSearchHintsAsync(GetSearchHints request)
+        private async Task<SearchHintResult> GetSearchHintsAsync(GetSearchHints request)
         {
             IEnumerable<BaseItem> inputItems;
 
@@ -113,34 +113,48 @@ namespace MediaBrowser.Api
 
             var results = await _searchEngine.GetSearchHints(inputItems, request.SearchTerm).ConfigureAwait(false);
 
+            var searchResultArray = results.ToArray();
+
+            IEnumerable<SearchHintInfo> returnResults = searchResultArray;
+
             if (request.StartIndex.HasValue)
             {
-                results = results.Skip(request.StartIndex.Value);
+                returnResults = returnResults.Skip(request.StartIndex.Value);
             }
 
             if (request.Limit.HasValue)
             {
-                results = results.Take(request.Limit.Value);
+                returnResults = returnResults.Take(request.Limit.Value);
             }
 
-            return results.Select(GetSearchHintResult);
+            return new SearchHintResult
+            {
+                TotalRecordCount = searchResultArray.Length,
+
+                SearchHints = returnResults.Select(GetSearchHintResult).ToArray()
+            };
         }
 
         /// <summary>
         /// Gets the search hint result.
         /// </summary>
-        /// <param name="item">The item.</param>
+        /// <param name="hintInfo">The hint info.</param>
         /// <returns>SearchHintResult.</returns>
-        private SearchHintResult GetSearchHintResult(BaseItem item)
+        private SearchHint GetSearchHintResult(SearchHintInfo hintInfo)
         {
-            var result = new SearchHintResult
+            var item = hintInfo.Item;
+
+            var result = new SearchHint
             {
                 Name = item.Name,
                 IndexNumber = item.IndexNumber,
                 ParentIndexNumber = item.ParentIndexNumber,
                 ItemId = DtoBuilder.GetClientItemId(item),
                 Type = item.GetType().Name,
-                MediaType = item.MediaType
+                MediaType = item.MediaType,
+                MatchedTerm = hintInfo.MatchedTerm,
+                DisplayMediaType = item.DisplayMediaType,
+                RunTimeTicks = item.RunTimeTicks
             };
 
             if (item.HasImage(ImageType.Primary))
@@ -160,14 +174,25 @@ namespace MediaBrowser.Api
             if (season != null)
             {
                 result.Series = season.Series.Name;
+
+                result.EpisodeCount = season.RecursiveChildren.OfType<Episode>().Count();
+            }
+
+            var series = item as Series;
+
+            if (series != null)
+            {
+                result.EpisodeCount = series.RecursiveChildren.OfType<Episode>().Count();
             }
 
             var album = item as MusicAlbum;
 
             if (album != null)
             {
-                var songs = album.Children.OfType<Audio>().ToList();
+                var songs = album.RecursiveChildren.OfType<Audio>().ToList();
 
+                result.SongCount = songs.Count;
+                
                 result.Artists = songs
                     .Select(i => i.Artist)
                     .Where(i => !string.IsNullOrEmpty(i))

+ 1 - 1
MediaBrowser.Controller/Dto/DtoBuilder.cs

@@ -431,7 +431,7 @@ namespace MediaBrowser.Controller.Dto
 
                 if (album != null)
                 {
-                    var songs = album.Children.OfType<Audio>().ToList();
+                    var songs = album.RecursiveChildren.OfType<Audio>().ToList();
 
                     dto.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));
 

+ 12 - 0
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -96,5 +96,17 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             return string.Equals(Artist, name, StringComparison.OrdinalIgnoreCase) || string.Equals(AlbumArtist, name, StringComparison.OrdinalIgnoreCase);
         }
+
+        public override string DisplayMediaType
+        {
+            get
+            {
+                return "Song";
+            }
+            set
+            {
+                base.DisplayMediaType = value;
+            }
+        }
     }
 }

+ 13 - 1
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -147,7 +147,19 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// <returns><c>true</c> if the specified artist has artist; otherwise, <c>false</c>.</returns>
         public bool HasArtist(string artist)
         {
-            return Children.OfType<Audio>().Any(i => i.HasArtist(artist));
+            return RecursiveChildren.OfType<Audio>().Any(i => i.HasArtist(artist));
+        }
+
+        public override string DisplayMediaType
+        {
+            get
+            {
+                return "Album";
+            }
+            set
+            {
+                base.DisplayMediaType = value;
+            }
         }
     }
 }

+ 2 - 2
MediaBrowser.Controller/Library/ILibrarySearchEngine.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="inputItems">The input items.</param>
         /// <param name="searchTerm">The search term.</param>
-        /// <returns>Task{IEnumerable{BaseItem}}.</returns>
-        Task<IEnumerable<BaseItem>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm);
+        /// <returns>Task{IEnumerable{SearchHintInfo}}.</returns>
+        Task<IEnumerable<SearchHintInfo>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm);
     }
 }

+ 22 - 0
MediaBrowser.Controller/Library/SearchHintInfo.cs

@@ -0,0 +1,22 @@
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Library
+{
+    /// <summary>
+    /// Class SearchHintInfo
+    /// </summary>
+    public class SearchHintInfo
+    {
+        /// <summary>
+        /// Gets or sets the item.
+        /// </summary>
+        /// <value>The item.</value>
+        public BaseItem Item { get; set; }
+
+        /// <summary>
+        /// Gets or sets the matched term.
+        /// </summary>
+        /// <value>The matched term.</value>
+        public string MatchedTerm { get; set; }
+    }
+}

+ 1 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -112,6 +112,7 @@
     <Compile Include="IServerApplicationPaths.cs" />
     <Compile Include="Library\ChildrenChangedEventArgs.cs" />
     <Compile Include="Dto\DtoBuilder.cs" />
+    <Compile Include="Library\SearchHintInfo.cs" />
     <Compile Include="Providers\IProviderManager.cs" />
     <Compile Include="Providers\MediaInfo\MediaEncoderHelpers.cs" />
     <Compile Include="Providers\MetadataProviderPriority.cs" />

+ 5 - 1
MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs

@@ -167,7 +167,11 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
 
             if (!string.IsNullOrEmpty(val))
             {
-                audio.AddStudios(val.Split(new[] { '/', '|' }, StringSplitOptions.RemoveEmptyEntries));
+                var studios =
+                    val.Split(new[] {'/', '|'}, StringSplitOptions.RemoveEmptyEntries)
+                       .Where(i => !string.Equals(i, audio.Artist, StringComparison.OrdinalIgnoreCase) && !string.Equals(i, audio.AlbumArtist, StringComparison.OrdinalIgnoreCase));
+
+                audio.AddStudios(studios);
             }
         }
 

+ 1 - 1
MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs

@@ -65,7 +65,7 @@ namespace MediaBrowser.Controller.Providers.Music
             var folder = (Folder)item;
 
             // Get each song, distinct by the combination of AlbumArtist and Album
-            var songs = folder.Children.OfType<Audio>().DistinctBy(i => (i.AlbumArtist ?? string.Empty) + (i.Album ?? string.Empty), StringComparer.OrdinalIgnoreCase).ToList();
+            var songs = folder.RecursiveChildren.OfType<Audio>().DistinctBy(i => (i.AlbumArtist ?? string.Empty) + (i.Album ?? string.Empty), StringComparer.OrdinalIgnoreCase).ToList();
 
             foreach (var song in songs.Where(song => !string.IsNullOrEmpty(song.Album) && !string.IsNullOrEmpty(song.AlbumArtist)))
             {

+ 1 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -86,6 +86,7 @@
     <Compile Include="Net\NetworkShareType.cs" />
     <Compile Include="Querying\PersonsQuery.cs" />
     <Compile Include="Querying\ThemeSongsResult.cs" />
+    <Compile Include="Search\SearchHint.cs" />
     <Compile Include="Search\SearchHintResult.cs" />
     <Compile Include="Serialization\IJsonSerializer.cs" />
     <Compile Include="Serialization\IXmlSerializer.cs" />

+ 106 - 0
MediaBrowser.Model/Search/SearchHint.cs

@@ -0,0 +1,106 @@
+using System;
+
+namespace MediaBrowser.Model.Search
+{
+    /// <summary>
+    /// Class SearchHintResult
+    /// </summary>
+    public class SearchHint
+    {
+        /// <summary>
+        /// Gets or sets the item id.
+        /// </summary>
+        /// <value>The item id.</value>
+        public string ItemId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets the matched term.
+        /// </summary>
+        /// <value>The matched term.</value>
+        public string MatchedTerm { get; set; }
+        
+        /// <summary>
+        /// Gets or sets the index number.
+        /// </summary>
+        /// <value>The index number.</value>
+        public int? IndexNumber { get; set; }
+
+        /// <summary>
+        /// Gets or sets the parent index number.
+        /// </summary>
+        /// <value>The parent index number.</value>
+        public int? ParentIndexNumber { get; set; }
+
+        /// <summary>
+        /// Gets or sets the image tag.
+        /// </summary>
+        /// <value>The image tag.</value>
+        public Guid? PrimaryImageTag { get; set; }
+
+        /// <summary>
+        /// Gets or sets the type.
+        /// </summary>
+        /// <value>The type.</value>
+        public string Type { get; set; }
+
+        /// <summary>
+        /// Gets or sets the run time ticks.
+        /// </summary>
+        /// <value>The run time ticks.</value>
+        public long? RunTimeTicks { get; set; }
+        
+        /// <summary>
+        /// Gets or sets the type of the media.
+        /// </summary>
+        /// <value>The type of the media.</value>
+        public string MediaType { get; set; }
+
+        /// <summary>
+        /// Gets or sets the display type of the media.
+        /// </summary>
+        /// <value>The display type of the media.</value>
+        public string DisplayMediaType { get; set; }
+        
+        /// <summary>
+        /// Gets or sets the series.
+        /// </summary>
+        /// <value>The series.</value>
+        public string Series { get; set; }
+
+        /// <summary>
+        /// Gets or sets the album.
+        /// </summary>
+        /// <value>The album.</value>
+        public string Album { get; set; }
+
+        /// <summary>
+        /// Gets or sets the album artist.
+        /// </summary>
+        /// <value>The album artist.</value>
+        public string AlbumArtist { get; set; }
+
+        /// <summary>
+        /// Gets or sets the artists.
+        /// </summary>
+        /// <value>The artists.</value>
+        public string[] Artists { get; set; }
+
+        /// <summary>
+        /// Gets or sets the song count.
+        /// </summary>
+        /// <value>The song count.</value>
+        public int? SongCount { get; set; }
+
+        /// <summary>
+        /// Gets or sets the episode count.
+        /// </summary>
+        /// <value>The episode count.</value>
+        public int? EpisodeCount { get; set; }
+    }
+}

+ 7 - 62
MediaBrowser.Model/Search/SearchHintResult.cs

@@ -1,5 +1,4 @@
-using System;
-
+
 namespace MediaBrowser.Model.Search
 {
     /// <summary>
@@ -8,69 +7,15 @@ namespace MediaBrowser.Model.Search
     public class SearchHintResult
     {
         /// <summary>
-        /// Gets or sets the item id.
-        /// </summary>
-        /// <value>The item id.</value>
-        public string ItemId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the index number.
-        /// </summary>
-        /// <value>The index number.</value>
-        public int? IndexNumber { get; set; }
-
-        /// <summary>
-        /// Gets or sets the parent index number.
-        /// </summary>
-        /// <value>The parent index number.</value>
-        public int? ParentIndexNumber { get; set; }
-
-        /// <summary>
-        /// Gets or sets the image tag.
-        /// </summary>
-        /// <value>The image tag.</value>
-        public Guid? PrimaryImageTag { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type.
-        /// </summary>
-        /// <value>The type.</value>
-        public string Type { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the media.
-        /// </summary>
-        /// <value>The type of the media.</value>
-        public string MediaType { get; set; }
-
-        /// <summary>
-        /// Gets or sets the series.
-        /// </summary>
-        /// <value>The series.</value>
-        public string Series { get; set; }
-
-        /// <summary>
-        /// Gets or sets the album.
-        /// </summary>
-        /// <value>The album.</value>
-        public string Album { get; set; }
-
-        /// <summary>
-        /// Gets or sets the album artist.
+        /// Gets or sets the search hints.
         /// </summary>
-        /// <value>The album artist.</value>
-        public string AlbumArtist { get; set; }
+        /// <value>The search hints.</value>
+        public SearchHint[] SearchHints { get; set; }
 
         /// <summary>
-        /// Gets or sets the artists.
+        /// Gets or sets the total record count.
         /// </summary>
-        /// <value>The artists.</value>
-        public string[] Artists { get; set; }
+        /// <value>The total record count.</value>
+        public int TotalRecordCount { get; set; }
     }
 }

+ 110 - 46
MediaBrowser.Server.Implementations/Library/LuceneSearchEngine.cs

@@ -97,24 +97,26 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="searchTerm">The search term.</param>
         /// <returns>IEnumerable{SearchHintResult}.</returns>
         /// <exception cref="System.ArgumentNullException">searchTerm</exception>
-        public async Task<IEnumerable<BaseItem>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm)
+        public async Task<IEnumerable<SearchHintInfo>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm)
         {
             if (string.IsNullOrEmpty(searchTerm))
             {
                 throw new ArgumentNullException("searchTerm");
             }
 
-            var hints = new List<Tuple<BaseItem, int>>();
+            var terms = GetWords(searchTerm);
+
+            var hints = new List<Tuple<BaseItem, string, int>>();
 
             var items = inputItems.Where(i => !(i is MusicArtist)).ToList();
 
             foreach (var item in items)
             {
-                var index = IndexOf(item.Name, searchTerm);
+                var index = GetIndex(item.Name, searchTerm, terms);
 
-                if (index != -1)
+                if (index.Item2 != -1)
                 {
-                    hints.Add(new Tuple<BaseItem, int>(item, index));
+                    hints.Add(new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2));
                 }
             }
 
@@ -127,16 +129,23 @@ namespace MediaBrowser.Server.Implementations.Library
 
             foreach (var item in artists)
             {
-                var index = IndexOf(item, searchTerm);
+                var index = GetIndex(item, searchTerm, terms);
 
-                if (index != -1)
+                if (index.Item2 != -1)
                 {
-                    var artist = await _libraryManager.GetArtist(item).ConfigureAwait(false);
+                    try
+                    {
+                        var artist = await _libraryManager.GetArtist(item).ConfigureAwait(false);
 
-                    hints.Add(new Tuple<BaseItem, int>(artist, index));
+                        hints.Add(new Tuple<BaseItem, string, int>(artist, index.Item1, index.Item2));
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error getting {0}", ex, item);
+                    }
                 }
             }
-            
+
             // Find genres
             var genres = items.SelectMany(i => i.Genres)
                 .Where(i => !string.IsNullOrEmpty(i))
@@ -145,13 +154,20 @@ namespace MediaBrowser.Server.Implementations.Library
 
             foreach (var item in genres)
             {
-                var index = IndexOf(item, searchTerm);
+                var index = GetIndex(item, searchTerm, terms);
 
-                if (index != -1)
+                if (index.Item2 != -1)
                 {
-                    var genre = await _libraryManager.GetGenre(item).ConfigureAwait(false);
+                    try
+                    {
+                        var genre = await _libraryManager.GetGenre(item).ConfigureAwait(false);
 
-                    hints.Add(new Tuple<BaseItem, int>(genre, index));
+                        hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error getting {0}", ex, item);
+                    }
                 }
             }
 
@@ -163,13 +179,20 @@ namespace MediaBrowser.Server.Implementations.Library
 
             foreach (var item in studios)
             {
-                var index = IndexOf(item, searchTerm);
+                var index = GetIndex(item, searchTerm, terms);
 
-                if (index != -1)
+                if (index.Item2 != -1)
                 {
-                    var studio = await _libraryManager.GetStudio(item).ConfigureAwait(false);
+                    try
+                    {
+                        var studio = await _libraryManager.GetStudio(item).ConfigureAwait(false);
 
-                    hints.Add(new Tuple<BaseItem, int>(studio, index));
+                        hints.Add(new Tuple<BaseItem, string, int>(studio, index.Item1, index.Item2));
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error getting {0}", ex, item);
+                    }
                 }
             }
 
@@ -182,52 +205,93 @@ namespace MediaBrowser.Server.Implementations.Library
 
             foreach (var item in persons)
             {
-                var index = IndexOf(item, searchTerm);
+                var index = GetIndex(item, searchTerm, terms);
 
-                if (index != -1)
+                if (index.Item2 != -1)
                 {
-                    var person = await _libraryManager.GetPerson(item).ConfigureAwait(false);
+                    try
+                    {
+                        var person = await _libraryManager.GetPerson(item).ConfigureAwait(false);
 
-                    hints.Add(new Tuple<BaseItem, int>(person, index));
+                        hints.Add(new Tuple<BaseItem, string, int>(person, index.Item1, index.Item2));
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error getting {0}", ex, item);
+                    }
                 }
             }
 
-            return hints.OrderBy(i => i.Item2).Select(i => i.Item1);
-        }
-
-        /// <summary>
-        /// Gets the words.
-        /// </summary>
-        /// <param name="term">The term.</param>
-        /// <returns>System.String[][].</returns>
-        private string[] GetWords(string term)
-        {
-            // TODO: Improve this to be more accurate and respect culture
-            var words = term.Split(' ');
-
-            return words;
+            return hints.OrderBy(i => i.Item3).Select(i => new SearchHintInfo
+            {
+                Item = i.Item1,
+                MatchedTerm = i.Item2
+            });
         }
 
         /// <summary>
-        /// Indexes the of.
+        /// Gets the index.
         /// </summary>
         /// <param name="input">The input.</param>
-        /// <param name="term">The term.</param>
+        /// <param name="searchInput">The search input.</param>
+        /// <param name="searchWords">The search input.</param>
         /// <returns>System.Int32.</returns>
-        private int IndexOf(string input, string term)
+        private Tuple<string, int> GetIndex(string input, string searchInput, string[] searchWords)
         {
-            var index = 0;
+            if (string.Equals(input, searchInput, StringComparison.OrdinalIgnoreCase))
+            {
+                return new Tuple<string, int>(searchInput, 0);
+            }
+
+            var index = input.IndexOf(searchInput, StringComparison.OrdinalIgnoreCase);
 
-            foreach (var word in GetWords(input))
+            if (index == 0)
             {
-                if (word.IndexOf(term, StringComparison.OrdinalIgnoreCase) != -1)
+                return new Tuple<string, int>(searchInput, 1);
+            }
+            if (index > 0)
+            {
+                return new Tuple<string, int>(searchInput, 2);
+            }
+
+            var items = GetWords(input);
+
+            for (var i = 0; i < searchWords.Length; i++)
+            {
+                var searchTerm = searchWords[i];
+
+                for (var j = 0; j < items.Length; j++)
                 {
-                    return index;
-                }
+                    var item = items[j];
+
+                    if (string.Equals(item, searchTerm, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new Tuple<string, int>(searchTerm, 3 + (i + 1) * (j + 1));
+                    }
+                    
+                    index = item.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
 
-                index++;
+                    if (index == 0)
+                    {
+                        return new Tuple<string, int>(searchTerm, 4 + (i + 1) * (j + 1));
+                    }
+                    if (index > 0)
+                    {
+                        return new Tuple<string, int>(searchTerm, 5 + (i + 1) * (j + 1));
+                    }
+                }
             }
-            return -1;
+            return new Tuple<string, int>(null, -1);
+        }
+
+        /// <summary>
+        /// Gets the words.
+        /// </summary>
+        /// <param name="term">The term.</param>
+        /// <returns>System.String[][].</returns>
+        private string[] GetWords(string term)
+        {
+            return term.Split().Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
         }
     }
 

+ 15 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -93,6 +93,21 @@
     <Content Include="dashboard-ui\css\images\bgflip.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\css\images\items\searchhints\film.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\css\images\items\searchhints\game.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\css\images\items\searchhints\music.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\css\images\items\searchhints\person.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\css\images\items\searchhints\tv.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\css\images\searchbutton.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>