Browse Source

added dlna music folders

Luke Pulverenti 10 năm trước cách đây
mục cha
commit
91ffff7771

+ 0 - 1
MediaBrowser.Api/Dlna/DlnaServerService.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Configuration;
 using ServiceStack;
 using ServiceStack.Text.Controller;
 using ServiceStack.Web;

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -788,7 +788,7 @@ namespace MediaBrowser.Api.Playback
         /// <returns>System.String.</returns>
         protected string GetInputArgument(string transcodingJobId, StreamState state)
         {
-            if (state.InputProtocol == MediaProtocol.File &&
+            if (SupportsThrottling && state.InputProtocol == MediaProtocol.File &&
                state.RunTimeTicks.HasValue &&
                state.VideoType == VideoType.VideoFile &&
                !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))

+ 0 - 8
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -62,14 +62,6 @@ namespace MediaBrowser.Api.Playback.Hls
         {
         }
 
-        protected override bool SupportsThrottling
-        {
-            get
-            {
-                return false;
-            }
-        }
-
         /// <summary>
         /// Gets the specified request.
         /// </summary>

+ 1 - 1
MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs

@@ -88,7 +88,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
         }
 
         private ConfigurationStore[] _configurationStores = {};
-        private IConfigurationFactory[] _configurationFactories;
+        private IConfigurationFactory[] _configurationFactories = {};
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.

+ 99 - 0
MediaBrowser.Common.Implementations/Devices/DeviceId.cs

@@ -0,0 +1,99 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Logging;
+using System;
+using System.IO;
+using System.Text;
+
+namespace MediaBrowser.Common.Implementations.Devices
+{
+    public class DeviceId
+    {
+        private readonly IApplicationPaths _appPaths;
+        private readonly ILogger _logger;
+
+        private readonly object _syncLock = new object();
+
+        private string CachePath
+        {
+            get { return Path.Combine(_appPaths.DataPath, "device.txt"); }
+        }
+
+        private string GetCachedId()
+        {
+            try
+            {
+                lock (_syncLock)
+                {
+                    var value = File.ReadAllText(CachePath, Encoding.UTF8);
+
+                    Guid guid;
+                    if (Guid.TryParse(value, out guid))
+                    {
+                        return value;
+                    }
+
+                    _logger.Error("Invalid value found in device id file");
+                }
+            }
+            catch (FileNotFoundException ex)
+            {
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error reading file", ex);
+            }
+
+            return null;
+        }
+
+        private void SaveId(string id)
+        {
+            try
+            {
+                var path = CachePath;
+
+                Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+                lock (_syncLock)
+                {
+                    File.WriteAllText(path, id, Encoding.UTF8);
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error writing to file", ex);
+            }
+        }
+
+        private string GetNewId()
+        {
+            return Guid.NewGuid().ToString("N");
+        }
+
+        private string GetDeviceId()
+        {
+            var id = GetCachedId();
+
+            if (string.IsNullOrWhiteSpace(id))
+            {
+                id = GetNewId();
+                SaveId(id);
+            }
+
+            return id;
+        }
+
+        private string _id;
+
+        public DeviceId(IApplicationPaths appPaths, ILogger logger)
+        {
+            _appPaths = appPaths;
+            _logger = logger;
+        }
+
+        public string Value
+        {
+            get { return _id ?? (_id = GetDeviceId()); }
+        }
+    }
+}

+ 1 - 0
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -82,6 +82,7 @@
     <Compile Include="BaseApplicationHost.cs" />
     <Compile Include="BaseApplicationPaths.cs" />
     <Compile Include="Configuration\BaseConfigurationManager.cs" />
+    <Compile Include="Devices\DeviceId.cs" />
     <Compile Include="HttpClientManager\HttpClientInfo.cs" />
     <Compile Include="HttpClientManager\HttpClientManager.cs" />
     <Compile Include="IO\CommonFileSystem.cs" />

+ 2 - 0
MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs

@@ -12,5 +12,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         bool HasArtist(string name);
 
         List<string> AllArtists { get; }
+
+        List<string> Artists { get; }
     }
 }

+ 17 - 0
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -41,6 +41,23 @@ namespace MediaBrowser.Controller.Entities
             ProductionLocations = new List<string>();
         }
 
+        [IgnoreDataMember]
+        public List<string> Artists
+        {
+            get
+            {
+                var list = new List<string>();
+
+                if (!string.IsNullOrEmpty(Artist))
+                {
+                    list.Add(Artist);
+                }
+
+                return list;
+
+            }
+        }
+
         [IgnoreDataMember]
         public List<string> AllArtists
         {

+ 110 - 21
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -171,6 +171,18 @@ namespace MediaBrowser.Controller.Entities
                 case CollectionType.MovieCollections:
                     return GetMovieCollections(parent, user, query);
 
+                case CollectionType.MusicLatest:
+                    return GetMusicLatest(parent, user, query);
+
+                case CollectionType.MusicAlbums:
+                    return GetMusicAlbums(parent, user, query);
+
+                case CollectionType.MusicAlbumArtists:
+                    return GetMusicAlbumArtists(parent, user, query);
+
+                case CollectionType.MusicArtists:
+                    return GetMusicArtists(parent, user, query);
+
                 default:
                     return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), query);
             }
@@ -188,7 +200,78 @@ namespace MediaBrowser.Controller.Entities
                 return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }), query);
             }
 
-            return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).OfType<MusicArtist>(), query);
+            var list = new List<BaseItem>();
+
+            var category = "music";
+
+            list.Add(await GetUserView(category, CollectionType.MusicLatest, user, "0", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.MusicAlbums, user, "1", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false));
+            //list.Add(await GetUserView(CollectionType.MusicArtists, user, "3", parent).ConfigureAwait(false));
+            //list.Add(await GetUserView(CollectionType.MusicGenres, user, "5", parent).ConfigureAwait(false));
+
+            return GetResult(list, query);
+        }
+
+        private QueryResult<BaseItem> GetMusicAlbumArtists(Folder parent, User user, UserItemsQuery query)
+        {
+            var artists = GetRecursiveChildren(parent, user, new[] { CollectionType.Music })
+                .Where(i => !i.IsFolder)
+                .OfType<IHasAlbumArtist>()
+                .SelectMany(i => i.AlbumArtists)
+                .Distinct(StringComparer.OrdinalIgnoreCase)
+                .Select(i =>
+                {
+                    try
+                    {
+                        return _libraryManager.GetArtist(i);
+                    }
+                    catch
+                    {
+                        // Already logged at lower levels
+                        return null;
+                    }
+                })
+                .Where(i => i != null);
+
+            return GetResult(artists, query);
+        }
+
+        private QueryResult<BaseItem> GetMusicArtists(Folder parent, User user, UserItemsQuery query)
+        {
+            var artists = GetRecursiveChildren(parent, user, new[] { CollectionType.Music })
+                .Where(i => !i.IsFolder)
+                .OfType<IHasArtist>()
+                .SelectMany(i => i.Artists)
+                .Distinct(StringComparer.OrdinalIgnoreCase)
+                .Select(i =>
+                {
+                    try
+                    {
+                        return _libraryManager.GetArtist(i);
+                    }
+                    catch
+                    {
+                        // Already logged at lower levels
+                        return null;
+                    }
+                })
+                .Where(i => i != null);
+
+            return GetResult(artists, query);
+        }
+
+        private QueryResult<BaseItem> GetMusicAlbums(Folder parent, User user, UserItemsQuery query)
+        {
+            return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is MusicAlbum), query);
+        }
+
+        private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, UserItemsQuery query)
+        {
+            query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
+            query.SortOrder = SortOrder.Descending;
+
+            return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is MusicVideo || i is Audio.Audio), GetSpecialItemsLimit(), query);
         }
 
         private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, UserItemsQuery query)
@@ -200,11 +283,13 @@ namespace MediaBrowser.Controller.Entities
 
             var list = new List<BaseItem>();
 
-            list.Add(await GetUserView(CollectionType.MovieResume, user, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.MovieLatest, user, "1", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.MovieMovies, user, "2", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.MovieCollections, user, "3", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.MovieFavorites, user, "4", parent).ConfigureAwait(false));
+            var category = "movies";
+
+            list.Add(await GetUserView(category, CollectionType.MovieResume, user, "0", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.MovieLatest, user, "1", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.MovieMovies, user, "2", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.MovieCollections, user, "3", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.MovieFavorites, user, "4", parent).ConfigureAwait(false));
             //list.Add(await GetUserView(CollectionType.MovieGenres, user, "5", parent).ConfigureAwait(false));
 
             return GetResult(list, query);
@@ -243,7 +328,7 @@ namespace MediaBrowser.Controller.Entities
 
             return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), GetSpecialItemsLimit(), query);
         }
-        
+
         private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, UserItemsQuery query)
         {
             var genres = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty })
@@ -278,10 +363,12 @@ namespace MediaBrowser.Controller.Entities
 
             var list = new List<BaseItem>();
 
-            list.Add(await GetUserView(CollectionType.TvResume, user, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.TvNextUp, user, "1", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.TvLatest, user, "2", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.TvSeries, user, "3", parent).ConfigureAwait(false));
+            var category = "tv";
+
+            list.Add(await GetUserView(category, CollectionType.TvResume, user, "0", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.TvNextUp, user, "1", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.TvLatest, user, "2", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.TvSeries, user, "3", parent).ConfigureAwait(false));
             //list.Add(await GetUserView(CollectionType.TvFavorites, user, "4", parent).ConfigureAwait(false));
             //list.Add(await GetUserView(CollectionType.TvGenres, user, "5", parent).ConfigureAwait(false));
 
@@ -297,10 +384,12 @@ namespace MediaBrowser.Controller.Entities
 
             var list = new List<BaseItem>();
 
-            list.Add(await GetUserView(CollectionType.LatestGames, user, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.GameFavorites, user, "2", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(CollectionType.GameSystems, user, "3", parent).ConfigureAwait(false));
+            var category = "games";
+
+            list.Add(await GetUserView(category, CollectionType.LatestGames, user, "0", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.GameFavorites, user, "2", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(category, CollectionType.GameSystems, user, "3", parent).ConfigureAwait(false));
             //list.Add(await GetUserView(CollectionType.GameGenres, user, "4", parent).ConfigureAwait(false));
 
             return GetResult(list, query);
@@ -341,7 +430,7 @@ namespace MediaBrowser.Controller.Entities
         private QueryResult<BaseItem> GetTvNextUp(Folder parent, UserItemsQuery query)
         {
             var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty });
-            
+
             var result = _tvSeriesManager.GetNextUp(new NextUpQuery
             {
                 Limit = query.Limit,
@@ -589,16 +678,16 @@ namespace MediaBrowser.Controller.Entities
         {
             var list = new List<BaseItem>();
 
-            list.Add(await _userViewManager.GetUserView(CollectionType.LiveTvNowPlaying, user, "0", CancellationToken.None).ConfigureAwait(false));
-            list.Add(await _userViewManager.GetUserView(CollectionType.LiveTvChannels, user, string.Empty, CancellationToken.None).ConfigureAwait(false));
-            list.Add(await _userViewManager.GetUserView(CollectionType.LiveTvRecordingGroups, user, string.Empty, CancellationToken.None).ConfigureAwait(false));
+            list.Add(await _userViewManager.GetUserView("livetv", CollectionType.LiveTvNowPlaying, user, "0", CancellationToken.None).ConfigureAwait(false));
+            list.Add(await _userViewManager.GetUserView("livetv", CollectionType.LiveTvChannels, user, string.Empty, CancellationToken.None).ConfigureAwait(false));
+            list.Add(await _userViewManager.GetUserView("livetv", CollectionType.LiveTvRecordingGroups, user, string.Empty, CancellationToken.None).ConfigureAwait(false));
 
             return list;
         }
 
-        private async Task<UserView> GetUserView(string type, User user, string sortName, Folder parent)
+        private async Task<UserView> GetUserView(string category, string type, User user, string sortName, Folder parent)
         {
-            var view = await _userViewManager.GetUserView(type, user, sortName, CancellationToken.None)
+            var view = await _userViewManager.GetUserView(category, type, user, sortName, CancellationToken.None)
                         .ConfigureAwait(false);
 
             if (parent.Id != view.ParentId)

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

@@ -336,10 +336,21 @@ namespace MediaBrowser.Controller.Library
         /// Gets the named folder.
         /// </summary>
         /// <param name="name">The name.</param>
+        /// <param name="category">The category.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="sortName">Name of the sort.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Folder}.</returns>
+        Task<UserView> GetNamedView(string name, string category, string viewType, string sortName, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the named view.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="viewType">Type of the view.</param>
+        /// <param name="sortName">Name of the sort.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task&lt;UserView&gt;.</returns>
         Task<UserView> GetNamedView(string name, string viewType, string sortName, CancellationToken cancellationToken);
     }
 }

+ 2 - 0
MediaBrowser.Controller/Library/IUserViewManager.cs

@@ -11,5 +11,7 @@ namespace MediaBrowser.Controller.Library
         Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken);
 
         Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken);
+
+        Task<UserView> GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken);
     }
 }

+ 13 - 3
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -1,8 +1,6 @@
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Providers;
 
 namespace MediaBrowser.Model.Configuration
 {
@@ -178,9 +176,10 @@ namespace MediaBrowser.Model.Configuration
 
         public bool DefaultMetadataSettingsApplied { get; set; }
 
-        public bool EnableTokenAuthentication { get; set; }
         public PeopleMetadataOptions PeopleMetadataOptions { get; set; }
 
+        public string[] SecureApps { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
@@ -225,6 +224,17 @@ namespace MediaBrowser.Model.Configuration
 
             PeopleMetadataOptions = new PeopleMetadataOptions();
 
+            SecureApps = new[]
+            {
+                "Dashboard",
+                "Chrome Companion",
+                "MBKinect",
+                "NuVue",
+                "Media Browser Theater",
+
+                //"MB-Classic"
+            };
+
             MetadataOptions = new[]
             {
                 new MetadataOptions(1, 1280) {ItemType = "Book"},

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

@@ -49,5 +49,11 @@
         public const string GameSystems = "GameSystems";
         public const string GameGenres = "GameGenres";
         public const string GameFavorites = "GameFavorites";
+
+        public const string MusicArtists = "MusicArtists";
+        public const string MusicAlbumArtists = "MusicAlbumArtists";
+        public const string MusicAlbums = "MusicAlbums";
+        public const string MusicGenres = "MusicGenres";
+        public const string MusicLatest = "MusicLatest";
     }
 }

+ 1 - 5
MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs

@@ -53,17 +53,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
             ValidateUser(req, allowLocal);
         }
 
-        // TODO: Remove this when all clients have supported the new sescurity
-        private readonly List<string> _updatedClients = new List<string>() { "Dashboard", "Chromecast" };
-
         private void ValidateUser(IRequest req, bool allowLocal)
         {
             //This code is executed before the service
             var auth = AuthorizationContext.GetAuthorizationInfo(req);
 
             if (!string.IsNullOrWhiteSpace(auth.Token)
-                || _config.Configuration.EnableTokenAuthentication
-                || _updatedClients.Contains(auth.Client ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+                || _config.Configuration.SecureApps.Contains(auth.Client ?? string.Empty, StringComparer.OrdinalIgnoreCase))
             {
                 if (!allowLocal || !req.IsLocal)
                 {

+ 11 - 7
MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
         /// </summary>
         /// <param name="httpReq">The HTTP req.</param>
         /// <returns>Dictionary{System.StringSystem.String}.</returns>
-        private static AuthorizationInfo GetAuthorization(IRequest httpReq)
+        private AuthorizationInfo GetAuthorization(IRequest httpReq)
         {
             var auth = GetAuthorizationDictionary(httpReq);
 
@@ -59,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
         /// </summary>
         /// <param name="httpReq">The HTTP req.</param>
         /// <returns>Dictionary{System.StringSystem.String}.</returns>
-        private static Dictionary<string, string> GetAuthorizationDictionary(IRequest httpReq)
+        private Dictionary<string, string> GetAuthorizationDictionary(IRequest httpReq)
         {
             var auth = httpReq.Headers["Authorization"];
 
@@ -71,14 +71,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
         /// </summary>
         /// <param name="authorizationHeader">The authorization header.</param>
         /// <returns>Dictionary{System.StringSystem.String}.</returns>
-        private static Dictionary<string, string> GetAuthorization(string authorizationHeader)
+        private Dictionary<string, string> GetAuthorization(string authorizationHeader)
         {
             if (authorizationHeader == null) return null;
 
-            var parts = authorizationHeader.Split(' ');
+            var parts = authorizationHeader.Split(new[] { ' ' }, 2);
 
             // There should be at least to parts
-            if (parts.Length < 2) return null;
+            if (parts.Length != 2) return null;
 
             // It has to be a digest request
             if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
@@ -87,7 +87,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
             }
 
             // Remove uptil the first space
-            authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
+            authorizationHeader = parts[1];
             parts = authorizationHeader.Split(',');
 
             var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -95,7 +95,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
             foreach (var item in parts)
             {
                 var param = item.Trim().Split(new[] { '=' }, 2);
-                result.Add(param[0], param[1].Trim(new[] { '"' }));
+
+                if (param.Length == 2)
+                {
+                    result.Add(param[0], param[1].Trim(new[] { '"' }));
+                }
             }
 
             return result;

+ 15 - 4
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -1484,16 +1484,27 @@ namespace MediaBrowser.Server.Implementations.Library
                 .Distinct(StringComparer.OrdinalIgnoreCase);
         }
 
-        public async Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken)
+        public Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken)
+        {
+            return GetNamedView(name, null, type, sortName, cancellationToken);
+        }
+
+        public async Task<UserView> GetNamedView(string name, string category, string type, string sortName, CancellationToken cancellationToken)
         {
             var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
-                "views",
-                _fileSystem.GetValidFilename(type));
+                "views");
+
+            if (!string.IsNullOrWhiteSpace(category))
+            {
+                path = Path.Combine(path, _fileSystem.GetValidFilename(category));
+            }
+
+            path = Path.Combine(path, _fileSystem.GetValidFilename(type));
 
             var id = (path + "_namedview_" + name).GetMBId(typeof(UserView));
 
             var item = GetItemById(id) as UserView;
-            
+
             if (item == null)
             {
                 Directory.CreateDirectory(Path.GetDirectoryName(path));

+ 5 - 34
MediaBrowser.Server.Implementations/Library/UserViewManager.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Entities;
@@ -16,7 +15,6 @@ using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Querying;
 using System;
 using System.Collections.Generic;
-using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -148,43 +146,16 @@ namespace MediaBrowser.Server.Implementations.Library
                 .ThenBy(i => i.SortName);
         }
 
-        public Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken)
+        public Task<UserView> GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken)
         {
             var name = _localizationManager.GetLocalizedString("ViewType" + type);
 
-            return _libraryManager.GetNamedView(name, type, sortName, cancellationToken);
+            return _libraryManager.GetNamedView(name, category, type, sortName, cancellationToken);
         }
 
-        public async Task<SpecialFolder> GetSpecialFolder(string name, SpecialFolderType type, string itemType, CancellationToken cancellationToken)
+        public Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken)
         {
-            var path = Path.Combine(_appPaths.ItemsByNamePath,
-                "specialfolders",
-                _fileSystem.GetValidFilename(name));
-
-            var id = (path + "_specialfolder_" + name).GetMBId(typeof(SpecialFolder));
-
-            var item = _libraryManager.GetItemById(id) as SpecialFolder;
-
-            if (item == null)
-            {
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
-
-                item = new SpecialFolder
-                {
-                    Path = path,
-                    Id = id,
-                    DateCreated = DateTime.UtcNow,
-                    Name = name,
-                    SpecialFolderType = type,
-                    ItemTypeName = itemType
-                };
-
-                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
-
-                await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-            }
-
-            return item;
+            return GetUserView(null, type, user, sortName, cancellationToken);
         }
     }
 }

+ 8 - 3
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -859,6 +859,9 @@
     "ViewTypeMovieCollections":  "Collections",
     "ViewTypeMovieFavorites":  "Favorites",
     "ViewTypeMovieGenres":  "Genres",
+    "ViewTypeMusicLatest":  "Latest",
+    "ViewTypeMusicAlbums":  "Albums",
+    "ViewTypeMusicAlbumArtists":  "Album Artists",
     "HeaderOtherDisplaySettings": "Display Settings",
     "HeaderMyViews": "My Views",
     "LabelSelectFolderGroups": "Automatically group content from the following folders into views such as Movies, Music and TV:",
@@ -1109,12 +1112,14 @@
     "HeaderMetadataSettings": "Metadata Settings",
     "LabelLockItemToPreventChanges": "Lock this item to prevent future changes",
     "MessageLeaveEmptyToInherit": "Leave empty to inherit settings from a parent item, or the global default value.",
-    "TabSupporterClub": "Supporter Club",
+    "TabDonate": "Donate",
     "HeaderDonationType": "Donation type:",
     "OptionMakeOneTimeDonation": "Make a separate donation",
     "OptionOneTimeDescription": "This is an additional donation to the team to show your support. It does not have any additional benefits.",
-    "OptionLifeTimeSupporterClubMembership": "Lifetime supporter club membership",
-    "HeaderSupporterBenefit": "Becoming a supporter club member provides additional benefits such as access to premium plugins, internet channel content, and more.",
+    "OptionLifeTimeSupporterMembership": "Lifetime supporter membership",
+    "OptionYearlySupporterMembership": "Yearly supporter membership",
+    "OptionMonthlySupporterMembership": "Monthly supporter membership",
+    "HeaderSupporterBenefit": "A supporter membership provides additional benefits such as access to premium plugins, internet channel content, and more.",
     "OptionNoTrailer": "No Trailer",
     "OptionNoThemeSong": "No Theme Song",
     "OptionNoThemeVideo": "No Theme Video",

+ 11 - 3
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Implementations;
+using MediaBrowser.Common.Implementations.Devices;
 using MediaBrowser.Common.Implementations.ScheduledTasks;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
@@ -903,11 +904,18 @@ namespace MediaBrowser.ServerApplication
             }
         }
 
-        private readonly string _systemId = Environment.MachineName.GetMD5().ToString();
-
+        private DeviceId _serverId;
         public string ServerId
         {
-            get { return _systemId; }
+            get
+            {
+                if (_serverId == null)
+                {
+                    _serverId = new DeviceId(ApplicationPaths, LogManager.GetLogger("ServerId"));
+                }
+
+                return _serverId.Value;
+            }
         }
 
         /// <summary>