diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 3e9a0926be..c51d9e7c01 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -37,11 +37,14 @@ namespace MediaBrowser.Api private readonly ISessionManager _sessionManager; + public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1); + /// /// Initializes a new instance of the class. /// /// The logger. /// The application paths. + /// The session manager. public ApiEntryPoint(ILogger logger, IServerApplicationPaths appPaths, ISessionManager sessionManager) { Logger = logger; @@ -99,7 +102,7 @@ namespace MediaBrowser.Api { var jobCount = _activeTranscodingJobs.Count; - Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, true)); + Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, path => true)); // Try to allow for some time to kill the ffmpeg processes and delete the partial stream files if (jobCount > 0) @@ -119,14 +122,12 @@ namespace MediaBrowser.Api /// The path. /// The type. /// The process. - /// The start time ticks. /// The device id. /// The state. /// The cancellation token source. public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, - long? startTimeTicks, string deviceId, StreamState state, CancellationTokenSource cancellationTokenSource) @@ -139,7 +140,6 @@ namespace MediaBrowser.Api Path = path, Process = process, ActiveRequestCount = 1, - StartTimeTicks = startTimeTicks, DeviceId = deviceId, CancellationTokenSource = cancellationTokenSource }); @@ -214,10 +214,15 @@ namespace MediaBrowser.Api /// The type. /// true if [has active transcoding job] [the specified path]; otherwise, false. public bool HasActiveTranscodingJob(string path, TranscodingJobType type) + { + return GetTranscodingJob(path, type) != null; + } + + public TranscodingJob GetTranscodingJob(string path, TranscodingJobType type) { lock (_activeTranscodingJobs) { - return _activeTranscodingJobs.Any(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); + return _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); } } @@ -290,34 +295,70 @@ namespace MediaBrowser.Api { var job = (TranscodingJob)state; - KillTranscodingJob(job, true); + KillTranscodingJob(job, path => true); } /// /// Kills the single transcoding job. /// /// The device id. - /// if set to true [delete files]. + /// The delete files. + /// if set to true [acquire lock]. + /// Task. + /// deviceId /// sourcePath - internal void KillTranscodingJobs(string deviceId, bool deleteFiles) + internal Task KillTranscodingJobs(string deviceId, Func deleteFiles, bool acquireLock) { if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException("deviceId"); } + return KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles, acquireLock); + } + + /// + /// Kills the transcoding jobs. + /// + /// The kill job. + /// The delete files. + /// if set to true [acquire lock]. + /// Task. + /// deviceId + internal async Task KillTranscodingJobs(Func killJob, Func deleteFiles, bool acquireLock) + { var jobs = new List(); lock (_activeTranscodingJobs) { // This is really only needed for HLS. // Progressive streams can stop on their own reliably - jobs.AddRange(_activeTranscodingJobs.Where(i => string.Equals(deviceId, i.DeviceId, StringComparison.OrdinalIgnoreCase))); + jobs.AddRange(_activeTranscodingJobs.Where(killJob)); } - foreach (var job in jobs) + if (jobs.Count == 0) { - KillTranscodingJob(job, deleteFiles); + return; + } + + if (acquireLock) + { + await TranscodingStartLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); + } + + try + { + foreach (var job in jobs) + { + KillTranscodingJob(job, deleteFiles); + } + } + finally + { + if (acquireLock) + { + TranscodingStartLock.Release(); + } } } @@ -325,8 +366,8 @@ namespace MediaBrowser.Api /// Kills the transcoding job. /// /// The job. - /// if set to true [delete files]. - private void KillTranscodingJob(TranscodingJob job, bool deleteFiles) + /// The delete. + private void KillTranscodingJob(TranscodingJob job, Func delete) { lock (_activeTranscodingJobs) { @@ -378,7 +419,7 @@ namespace MediaBrowser.Api } } - if (deleteFiles) + if (delete(job.Path)) { DeletePartialStreamFiles(job.Path, job.Type, 0, 1500); } @@ -386,7 +427,7 @@ namespace MediaBrowser.Api private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs) { - if (retryCount >= 8) + if (retryCount >= 10) { return; } @@ -440,6 +481,8 @@ namespace MediaBrowser.Api .Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1) .ToList(); + Exception e = null; + foreach (var file in filesToDelete) { try @@ -449,9 +492,15 @@ namespace MediaBrowser.Api } catch (IOException ex) { + e = ex; Logger.ErrorException("Error deleting HLS file {0}", ex, file); } } + + if (e != null) + { + throw e; + } } } @@ -486,12 +535,13 @@ namespace MediaBrowser.Api /// The kill timer. public Timer KillTimer { get; set; } - public long? StartTimeTicks { get; set; } public string DeviceId { get; set; } public CancellationTokenSource CancellationTokenSource { get; set; } public object ProcessLock = new object(); + + public bool HasExited { get; set; } } /// diff --git a/MediaBrowser.Api/AppThemeService.cs b/MediaBrowser.Api/AppThemeService.cs index 4d8eed7ddb..87084e415a 100644 --- a/MediaBrowser.Api/AppThemeService.cs +++ b/MediaBrowser.Api/AppThemeService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Themes; using MediaBrowser.Model.Themes; using ServiceStack; @@ -47,6 +48,7 @@ namespace MediaBrowser.Api { } + [Authenticated] public class AppThemeService : BaseApiService { private readonly IAppThemeManager _themeManager; @@ -92,7 +94,7 @@ namespace MediaBrowser.Api var contentType = MimeTypes.GetMimeType(info.Path); - return ToCachedResult(cacheGuid, info.DateModified, cacheDuration, () => _fileSystem.GetFileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read), contentType); + return ResultFactory.GetCachedResult(Request, cacheGuid, null, cacheDuration, () => _fileSystem.GetFileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read), contentType); } } } diff --git a/MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs b/MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs deleted file mode 100644 index 6c56083cbf..0000000000 --- a/MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs +++ /dev/null @@ -1,190 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Logging; -using ServiceStack.Web; -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Api -{ - public class AuthorizationRequestFilterAttribute : Attribute, IHasRequestFilter - { - //This property will be resolved by the IoC container - /// - /// Gets or sets the user manager. - /// - /// The user manager. - public IUserManager UserManager { get; set; } - - public ISessionManager SessionManager { get; set; } - - /// - /// Gets or sets the logger. - /// - /// The logger. - public ILogger Logger { get; set; } - - /// - /// The request filter is executed before the service. - /// - /// The http request wrapper - /// The http response wrapper - /// The request DTO - public void RequestFilter(IRequest request, IResponse response, object requestDto) - { - //This code is executed before the service - var auth = GetAuthorizationDictionary(request); - - if (auth != null) - { - User user = null; - - if (auth.ContainsKey("UserId")) - { - var userId = auth["UserId"]; - - if (!string.IsNullOrEmpty(userId)) - { - user = UserManager.GetUserById(new Guid(userId)); - } - } - - string deviceId; - string device; - string client; - string version; - - auth.TryGetValue("DeviceId", out deviceId); - auth.TryGetValue("Device", out device); - auth.TryGetValue("Client", out client); - auth.TryGetValue("Version", out version); - - if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version)) - { - var remoteEndPoint = request.RemoteIp; - - SessionManager.LogSessionActivity(client, version, deviceId, device, remoteEndPoint, user); - } - } - } - - /// - /// Gets the auth. - /// - /// The HTTP req. - /// Dictionary{System.StringSystem.String}. - private static Dictionary GetAuthorizationDictionary(IRequest httpReq) - { - var auth = httpReq.Headers["Authorization"]; - - return GetAuthorization(auth); - } - - public static User GetCurrentUser(IRequest httpReq, IUserManager userManager) - { - var info = GetAuthorization(httpReq); - - return string.IsNullOrEmpty(info.UserId) ? null : - userManager.GetUserById(new Guid(info.UserId)); - } - - /// - /// Gets the authorization. - /// - /// The HTTP req. - /// Dictionary{System.StringSystem.String}. - public static AuthorizationInfo GetAuthorization(IRequest httpReq) - { - var auth = GetAuthorizationDictionary(httpReq); - - string userId = null; - string deviceId = null; - string device = null; - string client = null; - string version = null; - - if (auth != null) - { - auth.TryGetValue("UserId", out userId); - auth.TryGetValue("DeviceId", out deviceId); - auth.TryGetValue("Device", out device); - auth.TryGetValue("Client", out client); - auth.TryGetValue("Version", out version); - } - - return new AuthorizationInfo - { - Client = client, - Device = device, - DeviceId = deviceId, - UserId = userId, - Version = version - }; - } - - /// - /// Gets the authorization. - /// - /// The authorization header. - /// Dictionary{System.StringSystem.String}. - private static Dictionary GetAuthorization(string authorizationHeader) - { - if (authorizationHeader == null) return null; - - var parts = authorizationHeader.Split(' '); - - // There should be at least to parts - if (parts.Length < 2) return null; - - // It has to be a digest request - if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - // Remove uptil the first space - authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' ')); - parts = authorizationHeader.Split(','); - - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var item in parts) - { - var param = item.Trim().Split(new[] { '=' }, 2); - result.Add(param[0], param[1].Trim(new[] { '"' })); - } - - return result; - } - - /// - /// A new shallow copy of this filter is used on every request. - /// - /// IHasRequestFilter. - public IHasRequestFilter Copy() - { - return this; - } - - /// - /// Order in which Request Filters are executed. - /// <0 Executed before global request filters - /// >0 Executed after global request filters - /// - /// The priority. - public int Priority - { - get { return 0; } - } - } - - public class AuthorizationInfo - { - public string UserId; - public string DeviceId; - public string Device; - public string Client; - public string Version; - } -} diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index ca5b8b63a4..1af7054d95 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -14,8 +14,7 @@ namespace MediaBrowser.Api /// /// Class BaseApiService /// - [AuthorizationRequestFilter] - public class BaseApiService : IHasResultFactory, IRestfulService + public class BaseApiService : IHasResultFactory, IRestfulService, IHasSession { /// /// Gets or sets the logger. @@ -35,6 +34,8 @@ namespace MediaBrowser.Api /// The request context. public IRequest Request { get; set; } + public ISessionContext SessionContext { get; set; } + public string GetHeader(string name) { return Request.Headers[name]; @@ -82,33 +83,18 @@ namespace MediaBrowser.Api /// /// Gets the session. /// - /// The session manager. /// SessionInfo. - protected SessionInfo GetSession(ISessionManager sessionManager) + /// Session not found. + protected SessionInfo GetSession() { - var auth = AuthorizationRequestFilterAttribute.GetAuthorization(Request); + var session = SessionContext.GetSession(Request); - return sessionManager.Sessions.First(i => string.Equals(i.DeviceId, auth.DeviceId) && - string.Equals(i.Client, auth.Client) && - string.Equals(i.ApplicationVersion, auth.Version)); - } + if (session == null) + { + throw new ArgumentException("Session not found."); + } - /// - /// To the cached result. - /// - /// - /// The cache key. - /// The last date modified. - /// Duration of the cache. - /// The factory fn. - /// Type of the content. - /// The response headers. - /// System.Object. - /// cacheKey - protected object ToCachedResult(Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func factoryFn, string contentType, IDictionary responseHeaders = null) - where T : class - { - return ResultFactory.GetCachedResult(Request, cacheKey, lastDateModified, cacheDuration, factoryFn, contentType, responseHeaders); + return session; } /// @@ -121,7 +107,7 @@ namespace MediaBrowser.Api return ResultFactory.GetStaticFileResult(Request, path); } - private readonly char[] _dashReplaceChars = new[] { '?', '/' }; + private readonly char[] _dashReplaceChars = { '?', '/' }; private const char SlugChar = '-'; protected MusicArtist GetArtist(string name, ILibraryManager libraryManager) @@ -154,7 +140,7 @@ namespace MediaBrowser.Api return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager)); } - protected IList GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) + protected IEnumerable GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) { if (!string.IsNullOrEmpty(parentId)) { @@ -164,7 +150,12 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); - return folder.GetRecursiveChildren(user).ToList(); + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return folder.GetRecursiveChildren(user); } return folder.GetRecursiveChildren(); @@ -173,7 +164,12 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); - return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user, null); + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); } return libraryManager.RootFolder.GetRecursiveChildren(); @@ -234,7 +230,8 @@ namespace MediaBrowser.Api return name; } - return libraryManager.RootFolder.GetRecursiveChildren(i => i is Game) + return libraryManager.RootFolder.GetRecursiveChildren() + .OfType() .SelectMany(i => i.Genres) .Distinct(StringComparer.OrdinalIgnoreCase) .FirstOrDefault(i => diff --git a/MediaBrowser.Api/BrandingService.cs b/MediaBrowser.Api/BrandingService.cs new file mode 100644 index 0000000000..4b49b411a8 --- /dev/null +++ b/MediaBrowser.Api/BrandingService.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Branding; +using ServiceStack; + +namespace MediaBrowser.Api +{ + [Route("/Branding/Configuration", "GET", Summary = "Gets branding configuration")] + public class GetBrandingOptions : IReturn + { + } + + public class BrandingService : BaseApiService + { + private readonly IConfigurationManager _config; + + public BrandingService(IConfigurationManager config) + { + _config = config; + } + + public object Get(GetBrandingOptions request) + { + var result = _config.GetConfiguration("branding"); + + return ToOptimizedResult(result); + } + } +} diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index d71db929fe..2dfa0918d6 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -8,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Api { @@ -172,7 +174,8 @@ namespace MediaBrowser.Api [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string UserId { get; set; } } - + + [Authenticated] public class ChannelService : BaseApiService { private readonly IChannelManager _channelManager; @@ -196,14 +199,14 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - public object Get(GetChannelFolder request) + public async Task Get(GetChannelFolder request) { - return ToOptimizedResult(_channelManager.GetChannelFolder(request.UserId, CancellationToken.None).Result); + return ToOptimizedResult(await _channelManager.GetChannelFolder(request.UserId, CancellationToken.None).ConfigureAwait(false)); } - - public object Get(GetChannels request) + + public async Task Get(GetChannels request) { - var result = _channelManager.GetChannels(new ChannelQuery + var result = await _channelManager.GetChannels(new ChannelQuery { Limit = request.Limit, StartIndex = request.StartIndex, @@ -211,14 +214,14 @@ namespace MediaBrowser.Api SupportsLatestItems = request.SupportsLatestItems, IsFavorite = request.IsFavorite - }, CancellationToken.None).Result; + }, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } - public object Get(GetChannelItems request) + public async Task Get(GetChannelItems request) { - var result = _channelManager.GetChannelItems(new ChannelItemQuery + var result = await _channelManager.GetChannelItems(new ChannelItemQuery { Limit = request.Limit, StartIndex = request.StartIndex, @@ -228,16 +231,16 @@ namespace MediaBrowser.Api SortOrder = request.SortOrder, SortBy = (request.SortBy ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(), Filters = request.GetFilters().ToArray(), - Fields = request.GetItemFields().ToList() + Fields = request.GetItemFields().ToArray() - }, CancellationToken.None).Result; + }, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } - public object Get(GetLatestChannelItems request) + public async Task Get(GetLatestChannelItems request) { - var result = _channelManager.GetLatestChannelItems(new AllChannelMediaQuery + var result = await _channelManager.GetLatestChannelItems(new AllChannelMediaQuery { Limit = request.Limit, StartIndex = request.StartIndex, @@ -246,7 +249,7 @@ namespace MediaBrowser.Api Filters = request.GetFilters().ToArray(), Fields = request.GetItemFields().ToList() - }, CancellationToken.None).Result; + }, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index b3191cd4b9..7b6e5ed19e 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -1,15 +1,17 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Serialization; using ServiceStack; +using ServiceStack.Text.Controller; +using ServiceStack.Web; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace MediaBrowser.Api @@ -18,35 +20,58 @@ namespace MediaBrowser.Api /// Class GetConfiguration /// [Route("/System/Configuration", "GET", Summary = "Gets application configuration")] + [Authenticated] public class GetConfiguration : IReturn { } + [Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")] + [Authenticated] + public class GetNamedConfiguration + { + [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Key { get; set; } + } + /// /// Class UpdateConfiguration /// [Route("/System/Configuration", "POST", Summary = "Updates application configuration")] + [Authenticated] public class UpdateConfiguration : ServerConfiguration, IReturnVoid { } + [Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")] + [Authenticated] + public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream + { + [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Key { get; set; } + + public Stream RequestStream { get; set; } + } + [Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")] + [Authenticated] public class GetDefaultMetadataOptions : IReturn { } [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")] + [Authenticated] public class GetMetadataPlugins : IReturn> { } - [Route("/System/Configuration/VideoImageExtraction", "POST", Summary = "Updates image extraction for all types")] - public class UpdateVideoImageExtraction : IReturnVoid + [Route("/System/Configuration/MetadataPlugins/Autoset", "POST")] + [Authenticated] + public class AutoSetMetadataOptions : IReturnVoid { - public bool Enabled { get; set; } + } public class ConfigurationService : BaseApiService @@ -63,13 +88,15 @@ namespace MediaBrowser.Api private readonly IFileSystem _fileSystem; private readonly IProviderManager _providerManager; + private readonly ILibraryManager _libraryManager; - public ConfigurationService(IJsonSerializer jsonSerializer, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IProviderManager providerManager) + public ConfigurationService(IJsonSerializer jsonSerializer, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IProviderManager providerManager, ILibraryManager libraryManager) { _jsonSerializer = jsonSerializer; _configurationManager = configurationManager; _fileSystem = fileSystem; _providerManager = providerManager; + _libraryManager = libraryManager; } /// @@ -88,6 +115,60 @@ namespace MediaBrowser.Api return ToOptimizedResultUsingCache(cacheKey, dateModified, null, () => _configurationManager.Configuration); } + public object Get(GetNamedConfiguration request) + { + var result = _configurationManager.GetConfiguration(request.Key); + + return ToOptimizedResult(result); + } + + const string XbmcMetadata = "Xbmc Nfo"; + const string MediaBrowserMetadata = "Media Browser Xml"; + + public void Post(AutoSetMetadataOptions request) + { + var service = AutoDetectMetadataService(); + + Logger.Info("Setting preferred metadata format to " + service); + + var serviceToDisable = string.Equals(service, XbmcMetadata) ? + MediaBrowserMetadata : + XbmcMetadata; + + _configurationManager.DisableMetadataService(serviceToDisable); + _configurationManager.SaveConfiguration(); + } + + private string AutoDetectMetadataService() + { + try + { + var paths = _libraryManager.GetDefaultVirtualFolders() + .SelectMany(i => i.Locations) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(i => new DirectoryInfo(i)) + .ToList(); + + if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories)) + .Any()) + { + return XbmcMetadata; + } + + if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories)) + .Any(i => string.Equals(i.Name, "series.xml", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "movie.xml", StringComparison.OrdinalIgnoreCase))) + { + return MediaBrowserMetadata; + } + } + catch (Exception) + { + + } + + return XbmcMetadata; + } + /// /// Posts the specified configuraiton. /// @@ -95,7 +176,6 @@ namespace MediaBrowser.Api public void Post(UpdateConfiguration request) { // Silly, but we need to serialize and deserialize or the XmlSerializer will write the xml with an element name of UpdateConfiguration - var json = _jsonSerializer.SerializeToString(request); var config = _jsonSerializer.DeserializeFromString(json); @@ -103,6 +183,17 @@ namespace MediaBrowser.Api _configurationManager.ReplaceConfiguration(config); } + public void Post(UpdateNamedConfiguration request) + { + var pathInfo = PathInfo.Parse(Request.PathInfo); + var key = pathInfo.GetArgumentValue(2); + + var configurationType = _configurationManager.GetConfigurationType(key); + var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType); + + _configurationManager.SaveConfiguration(key, configuration); + } + public object Get(GetDefaultMetadataOptions request) { return ToOptimizedSerializedResultUsingCache(new MetadataOptions()); @@ -112,71 +203,5 @@ namespace MediaBrowser.Api { return ToOptimizedSerializedResultUsingCache(_providerManager.GetAllMetadataPlugins().ToList()); } - - /// - /// This is a temporary method used until image settings get broken out. - /// - /// - public void Post(UpdateVideoImageExtraction request) - { - var config = _configurationManager.Configuration; - - EnableImageExtractionForType(typeof(Movie), config, request.Enabled); - EnableImageExtractionForType(typeof(Episode), config, request.Enabled); - EnableImageExtractionForType(typeof(AdultVideo), config, request.Enabled); - EnableImageExtractionForType(typeof(MusicVideo), config, request.Enabled); - EnableImageExtractionForType(typeof(Video), config, request.Enabled); - EnableImageExtractionForType(typeof(Trailer), config, request.Enabled); - - _configurationManager.SaveConfiguration(); - } - - private void EnableImageExtractionForType(Type type, ServerConfiguration config, bool enabled) - { - var options = GetMetadataOptions(type, config); - - const string imageProviderName = "Screen Grabber"; - - var contains = options.DisabledImageFetchers.Contains(imageProviderName, StringComparer.OrdinalIgnoreCase); - - if (!enabled && !contains) - { - var list = options.DisabledImageFetchers.ToList(); - - list.Add(imageProviderName); - - options.DisabledImageFetchers = list.ToArray(); - } - else if (enabled && contains) - { - var list = options.DisabledImageFetchers.ToList(); - - list.Remove(imageProviderName); - - options.DisabledImageFetchers = list.ToArray(); - } - } - - private MetadataOptions GetMetadataOptions(Type type, ServerConfiguration config) - { - var options = config.MetadataOptions - .FirstOrDefault(i => string.Equals(i.ItemType, type.Name, StringComparison.OrdinalIgnoreCase)); - - if (options == null) - { - var list = config.MetadataOptions.ToList(); - - options = new MetadataOptions - { - ItemType = type.Name - }; - - list.Add(options); - - config.MetadataOptions = list.ToArray(); - } - - return options; - } } } diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs deleted file mode 100644 index 6acecd342d..0000000000 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ /dev/null @@ -1,671 +0,0 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using ServiceStack; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace MediaBrowser.Api.DefaultTheme -{ - [Route("/MBT/DefaultTheme/Games", "GET")] - public class GetGamesView : IReturn - { - [ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - - [ApiMember(Name = "RecentlyPlayedGamesLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int RecentlyPlayedGamesLimit { get; set; } - - public string ParentId { get; set; } - } - - [Route("/MBT/DefaultTheme/TV", "GET")] - public class GetTvView : IReturn - { - [ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - - [ApiMember(Name = "ComedyGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ComedyGenre { get; set; } - - [ApiMember(Name = "RomanceGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string RomanceGenre { get; set; } - - [ApiMember(Name = "TopCommunityRating", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public double TopCommunityRating { get; set; } - - [ApiMember(Name = "NextUpEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int NextUpEpisodeLimit { get; set; } - - [ApiMember(Name = "ResumableEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int ResumableEpisodeLimit { get; set; } - - [ApiMember(Name = "LatestEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int LatestEpisodeLimit { get; set; } - - public string ParentId { get; set; } - } - - [Route("/MBT/DefaultTheme/Movies", "GET")] - public class GetMovieView : IReturn - { - [ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - - [ApiMember(Name = "FamilyGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string FamilyGenre { get; set; } - - [ApiMember(Name = "ComedyGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ComedyGenre { get; set; } - - [ApiMember(Name = "RomanceGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string RomanceGenre { get; set; } - - [ApiMember(Name = "LatestMoviesLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int LatestMoviesLimit { get; set; } - - [ApiMember(Name = "LatestTrailersLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int LatestTrailersLimit { get; set; } - - public string ParentId { get; set; } - } - - [Route("/MBT/DefaultTheme/Favorites", "GET")] - public class GetFavoritesView : IReturn - { - [ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - } - - public class DefaultThemeService : BaseApiService - { - private readonly IUserManager _userManager; - private readonly IDtoService _dtoService; - private readonly ILogger _logger; - private readonly ILibraryManager _libraryManager; - private readonly IUserDataManager _userDataManager; - - private readonly IImageProcessor _imageProcessor; - private readonly IItemRepository _itemRepo; - - public DefaultThemeService(IUserManager userManager, IDtoService dtoService, ILogger logger, ILibraryManager libraryManager, IImageProcessor imageProcessor, IUserDataManager userDataManager, IItemRepository itemRepo) - { - _userManager = userManager; - _dtoService = dtoService; - _logger = logger; - _libraryManager = libraryManager; - _imageProcessor = imageProcessor; - _userDataManager = userDataManager; - _itemRepo = itemRepo; - } - - public object Get(GetFavoritesView request) - { - var user = _userManager.GetUserById(request.UserId); - - var allItems = user.RootFolder.GetRecursiveChildren(user) - .ToList(); - - var allFavoriteItems = allItems.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite) - .ToList(); - - var itemsWithImages = allFavoriteItems.Where(i => !string.IsNullOrEmpty(i.PrimaryImagePath)) - .ToList(); - - var itemsWithBackdrops = allFavoriteItems.Where(i => i.GetImages(ImageType.Backdrop).Any()) - .ToList(); - - var view = new FavoritesView(); - - var fields = new List(); - - view.BackdropItems = FilterItemsForBackdropDisplay(itemsWithBackdrops) - .Randomize("backdrop") - .Take(10) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - var spotlightItems = itemsWithBackdrops.Randomize("spotlight") - .Take(10) - .ToList(); - - view.SpotlightItems = spotlightItems - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - fields.Add(ItemFields.PrimaryImageAspectRatio); - - view.Albums = itemsWithImages - .OfType() - .Randomize() - .Take(4) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - view.Books = itemsWithImages - .OfType() - .Randomize() - .Take(6) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - view.Episodes = itemsWithImages - .OfType() - .Randomize() - .Take(6) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - view.Games = itemsWithImages - .OfType() - .Randomize() - .Take(6) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - view.Movies = itemsWithImages - .OfType() - .Randomize() - .Take(6) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - view.Series = itemsWithImages - .OfType() - .Randomize() - .Take(6) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - - view.Songs = itemsWithImages - .OfType