Browse Source

added collection type

Luke Pulverenti 12 years ago
parent
commit
dab5003d6b

+ 9 - 1
MediaBrowser.Api/Library/LibraryHelpers.cs

@@ -16,10 +16,11 @@ namespace MediaBrowser.Api.Library
         /// Adds the virtual folder.
         /// </summary>
         /// <param name="name">The name.</param>
+        /// <param name="collectionType">Type of the collection.</param>
         /// <param name="user">The user.</param>
         /// <param name="appPaths">The app paths.</param>
         /// <exception cref="System.ArgumentException">There is already a media collection with the name  + name + .</exception>
-        public static void AddVirtualFolder(string name, User user, IServerApplicationPaths appPaths)
+        public static void AddVirtualFolder(string name, string collectionType, User user, IServerApplicationPaths appPaths)
         {
             name = FileSystem.GetValidFilename(name);
 
@@ -32,6 +33,13 @@ namespace MediaBrowser.Api.Library
             }
 
             Directory.CreateDirectory(virtualFolderPath);
+
+            if (!string.IsNullOrEmpty(collectionType))
+            {
+                var path = Path.Combine(virtualFolderPath, collectionType + ".collection");
+
+                File.Create(path);
+            }
         }
 
         /// <summary>

+ 8 - 2
MediaBrowser.Api/Library/LibraryStructureService.cs

@@ -38,6 +38,12 @@ namespace MediaBrowser.Api.Library
         /// </summary>
         /// <value>The name.</value>
         public string Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets the type of the collection.
+        /// </summary>
+        /// <value>The type of the collection.</value>
+        public string CollectionType { get; set; }
     }
 
     [Route("/Library/VirtualFolders/{Name}", "DELETE")]
@@ -196,13 +202,13 @@ namespace MediaBrowser.Api.Library
         {
             if (string.IsNullOrEmpty(request.UserId))
             {
-                LibraryHelpers.AddVirtualFolder(request.Name, null, _appPaths);
+                LibraryHelpers.AddVirtualFolder(request.Name, request.CollectionType, null, _appPaths);
             }
             else
             {
                 var user = _userManager.GetUserById(new Guid(request.UserId));
 
-                LibraryHelpers.AddVirtualFolder(request.Name, user, _appPaths);
+                LibraryHelpers.AddVirtualFolder(request.Name, request.CollectionType, user, _appPaths);
             }
 
             _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);

+ 23 - 3
MediaBrowser.Api/UserLibrary/ArtistsService.cs

@@ -125,15 +125,35 @@ namespace MediaBrowser.Api.UserLibrary
         {
             var name = DeSlugArtistName(request.Name, LibraryManager);
 
-            var items = GetItems(request.UserId).OfType<Audio>().Where(i => i.HasArtist(name)).ToList();
+            var items = GetItems(request.UserId).Where(i =>
+            {
+                var song = i as Audio;
+
+                if (song != null)
+                {
+                    return song.HasArtist(name);
+                }
+
+                var musicVideo = i as MusicVideo;
+
+                if (musicVideo != null)
+                {
+                    return musicVideo.HasArtist(name);
+                }
+                
+                return false;
+
+            }).ToList();
 
             var counts = new ItemByNameCounts
             {
                 TotalCount = items.Count,
 
-                SongCount = items.Count(),
+                SongCount = items.OfType<Audio>().Count(),
+
+                AlbumCount = items.Select(i => i.Parent).OfType<MusicAlbum>().Distinct().Count(),
 
-                AlbumCount = items.Select(i => i.Parent).OfType<MusicAlbum>().Distinct().Count()
+                MusicVideoCount = items.OfType<MusicVideo>().Count(i => i.HasArtist(name))
             };
 
             return ToOptimizedResult(counts);

+ 13 - 0
MediaBrowser.Controller/Dto/DtoBuilder.cs

@@ -548,6 +548,19 @@ namespace MediaBrowser.Controller.Dto
             {
                 SetGameProperties(dto, game);
             }
+
+            var musicVideo = item as MusicVideo;
+
+            if (musicVideo != null)
+            {
+                SetMusicVideoProperties(dto, musicVideo);
+            }
+        }
+
+        private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)
+        {
+            dto.Album = item.Album;
+            dto.Artists = string.IsNullOrEmpty(item.Artist) ? new string[] { } : new[] { item.Artist };
         }
 
         private void SetGameProperties(BaseItemDto dto, Game item)

+ 19 - 1
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -336,7 +336,7 @@ namespace MediaBrowser.Controller.Entities
                 // When resolving the root, we need it's grandchildren (children of user views)
                 var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
 
-                args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, Logger, flattenFolderDepth: flattenFolderDepth, args: args, resolveShortcuts: isPhysicalRoot || args.IsVf);
+                args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
 
                 // Need to remove subpaths that may have been resolved from shortcuts
                 // Example: if \\server\movies exists, then strip out \\server\movies\action
@@ -1111,6 +1111,24 @@ namespace MediaBrowser.Controller.Entities
                 throw new ArgumentNullException();
             }
 
+            // Normalize
+            if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
+            {
+                person.Type = PersonType.GuestStar;
+            }
+            else if (string.Equals(person.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase))
+            {
+                person.Type = PersonType.Director;
+            }
+            else if (string.Equals(person.Role, PersonType.Producer, StringComparison.OrdinalIgnoreCase))
+            {
+                person.Type = PersonType.Producer;
+            }
+            else if (string.Equals(person.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase))
+            {
+                person.Type = PersonType.Writer;
+            }
+
             // If the type is GuestStar and there's already an Actor entry, then update it to avoid dupes
             if (string.Equals(person.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
             {

+ 6 - 4
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -1,4 +1,5 @@
-using System;
+using MediaBrowser.Controller.Library;
+using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
@@ -6,7 +7,6 @@ using System.Linq;
 using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -29,6 +29,8 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        public string CollectionType { get; set; }
+
         /// <summary>
         /// Allow different display preferences for each collection folder
         /// </summary>
@@ -69,7 +71,7 @@ namespace MediaBrowser.Controller.Entities
         /// Our children are actually just references to the ones in the physical root...
         /// </summary>
         /// <value>The actual children.</value>
-        protected override ConcurrentDictionary<Guid,BaseItem> ActualChildren
+        protected override ConcurrentDictionary<Guid, BaseItem> ActualChildren
         {
             get
             {
@@ -91,7 +93,7 @@ namespace MediaBrowser.Controller.Entities
                     .Where(i => i.Path != null && resolveArgs.PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
                     .SelectMany(c => c.Children);
 
-                return new ConcurrentDictionary<Guid,BaseItem>(ourChildren.ToDictionary(i => i.Id));
+                return new ConcurrentDictionary<Guid, BaseItem>(ourChildren.ToDictionary(i => i.Id));
             }
         }
     }

+ 2 - 2
MediaBrowser.Controller/Entities/Folder.cs

@@ -1,5 +1,4 @@
-using System.Collections;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
@@ -8,6 +7,7 @@ using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Entities;
 using System;
+using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;

+ 8 - 7
MediaBrowser.Controller/IO/FileData.cs

@@ -17,18 +17,22 @@ namespace MediaBrowser.Controller.IO
         /// </summary>
         /// <param name="path">The path.</param>
         /// <param name="logger">The logger.</param>
+        /// <param name="args">The args.</param>
         /// <param name="searchPattern">The search pattern.</param>
         /// <param name="flattenFolderDepth">The flatten folder depth.</param>
         /// <param name="resolveShortcuts">if set to <c>true</c> [resolve shortcuts].</param>
-        /// <param name="args">The args.</param>
         /// <returns>Dictionary{System.StringFileSystemInfo}.</returns>
         /// <exception cref="System.ArgumentNullException">path</exception>
-        public static Dictionary<string, FileSystemInfo> GetFilteredFileSystemEntries(string path, ILogger logger, string searchPattern = "*", int flattenFolderDepth = 0, bool resolveShortcuts = true, ItemResolveArgs args = null)
+        public static Dictionary<string, FileSystemInfo> GetFilteredFileSystemEntries(string path, ILogger logger, ItemResolveArgs args, string searchPattern = "*", int flattenFolderDepth = 0, bool resolveShortcuts = true)
         {
             if (string.IsNullOrEmpty(path))
             {
                 throw new ArgumentNullException("path");
             }
+            if (args == null)
+            {
+                throw new ArgumentNullException("args");
+            }
 
             var entries = new DirectoryInfo(path).EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
 
@@ -60,16 +64,13 @@ namespace MediaBrowser.Controller.IO
                     var data = new DirectoryInfo(newPath);
 
                     // add to our physical locations
-                    if (args != null)
-                    {
-                        args.AddAdditionalLocation(newPath);
-                    }
+                    args.AddAdditionalLocation(newPath);
 
                     dict[newPath] = data;
                 }
                 else if (flattenFolderDepth > 0 && isDirectory)
                 {
-                    foreach (var child in GetFilteredFileSystemEntries(fullName, logger, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts))
+                    foreach (var child in GetFilteredFileSystemEntries(fullName, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts))
                     {
                         dict[child.Key] = child.Value;
                     }

+ 7 - 0
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -254,5 +254,12 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="item">The item.</param>
         void ReportItemRemoved(BaseItem item);
+
+        /// <summary>
+        /// Finds the type of the collection.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>System.String.</returns>
+        string FindCollectionType(BaseItem item);
     }
 }

+ 7 - 0
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -161,6 +161,13 @@ namespace MediaBrowser.Model.ApiClient
         /// <returns>Task{ItemsResult}.</returns>
         Task<ItemsResult> GetSimilarMoviesAsync(SimilarItemsQuery query);
 
+        /// <summary>
+        /// Gets the similar trailers async.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>Task{ItemsResult}.</returns>
+        Task<ItemsResult> GetSimilarTrailersAsync(SimilarItemsQuery query);
+        
         /// <summary>
         /// Gets the similar series async.
         /// </summary>

+ 20 - 0
MediaBrowser.Model/Entities/CollectionType.cs

@@ -0,0 +1,20 @@
+
+namespace MediaBrowser.Model.Entities
+{
+    public static class CollectionType
+    {
+        public const string Movies = "movies";
+
+        public const string TvShows = "tvshows";
+
+        public const string Music = "music";
+
+        public const string MusicVideos = "musicvideos";
+
+        public const string Trailers = "trailers";
+
+        public const string HomeVideos = "homevideos";
+
+        public const string BoxSets = "boxsets";
+    }
+}

+ 6 - 0
MediaBrowser.Model/Entities/VirtualFolderInfo.cs

@@ -19,6 +19,12 @@ namespace MediaBrowser.Model.Entities
         /// <value>The locations.</value>
         public List<string> Locations { get; set; }
 
+        /// <summary>
+        /// Gets or sets the type of the collection.
+        /// </summary>
+        /// <value>The type of the collection.</value>
+        public string CollectionType { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
         /// </summary>

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

@@ -53,6 +53,7 @@
     <Compile Include="Dto\ItemByNameCounts.cs" />
     <Compile Include="Dto\ItemCounts.cs" />
     <Compile Include="Dto\StudioDto.cs" />
+    <Compile Include="Entities\CollectionType.cs" />
     <Compile Include="Entities\ItemReview.cs" />
     <Compile Include="Entities\MediaUrl.cs" />
     <Compile Include="Entities\MetadataFields.cs" />

+ 3 - 1
MediaBrowser.Providers/Savers/MovieXmlSaver.cs

@@ -102,7 +102,9 @@ namespace MediaBrowser.Providers.Savers
             XmlSaverHelpers.Save(builder, xmlFilePath, new[]
                 {
                     "IMDBrating",
-                    "Description"
+                    "Description",
+                    "Artist",
+                    "Album"
                 });
 
             // Set last refreshed so that the provider doesn't trigger after the file save

+ 1 - 1
MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs

@@ -64,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.IO
         public async void RemoveTempIgnore(string path)
         {
             // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. 
-            await Task.Delay(500).ConfigureAwait(false);
+            await Task.Delay(1000).ConfigureAwait(false);
 
             string val;
             _tempIgnoredPaths.TryRemove(path, out val);

+ 57 - 2
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -487,7 +487,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 // When resolving the root, we need it's grandchildren (children of user views)
                 var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
 
-                args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _logger, flattenFolderDepth: flattenFolderDepth, args: args, resolveShortcuts: isPhysicalRoot || args.IsVf);
+                args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
 
                 // Need to remove subpaths that may have been resolved from shortcuts
                 // Example: if \\server\movies exists, then strip out \\server\movies\action
@@ -1168,10 +1168,23 @@ namespace MediaBrowser.Server.Implementations.Library
                 .Select(dir => new VirtualFolderInfo
                 {
                     Name = Path.GetFileName(dir),
-                    Locations = Directory.EnumerateFiles(dir, "*.lnk", SearchOption.TopDirectoryOnly).Select(FileSystem.ResolveShortcut).OrderBy(i => i).ToList()
+
+                    Locations = Directory.EnumerateFiles(dir, "*.lnk", SearchOption.TopDirectoryOnly)
+                                .Select(FileSystem.ResolveShortcut)
+                                .OrderBy(i => i)
+                                .ToList(),
+
+                    CollectionType = GetCollectionType(dir)
                 });
         }
 
+        private string GetCollectionType(string path)
+        {
+            return new DirectoryInfo(path).EnumerateFiles("*.collection", SearchOption.TopDirectoryOnly)
+                .Select(i => Path.GetFileNameWithoutExtension(i.FullName))
+                .FirstOrDefault();
+        }
+
         /// <summary>
         /// Gets the item by id.
         /// </summary>
@@ -1405,5 +1418,47 @@ namespace MediaBrowser.Server.Implementations.Library
                 }
             }
         }
+
+        /// <summary>
+        /// Finds the type of the collection.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>System.String.</returns>
+        public string FindCollectionType(BaseItem item)
+        {
+            while (!(item.Parent is AggregateFolder) && item.Parent != null)
+            {
+                item = item.Parent;
+            }
+
+            if (item == null)
+            {
+                return null;
+            }
+
+            var collectionTypes = _userManager.Users
+                .Select(i => i.RootFolder)
+                .Distinct()
+                .SelectMany(i => i.Children)
+                .OfType<CollectionFolder>()
+                .Where(i =>
+                {
+                    try
+                    {
+                        return i.LocationType != LocationType.Remote && i.LocationType != LocationType.Virtual &&
+                               i.ResolveArgs.PhysicalLocations.Contains(item.Path);
+                    }
+                    catch (IOException ex)
+                    {
+                        _logger.ErrorException("Error getting resolve args for {0}", ex, i.Path);
+                        return false;
+                    }
+                })
+                .Select(i => i.CollectionType)
+                .Where(i => !string.IsNullOrEmpty(i))
+                .Distinct();
+
+            return collectionTypes.SingleOrDefault();
+        }
     }
 }

+ 15 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs

@@ -1,6 +1,9 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Resolvers;
+using System;
+using System.IO;
+using System.Linq;
 
 namespace MediaBrowser.Server.Implementations.Library.Resolvers
 {
@@ -37,7 +40,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
                 }
                 if (args.IsVf)
                 {
-                    return new CollectionFolder();
+                    return new CollectionFolder
+                    {
+                        CollectionType = GetCollectionType(args)
+                    };
                 }
 
                 return new Folder();
@@ -45,6 +51,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
 
             return null;
         }
+
+        private string GetCollectionType(ItemResolveArgs args)
+        {
+            return args.FileSystemChildren
+                .Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory && string.Equals(".collection", i.Extension, StringComparison.OrdinalIgnoreCase))
+                .Select(i => Path.GetFileNameWithoutExtension(i.FullName))
+                .FirstOrDefault();
+        }
     }
 
     /// <summary>

+ 19 - 10
MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -18,10 +18,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
     public class MovieResolver : BaseVideoResolver<Video>
     {
         private IServerApplicationPaths ApplicationPaths { get; set; }
+        private readonly ILibraryManager _libraryManager;
 
-        public MovieResolver(IServerApplicationPaths appPaths)
+        public MovieResolver(IServerApplicationPaths appPaths, ILibraryManager libraryManager)
         {
             ApplicationPaths = appPaths;
+            _libraryManager = libraryManager;
         }
 
         /// <summary>
@@ -73,19 +75,27 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     return null;
                 }
 
-                // A shortcut to help us resolve faster in some cases
-                var isKnownMovie = args.ContainsMetaFileByName("movie.xml");
+                var collectionType = args.Parent == null ? null : _libraryManager.FindCollectionType(args.Parent);
 
-                if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1)
+                if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1 ||
+                    string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
                 {
-                    return FindMovie<Trailer>(args.Path, args.FileSystemChildren, isKnownMovie);
+                    return FindMovie<Trailer>(args.Path, args.FileSystemChildren);
                 }
-                if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1)
+
+                if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
+                    string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
                 {
-                    return FindMovie<MusicVideo>(args.Path, args.FileSystemChildren, isKnownMovie);
+                    return FindMovie<MusicVideo>(args.Path, args.FileSystemChildren);
+                }
+
+                if (!string.IsNullOrEmpty(collectionType) && 
+                    !string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+                {
+                    return null;
                 }
 
-                return FindMovie<Movie>(args.Path, args.FileSystemChildren, isKnownMovie);
+                return FindMovie<Movie>(args.Path, args.FileSystemChildren);
             }
 
             return null;
@@ -126,9 +136,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
         /// <typeparam name="T"></typeparam>
         /// <param name="path">The path.</param>
         /// <param name="fileSystemEntries">The file system entries.</param>
-        /// <param name="isKnownMovie">if set to <c>true</c> [is known movie].</param>
         /// <returns>Movie.</returns>
-        private T FindMovie<T>(string path, IEnumerable<FileSystemInfo> fileSystemEntries, bool isKnownMovie)
+        private T FindMovie<T>(string path, IEnumerable<FileSystemInfo> fileSystemEntries)
             where T : Video, new()
         {
             var movies = new List<T>();

+ 8 - 2
MediaBrowser.WebDashboard/ApiClient.js

@@ -896,16 +896,22 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
        * Adds a virtual folder to either the default view or a user view
        * @param {String} name
        */
-        self.addVirtualFolder = function (name, userId) {
+        self.addVirtualFolder = function (name, type, userId) {
 
             if (!name) {
                 throw new Error("null name");
             }
 
+            var options = {};
+
+            if (type) {
+                options.collectionType = type;
+            }
+
             var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/VirtualFolders";
 
             url += "/" + name;
-            url = self.getUrl(url);
+            url = self.getUrl(url, options);
 
             return self.ajax({
                 type: "POST",

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.143" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.144" targetFramework="net45" />
   <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" />
   <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" />
 </packages>