Merge pull request #1005 from MediaBrowser/dev

3.0.5518.0
This commit is contained in:
Luke 2015-02-09 16:58:30 -05:00
commit 4cc3b2f0cc
499 changed files with 36502 additions and 25590 deletions

View File

@ -1,10 +1,10 @@
using MediaBrowser.Api.Playback; using MediaBrowser.Api.Playback;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using System; using System;
@ -39,6 +39,7 @@ namespace MediaBrowser.Api
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
private readonly IFileSystem _fileSystem;
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1); public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
@ -48,11 +49,12 @@ namespace MediaBrowser.Api
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
/// <param name="sessionManager">The session manager.</param> /// <param name="sessionManager">The session manager.</param>
/// <param name="config">The configuration.</param> /// <param name="config">The configuration.</param>
public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config) public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem)
{ {
Logger = logger; Logger = logger;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_config = config; _config = config;
_fileSystem = fileSystem;
Instance = this; Instance = this;
} }
@ -86,12 +88,12 @@ namespace MediaBrowser.Api
/// </summary> /// </summary>
private void DeleteEncodedMediaCache() private void DeleteEncodedMediaCache()
{ {
var path = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower()); var path = _config.ApplicationPaths.TranscodingTempPath;
foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
.ToList()) .ToList())
{ {
File.Delete(file); _fileSystem.DeleteFile(file);
} }
} }
@ -462,7 +464,7 @@ namespace MediaBrowser.Api
/// <param name="outputFilePath">The output file path.</param> /// <param name="outputFilePath">The output file path.</param>
private void DeleteProgressivePartialStreamFiles(string outputFilePath) private void DeleteProgressivePartialStreamFiles(string outputFilePath)
{ {
File.Delete(outputFilePath); _fileSystem.DeleteFile(outputFilePath);
} }
/// <summary> /// <summary>
@ -485,7 +487,7 @@ namespace MediaBrowser.Api
try try
{ {
Logger.Info("Deleting HLS file {0}", file); Logger.Info("Deleting HLS file {0}", file);
File.Delete(file); _fileSystem.DeleteFile(file);
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {

View File

@ -1,9 +1,12 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using ServiceStack.Text.Controller;
using ServiceStack.Web; using ServiceStack.Web;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -35,6 +38,7 @@ namespace MediaBrowser.Api
public IRequest Request { get; set; } public IRequest Request { get; set; }
public ISessionContext SessionContext { get; set; } public ISessionContext SessionContext { get; set; }
public IAuthorizationContext AuthorizationContext { get; set; }
public string GetHeader(string name) public string GetHeader(string name)
{ {
@ -109,6 +113,37 @@ namespace MediaBrowser.Api
private readonly char[] _dashReplaceChars = { '?', '/', '&' }; private readonly char[] _dashReplaceChars = { '?', '/', '&' };
private const char SlugChar = '-'; private const char SlugChar = '-';
protected DtoOptions GetDtoOptions(object request)
{
var options = new DtoOptions();
options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
var hasFields = request as IHasItemFields;
if (hasFields != null)
{
options.Fields = hasFields.GetItemFields().ToList();
}
var hasDtoOptions = request as IHasDtoOptions;
if (hasDtoOptions != null)
{
options.EnableImages = hasDtoOptions.EnableImages ?? true;
if (hasDtoOptions.ImageTypeLimit.HasValue)
{
options.ImageTypeLimit = hasDtoOptions.ImageTypeLimit.Value;
}
if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes))
{
options.ImageTypes = (hasDtoOptions.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
}
}
return options;
}
protected MusicArtist GetArtist(string name, ILibraryManager libraryManager) protected MusicArtist GetArtist(string name, ILibraryManager libraryManager)
{ {
return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager)); return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager));
@ -139,11 +174,11 @@ namespace MediaBrowser.Api
return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager)); return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
} }
protected IEnumerable<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) protected IList<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
{ {
if (!string.IsNullOrEmpty(parentId)) if (!string.IsNullOrEmpty(parentId))
{ {
var folder = (Folder) libraryManager.GetItemById(new Guid(parentId)); var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
if (userId.HasValue) if (userId.HasValue)
{ {
@ -154,10 +189,13 @@ namespace MediaBrowser.Api
throw new ArgumentException("User not found"); throw new ArgumentException("User not found");
} }
return folder.GetRecursiveChildren(user); return folder
.GetRecursiveChildren(user, filter)
.ToList();
} }
return folder.GetRecursiveChildren(); return folder
.GetRecursiveChildren(filter);
} }
if (userId.HasValue) if (userId.HasValue)
{ {
@ -168,10 +206,16 @@ namespace MediaBrowser.Api
throw new ArgumentException("User not found"); throw new ArgumentException("User not found");
} }
return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); return userManager
.GetUserById(userId.Value)
.RootFolder
.GetRecursiveChildren(user, filter)
.ToList();
} }
return libraryManager.RootFolder.GetRecursiveChildren(); return libraryManager
.RootFolder
.GetRecursiveChildren(filter);
} }
/// <summary> /// <summary>
@ -187,8 +231,9 @@ namespace MediaBrowser.Api
return name; return name;
} }
return libraryManager.RootFolder.RecursiveChildren return libraryManager.RootFolder
.OfType<Audio>() .GetRecursiveChildren(i => i is IHasArtist)
.Cast<IHasArtist>()
.SelectMany(i => i.AllArtists) .SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i => .FirstOrDefault(i =>
@ -229,8 +274,8 @@ namespace MediaBrowser.Api
return name; return name;
} }
return libraryManager.RootFolder.GetRecursiveChildren() return libraryManager.RootFolder
.OfType<Game>() .GetRecursiveChildren(i => i is Game)
.SelectMany(i => i.Genres) .SelectMany(i => i.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i => .FirstOrDefault(i =>
@ -252,7 +297,8 @@ namespace MediaBrowser.Api
return name; return name;
} }
return libraryManager.RootFolder.GetRecursiveChildren() return libraryManager.RootFolder
.GetRecursiveChildren()
.SelectMany(i => i.Studios) .SelectMany(i => i.Studios)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i => .FirstOrDefault(i =>
@ -274,7 +320,8 @@ namespace MediaBrowser.Api
return name; return name;
} }
return libraryManager.RootFolder.GetRecursiveChildren() return libraryManager.RootFolder
.GetRecursiveChildren()
.SelectMany(i => i.People) .SelectMany(i => i.People)
.Select(i => i.Name) .Select(i => i.Name)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
@ -287,6 +334,20 @@ namespace MediaBrowser.Api
}) ?? name; }) ?? name;
} }
protected string GetPathValue(int index)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var first = pathInfo.GetArgumentValue<string>(0);
// backwards compatibility
if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase))
{
index++;
}
return pathInfo.GetArgumentValue<string>(index);
}
/// <summary> /// <summary>
/// Gets the name of the item by. /// Gets the name of the item by.
/// </summary> /// </summary>
@ -294,7 +355,6 @@ namespace MediaBrowser.Api
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <returns>Task{BaseItem}.</returns> /// <returns>Task{BaseItem}.</returns>
/// <exception cref="System.ArgumentException"></exception>
protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager) protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager)
{ {
BaseItem item; BaseItem item;

View File

@ -9,6 +9,11 @@ namespace MediaBrowser.Api
{ {
} }
[Route("/Branding/Css", "GET", Summary = "Gets custom css")]
public class GetBrandingCss
{
}
public class BrandingService : BaseApiService public class BrandingService : BaseApiService
{ {
private readonly IConfigurationManager _config; private readonly IConfigurationManager _config;
@ -24,5 +29,12 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result); return ToOptimizedResult(result);
} }
public object Get(GetBrandingCss request)
{
var result = _config.GetConfiguration<BrandingOptions>("branding");
return ResultFactory.GetResult(result.CustomCss, "text/css");
}
} }
} }

View File

@ -143,8 +143,7 @@ namespace MediaBrowser.Api
public void Post(UpdateNamedConfiguration request) public void Post(UpdateNamedConfiguration request)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var key = GetPathValue(2);
var key = pathInfo.GetArgumentValue<string>(2);
var configurationType = _configurationManager.GetConfigurationType(key); var configurationType = _configurationManager.GetConfigurationType(key);
var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType); var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType);

View File

@ -39,11 +39,11 @@ namespace MediaBrowser.Api
[ApiMember(Name = "SendingUserId", Description = "Sending User Id", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] [ApiMember(Name = "SendingUserId", Description = "Sending User Id", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string SendingUserId { get; set; } public string SendingUserId { get; set; }
[ApiMember(Name = "ExcludeLibraries", Description = "ExcludeLibraries", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] [ApiMember(Name = "EnabledLibraries", Description = "EnabledLibraries", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string ExcludedLibraries { get; set; } public string EnabledLibraries { get; set; }
[ApiMember(Name = "ExcludedChannels", Description = "ExcludedChannels", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] [ApiMember(Name = "EnabledChannels", Description = "EnabledChannels", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string ExcludedChannels { get; set; } public string EnabledChannels { get; set; }
[ApiMember(Name = "EnableLiveTv", Description = "EnableLiveTv", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] [ApiMember(Name = "EnableLiveTv", Description = "EnableLiveTv", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public bool EnableLiveTv { get; set; } public bool EnableLiveTv { get; set; }
@ -91,12 +91,12 @@ namespace MediaBrowser.Api
public object Post(CreateConnectInvite request) public object Post(CreateConnectInvite request)
{ {
var excludeLibraries = (request.ExcludedLibraries ?? string.Empty) var enabledLibraries = (request.EnabledLibraries ?? string.Empty)
.Split(',') .Split(',')
.Where(i => !string.IsNullOrWhiteSpace(i)) .Where(i => !string.IsNullOrWhiteSpace(i))
.ToArray(); .ToArray();
var excludedChannels = (request.ExcludedChannels ?? string.Empty) var enabledChannels = (request.EnabledChannels ?? string.Empty)
.Split(',') .Split(',')
.Where(i => !string.IsNullOrWhiteSpace(i)) .Where(i => !string.IsNullOrWhiteSpace(i))
.ToArray(); .ToArray();
@ -105,8 +105,8 @@ namespace MediaBrowser.Api
{ {
ConnectUserName = request.ConnectUsername, ConnectUserName = request.ConnectUsername,
SendingUserId = request.SendingUserId, SendingUserId = request.SendingUserId,
ExcludedLibraries = excludeLibraries, EnabledLibraries = enabledLibraries,
ExcludedChannels = excludedChannels, EnabledChannels = enabledChannels,
EnableLiveTv = request.EnableLiveTv EnableLiveTv = request.EnableLiveTv
}); });
} }

View File

@ -1,6 +1,5 @@
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web; using ServiceStack.Web;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -19,19 +18,31 @@ namespace MediaBrowser.Api.Dlna
public string UuId { get; set; } public string UuId { get; set; }
} }
[Route("/Dlna/contentdirectory/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")] [Route("/Dlna/{UuId}/contentdirectory/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
[Route("/Dlna/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")] [Route("/Dlna/{UuId}/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
public class GetContentDirectory public class GetContentDirectory
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; }
} }
[Route("/Dlna/connectionmanager/connectionmanager.xml", "GET", Summary = "Gets dlna connection manager xml")] [Route("/Dlna/{UuId}/connectionmanager/connectionmanager.xml", "GET", Summary = "Gets dlna connection manager xml")]
[Route("/Dlna/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")] [Route("/Dlna/{UuId}/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")]
public class GetConnnectionManager public class GetConnnectionManager
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; }
} }
[Route("/Dlna/contentdirectory/{UuId}/control", "POST", Summary = "Processes a control request")] [Route("/Dlna/{UuId}/mediareceiverregistrar/mediareceiverregistrar.xml", "GET", Summary = "Gets dlna mediareceiverregistrar xml")]
[Route("/Dlna/{UuId}/mediareceiverregistrar/mediareceiverregistrar", "GET", Summary = "Gets dlna mediareceiverregistrar xml")]
public class GetMediaReceiverRegistrar
{
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; }
}
[Route("/Dlna/{UuId}/contentdirectory/control", "POST", Summary = "Processes a control request")]
public class ProcessContentDirectoryControlRequest : IRequiresRequestStream public class ProcessContentDirectoryControlRequest : IRequiresRequestStream
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -40,7 +51,7 @@ namespace MediaBrowser.Api.Dlna
public Stream RequestStream { get; set; } public Stream RequestStream { get; set; }
} }
[Route("/Dlna/connectionmanager/{UuId}/control", "POST", Summary = "Processes a control request")] [Route("/Dlna/{UuId}/connectionmanager/control", "POST", Summary = "Processes a control request")]
public class ProcessConnectionManagerControlRequest : IRequiresRequestStream public class ProcessConnectionManagerControlRequest : IRequiresRequestStream
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -49,23 +60,43 @@ namespace MediaBrowser.Api.Dlna
public Stream RequestStream { get; set; } public Stream RequestStream { get; set; }
} }
[Route("/Dlna/contentdirectory/{UuId}/events", Summary = "Processes an event subscription request")] [Route("/Dlna/{UuId}/mediareceiverregistrar/control", "POST", Summary = "Processes a control request")]
public class ProcessMediaReceiverRegistrarControlRequest : IRequiresRequestStream
{
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; }
public Stream RequestStream { get; set; }
}
[Route("/Dlna/{UuId}/mediareceiverregistrar/events", Summary = "Processes an event subscription request")]
public class ProcessMediaReceiverRegistrarEventRequest
{
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,POST")]
public string UuId { get; set; }
}
[Route("/Dlna/{UuId}/contentdirectory/events", Summary = "Processes an event subscription request")]
public class ProcessContentDirectoryEventRequest public class ProcessContentDirectoryEventRequest
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,POST")]
public string UuId { get; set; } public string UuId { get; set; }
} }
[Route("/Dlna/connectionmanager/{UuId}/events", Summary = "Processes an event subscription request")] [Route("/Dlna/{UuId}/connectionmanager/events", Summary = "Processes an event subscription request")]
public class ProcessConnectionManagerEventRequest public class ProcessConnectionManagerEventRequest
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,POST")]
public string UuId { get; set; } public string UuId { get; set; }
} }
[Route("/Dlna/{UuId}/icons/{Filename}", "GET", Summary = "Gets a server icon")]
[Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")] [Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
public class GetIcon public class GetIcon
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; }
[ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Filename { get; set; } public string Filename { get; set; }
} }
@ -75,17 +106,21 @@ namespace MediaBrowser.Api.Dlna
private readonly IDlnaManager _dlnaManager; private readonly IDlnaManager _dlnaManager;
private readonly IContentDirectory _contentDirectory; private readonly IContentDirectory _contentDirectory;
private readonly IConnectionManager _connectionManager; private readonly IConnectionManager _connectionManager;
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager) public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar)
{ {
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_contentDirectory = contentDirectory; _contentDirectory = contentDirectory;
_connectionManager = connectionManager; _connectionManager = connectionManager;
_mediaReceiverRegistrar = mediaReceiverRegistrar;
} }
public object Get(GetDescriptionXml request) public object Get(GetDescriptionXml request)
{ {
var xml = _dlnaManager.GetServerDescriptionXml(GetRequestHeaders(), request.UuId); var url = Request.AbsoluteUri;
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
var xml = _dlnaManager.GetServerDescriptionXml(GetRequestHeaders(), request.UuId, serverAddress);
return ResultFactory.GetResult(xml, "text/xml"); return ResultFactory.GetResult(xml, "text/xml");
} }
@ -97,6 +132,13 @@ namespace MediaBrowser.Api.Dlna
return ResultFactory.GetResult(xml, "text/xml"); return ResultFactory.GetResult(xml, "text/xml");
} }
public object Get(GetMediaReceiverRegistrar request)
{
var xml = _mediaReceiverRegistrar.GetServiceXml(GetRequestHeaders());
return ResultFactory.GetResult(xml, "text/xml");
}
public object Get(GetConnnectionManager request) public object Get(GetConnnectionManager request)
{ {
var xml = _connectionManager.GetServiceXml(GetRequestHeaders()); var xml = _connectionManager.GetServiceXml(GetRequestHeaders());
@ -104,6 +146,13 @@ namespace MediaBrowser.Api.Dlna
return ResultFactory.GetResult(xml, "text/xml"); return ResultFactory.GetResult(xml, "text/xml");
} }
public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request)
{
var response = await PostAsync(request.RequestStream, _mediaReceiverRegistrar).ConfigureAwait(false);
return ResultFactory.GetResult(response.Xml, "text/xml");
}
public async Task<object> Post(ProcessContentDirectoryControlRequest request) public async Task<object> Post(ProcessContentDirectoryControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, _contentDirectory).ConfigureAwait(false); var response = await PostAsync(request.RequestStream, _contentDirectory).ConfigureAwait(false);
@ -120,8 +169,7 @@ namespace MediaBrowser.Api.Dlna
private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service) private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var id = GetPathValue(2);
var id = pathInfo.GetArgumentValue<string>(2);
using (var reader = new StreamReader(requestStream)) using (var reader = new StreamReader(requestStream))
{ {
@ -172,6 +220,11 @@ namespace MediaBrowser.Api.Dlna
return ProcessEventRequest(_connectionManager); return ProcessEventRequest(_connectionManager);
} }
public object Any(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(_mediaReceiverRegistrar);
}
private object ProcessEventRequest(IEventManager eventManager) private object ProcessEventRequest(IEventManager eventManager)
{ {
var subscriptionId = GetHeader("SID"); var subscriptionId = GetHeader("SID");

View File

@ -102,8 +102,8 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetGameSystemSummaries request) public object Get(GetGameSystemSummaries request)
{ {
var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager) var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem)
.OfType<GameSystem>() .Cast<GameSystem>()
.ToList(); .ToList();
var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId.Value); var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId.Value);
@ -119,9 +119,8 @@ namespace MediaBrowser.Api
public object Get(GetPlayerIndex request) public object Get(GetPlayerIndex request)
{ {
var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager) var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game)
.OfType<Game>() .Cast<Game>();
.ToList();
var lookup = games var lookup = games
.ToLookup(i => i.PlayersSupported ?? -1) .ToLookup(i => i.PlayersSupported ?? -1)
@ -150,9 +149,11 @@ namespace MediaBrowser.Api
DisplayName = system.Name DisplayName = system.Name
}; };
var items = user == null ? system.RecursiveChildren : system.GetRecursiveChildren(user); var items = user == null ?
system.GetRecursiveChildren(i => i is Game) :
system.GetRecursiveChildren(user, i => i is Game);
var games = items.OfType<Game>().ToList(); var games = items.Cast<Game>().ToList();
summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder); summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
@ -172,7 +173,9 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetSimilarGames request) public object Get(GetSimilarGames request)
{ {
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, var dtoOptions = GetDtoOptions(request);
var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo, _itemRepo,
_libraryManager, _libraryManager,
_userDataRepository, _userDataRepository,

View File

@ -1,8 +1,4 @@
using MediaBrowser.Controller.Dto; 
using MediaBrowser.Model.Entities;
using System;
using System.Linq;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
public interface IHasDtoOptions : IHasItemFields public interface IHasDtoOptions : IHasItemFields
@ -13,27 +9,4 @@ namespace MediaBrowser.Api
string EnableImageTypes { get; set; } string EnableImageTypes { get; set; }
} }
public static class HasDtoOptionsExtensions
{
public static DtoOptions GetDtoOptions(this IHasDtoOptions request)
{
var options = new DtoOptions();
options.Fields = request.GetItemFields().ToList();
options.EnableImages = request.EnableImages ?? true;
if (request.ImageTypeLimit.HasValue)
{
options.ImageTypeLimit = request.ImageTypeLimit.Value;
}
if (!string.IsNullOrWhiteSpace(request.EnableImageTypes))
{
options.ImageTypes = (request.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
}
return options;
}
}
} }

View File

@ -10,7 +10,6 @@ using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web; using ServiceStack.Web;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -396,8 +395,7 @@ namespace MediaBrowser.Api.Images
public object Get(GetItemByNameImage request) public object Get(GetItemByNameImage request)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var type = GetPathValue(0);
var type = pathInfo.GetArgumentValue<string>(0);
var item = GetItemByName(request.Name, type, _libraryManager); var item = GetItemByName(request.Name, type, _libraryManager);
@ -406,8 +404,7 @@ namespace MediaBrowser.Api.Images
public object Head(GetItemByNameImage request) public object Head(GetItemByNameImage request)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var type = GetPathValue(0);
var type = pathInfo.GetArgumentValue<string>(0);
var item = GetItemByName(request.Name, type, _libraryManager); var item = GetItemByName(request.Name, type, _libraryManager);
@ -420,10 +417,9 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
public void Post(PostUserImage request) public void Post(PostUserImage request)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var id = new Guid(GetPathValue(1));
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true); request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true);
var item = _userManager.GetUserById(id); var item = _userManager.GetUserById(id);
@ -438,10 +434,9 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
public void Post(PostItemImage request) public void Post(PostItemImage request)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var id = new Guid(GetPathValue(1));
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true); request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true);
var item = _libraryManager.GetItemById(id); var item = _libraryManager.GetItemById(id);

View File

@ -205,7 +205,8 @@ namespace MediaBrowser.Api
Logger = Logger, Logger = Logger,
Request = Request, Request = Request,
ResultFactory = ResultFactory, ResultFactory = ResultFactory,
SessionContext = SessionContext SessionContext = SessionContext,
AuthorizationContext = AuthorizationContext
}; };
service.Post(new RefreshItem service.Post(new RefreshItem

View File

@ -51,7 +51,7 @@ namespace MediaBrowser.Api
var cancellationToken = CancellationToken.None; var cancellationToken = CancellationToken.None;
var albums = _libraryManager.RootFolder var albums = _libraryManager.RootFolder
.RecursiveChildren .GetRecursiveChildren()
.OfType<MusicAlbum>() .OfType<MusicAlbum>()
.Where(i => i.HasArtist(item.Name)) .Where(i => i.HasArtist(item.Name))
.ToList(); .ToList();

View File

@ -215,7 +215,7 @@ namespace MediaBrowser.Api
{ {
var folder = (Folder)item; var folder = (Folder)item;
foreach (var child in folder.RecursiveChildren.ToList()) foreach (var child in folder.GetRecursiveChildren())
{ {
child.IsLocked = newLockData; child.IsLocked = newLockData;
await child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); await child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);

View File

@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Library
if (!string.IsNullOrEmpty(shortcut)) if (!string.IsNullOrEmpty(shortcut))
{ {
File.Delete(shortcut); fileSystem.DeleteFile(shortcut);
} }
} }

View File

@ -5,10 +5,8 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -226,6 +224,18 @@ namespace MediaBrowser.Api.Library
public string TvdbId { get; set; } public string TvdbId { get; set; }
} }
[Route("/Items/{Id}/Download", "GET", Summary = "Downloads item media")]
[Authenticated(Roles = "download")]
public class GetDownload
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
/// <summary> /// <summary>
/// Class LibraryService /// Class LibraryService
/// </summary> /// </summary>
@ -272,7 +282,7 @@ namespace MediaBrowser.Api.Library
items = items.Where(i => i.IsHidden == val).ToList(); items = items.Where(i => i.IsHidden == val).ToList();
} }
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var result = new ItemsResult var result = new ItemsResult
{ {
@ -289,6 +299,28 @@ namespace MediaBrowser.Api.Library
Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)); Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
} }
public object Get(GetDownload request)
{
var item = _libraryManager.GetItemById(request.Id);
if (!item.CanDelete())
{
throw new ArgumentException("Item does not support downloading");
}
var headers = new Dictionary<string, string>();
// Quotes are valid in linux. They'll possibly cause issues here
var filename = Path.GetFileName(item.Path).Replace("\"", string.Empty);
headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename);
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
Path = item.Path,
ResponseHeaders = headers
});
}
public object Get(GetFile request) public object Get(GetFile request)
{ {
var item = _libraryManager.GetItemById(request.Id); var item = _libraryManager.GetItemById(request.Id);
@ -344,7 +376,7 @@ namespace MediaBrowser.Api.Library
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null; var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
BaseItem parent = item.Parent; BaseItem parent = item.Parent;
@ -392,52 +424,43 @@ namespace MediaBrowser.Api.Library
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetItemCounts request) public object Get(GetItemCounts request)
{ {
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager) var filteredItems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i.LocationType != LocationType.Virtual && FilterItem(i, request, request.UserId));
.Where(i => i.LocationType != LocationType.Virtual)
.ToList();
var filteredItems = request.UserId.HasValue ? FilterItems(items, request, request.UserId.Value).ToList() : items;
var albums = filteredItems.OfType<MusicAlbum>().ToList();
var episodes = filteredItems.OfType<Episode>().ToList();
var games = filteredItems.OfType<Game>().ToList();
var movies = filteredItems.OfType<Movie>().ToList();
var musicVideos = filteredItems.OfType<MusicVideo>().ToList();
var boxsets = filteredItems.OfType<BoxSet>().ToList();
var books = filteredItems.OfType<Book>().ToList();
var songs = filteredItems.OfType<Audio>().ToList();
var series = filteredItems.OfType<Series>().ToList();
var counts = new ItemCounts var counts = new ItemCounts
{ {
AlbumCount = albums.Count, AlbumCount = filteredItems.Count(i => i is MusicAlbum),
EpisodeCount = episodes.Count, EpisodeCount = filteredItems.Count(i => i is Episode),
GameCount = games.Count, GameCount = filteredItems.Count(i => i is Game),
GameSystemCount = filteredItems.OfType<GameSystem>().Count(), GameSystemCount = filteredItems.Count(i => i is GameSystem),
MovieCount = movies.Count, MovieCount = filteredItems.Count(i => i is Movie),
SeriesCount = series.Count, SeriesCount = filteredItems.Count(i => i is Series),
SongCount = songs.Count, SongCount = filteredItems.Count(i => i is Audio),
MusicVideoCount = musicVideos.Count, MusicVideoCount = filteredItems.Count(i => i is MusicVideo),
BoxSetCount = boxsets.Count, BoxSetCount = filteredItems.Count(i => i is BoxSet),
BookCount = books.Count, BookCount = filteredItems.Count(i => i is Book),
UniqueTypes = items.Select(i => i.GetClientTypeName()).Distinct().ToList() UniqueTypes = filteredItems.Select(i => i.GetClientTypeName()).Distinct().ToList()
}; };
return ToOptimizedSerializedResultUsingCache(counts); return ToOptimizedSerializedResultUsingCache(counts);
} }
private IEnumerable<T> FilterItems<T>(IEnumerable<T> items, GetItemCounts request, Guid userId) private bool FilterItem(BaseItem item, GetItemCounts request, Guid? userId)
where T : BaseItem {
if (userId.HasValue)
{ {
if (request.IsFavorite.HasValue) if (request.IsFavorite.HasValue)
{ {
var val = request.IsFavorite.Value; var val = request.IsFavorite.Value;
items = items.Where(i => _userDataManager.GetUserData(userId, i.GetUserDataKey()).IsFavorite == val); if (_userDataManager.GetUserData(userId.Value, item.GetUserDataKey()).IsFavorite != val)
{
return false;
}
}
} }
return items; return true;
} }
/// <summary> /// <summary>
@ -467,24 +490,10 @@ namespace MediaBrowser.Api.Library
var auth = _authContext.GetAuthorizationInfo(Request); var auth = _authContext.GetAuthorizationInfo(Request);
var user = _userManager.GetUserById(auth.UserId); var user = _userManager.GetUserById(auth.UserId);
if (item is Playlist || item is BoxSet) if (!item.CanDelete(user))
{
// For now this is allowed if user can see the playlist
}
else if (item is ILiveTvRecording)
{
if (!user.Policy.EnableLiveTvManagement)
{ {
throw new UnauthorizedAccessException(); throw new UnauthorizedAccessException();
} }
}
else
{
if (!user.Policy.EnableContentDeletion)
{
throw new UnauthorizedAccessException();
}
}
var task = _libraryManager.DeleteItem(item); var task = _libraryManager.DeleteItem(item);
@ -544,7 +553,7 @@ namespace MediaBrowser.Api.Library
ThemeSongsResult = themeSongs, ThemeSongsResult = themeSongs,
ThemeVideosResult = themeVideos, ThemeVideosResult = themeVideos,
SoundtrackSongsResult = GetSoundtrackSongs(request.Id, request.UserId, request.InheritFromParent) SoundtrackSongsResult = GetSoundtrackSongs(request, request.Id, request.UserId, request.InheritFromParent)
}); });
} }
@ -597,7 +606,7 @@ namespace MediaBrowser.Api.Library
} }
} }
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = themeSongIds.Select(_libraryManager.GetItemById) var dtos = themeSongIds.Select(_libraryManager.GetItemById)
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
@ -667,7 +676,7 @@ namespace MediaBrowser.Api.Library
} }
} }
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = themeVideoIds.Select(_libraryManager.GetItemById) var dtos = themeVideoIds.Select(_libraryManager.GetItemById)
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
@ -711,13 +720,24 @@ namespace MediaBrowser.Api.Library
public object Get(GetYearIndex request) public object Get(GetYearIndex request)
{ {
IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager); var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes)
? new string[] { }
: request.IncludeItemTypes.Split(',');
if (!string.IsNullOrEmpty(request.IncludeItemTypes)) Func<BaseItem, bool> filter = i =>
{ {
var vals = request.IncludeItemTypes.Split(','); if (includeTypes.Length > 0)
items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); {
if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
{
return false;
} }
}
return true;
};
IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, filter);
var lookup = items var lookup = items
.ToLookup(i => i.ProductionYear ?? -1) .ToLookup(i => i.ProductionYear ?? -1)
@ -732,23 +752,22 @@ namespace MediaBrowser.Api.Library
return ToOptimizedSerializedResultUsingCache(lookup); return ToOptimizedSerializedResultUsingCache(lookup);
} }
public ThemeMediaResult GetSoundtrackSongs(string id, Guid? userId, bool inheritFromParent) public ThemeMediaResult GetSoundtrackSongs(GetThemeMedia request, string id, Guid? userId, bool inheritFromParent)
{ {
var user = userId.HasValue ? _userManager.GetUserById(userId.Value) : null; var user = userId.HasValue ? _userManager.GetUserById(userId.Value) : null;
var item = string.IsNullOrEmpty(id) var item = string.IsNullOrEmpty(id)
? (userId.HasValue ? (userId.HasValue
? user.RootFolder ? user.RootFolder
: (Folder)_libraryManager.RootFolder) : _libraryManager.RootFolder)
: _libraryManager.GetItemById(id); : _libraryManager.GetItemById(id);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = GetSoundtrackSongIds(item, inheritFromParent) var dtos = GetSoundtrackSongIds(item, inheritFromParent)
.Select(_libraryManager.GetItemById) .Select(_libraryManager.GetItemById)
.OfType<MusicAlbum>() .OfType<MusicAlbum>()
.SelectMany(i => i.RecursiveChildren) .SelectMany(i => i.GetRecursiveChildren(a => a is Audio))
.OfType<Audio>()
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));

View File

@ -3,7 +3,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using ServiceStack; using ServiceStack;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -18,7 +17,6 @@ namespace MediaBrowser.Api.Library
/// Class GetDefaultVirtualFolders /// Class GetDefaultVirtualFolders
/// </summary> /// </summary>
[Route("/Library/VirtualFolders", "GET")] [Route("/Library/VirtualFolders", "GET")]
[Route("/Users/{UserId}/VirtualFolders", "GET")]
public class GetVirtualFolders : IReturn<List<VirtualFolderInfo>> public class GetVirtualFolders : IReturn<List<VirtualFolderInfo>>
{ {
/// <summary> /// <summary>
@ -143,11 +141,6 @@ namespace MediaBrowser.Api.Library
/// </summary> /// </summary>
private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationPaths _appPaths;
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary> /// <summary>
/// The _library manager /// The _library manager
/// </summary> /// </summary>
@ -156,27 +149,21 @@ namespace MediaBrowser.Api.Library
private readonly ILibraryMonitor _libraryMonitor; private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LibraryStructureService"/> class. /// Initializes a new instance of the <see cref="LibraryStructureService" /> class.
/// </summary> /// </summary>
/// <param name="appPaths">The app paths.</param> public LibraryStructureService(IServerApplicationPaths appPaths, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem)
/// <param name="userManager">The user manager.</param>
/// <param name="libraryManager">The library manager.</param>
public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger)
{ {
if (appPaths == null) if (appPaths == null)
{ {
throw new ArgumentNullException("appPaths"); throw new ArgumentNullException("appPaths");
} }
_userManager = userManager;
_appPaths = appPaths; _appPaths = appPaths;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_libraryMonitor = libraryMonitor; _libraryMonitor = libraryMonitor;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_logger = logger;
} }
/// <summary> /// <summary>
@ -186,21 +173,10 @@ namespace MediaBrowser.Api.Library
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetVirtualFolders request) public object Get(GetVirtualFolders request)
{ {
if (string.IsNullOrEmpty(request.UserId)) var result = _libraryManager.GetVirtualFolders().OrderBy(i => i.Name).ToList();
{
var result = _libraryManager.GetDefaultVirtualFolders().OrderBy(i => i.Name).ToList();
return ToOptimizedSerializedResultUsingCache(result); return ToOptimizedSerializedResultUsingCache(result);
} }
else
{
var user = _userManager.GetUserById(request.UserId);
var result = _libraryManager.GetVirtualFolders(user).OrderBy(i => i.Name).ToList();
return ToOptimizedSerializedResultUsingCache(result);
}
}
/// <summary> /// <summary>
/// Posts the specified request. /// Posts the specified request.
@ -348,7 +324,7 @@ namespace MediaBrowser.Api.Library
try try
{ {
Directory.Delete(path, true); _fileSystem.DeleteDirectory(path, true);
// Need to add a delay here or directory watchers may still pick up the changes // Need to add a delay here or directory watchers may still pick up the changes
var delayTask = Task.Delay(1000); var delayTask = Task.Delay(1000);

View File

@ -82,6 +82,10 @@
<Compile Include="Playback\Hls\MpegDashService.cs" /> <Compile Include="Playback\Hls\MpegDashService.cs" />
<Compile Include="Playback\MediaInfoService.cs" /> <Compile Include="Playback\MediaInfoService.cs" />
<Compile Include="PlaylistService.cs" /> <Compile Include="PlaylistService.cs" />
<Compile Include="Reports\ReportFieldType.cs" />
<Compile Include="Reports\ReportResult.cs" />
<Compile Include="Reports\ReportsService.cs" />
<Compile Include="Reports\ReportRequests.cs" />
<Compile Include="StartupWizardService.cs" /> <Compile Include="StartupWizardService.cs" />
<Compile Include="Subtitles\SubtitleService.cs" /> <Compile Include="Subtitles\SubtitleService.cs" />
<Compile Include="Movies\CollectionService.cs" /> <Compile Include="Movies\CollectionService.cs" />
@ -110,7 +114,6 @@
<Compile Include="NotificationsService.cs" /> <Compile Include="NotificationsService.cs" />
<Compile Include="PackageReviewService.cs" /> <Compile Include="PackageReviewService.cs" />
<Compile Include="PackageService.cs" /> <Compile Include="PackageService.cs" />
<Compile Include="Playback\BifService.cs" />
<Compile Include="Playback\Hls\BaseHlsService.cs" /> <Compile Include="Playback\Hls\BaseHlsService.cs" />
<Compile Include="Playback\Hls\DynamicHlsService.cs" /> <Compile Include="Playback\Hls\DynamicHlsService.cs" />
<Compile Include="Playback\Hls\HlsSegmentService.cs" /> <Compile Include="Playback\Hls\HlsSegmentService.cs" />
@ -131,6 +134,8 @@
<Compile Include="SearchService.cs" /> <Compile Include="SearchService.cs" />
<Compile Include="Session\SessionsService.cs" /> <Compile Include="Session\SessionsService.cs" />
<Compile Include="SimilarItemsHelper.cs" /> <Compile Include="SimilarItemsHelper.cs" />
<Compile Include="Sync\SyncJobWebSocketListener.cs" />
<Compile Include="Sync\SyncJobsWebSocketListener.cs" />
<Compile Include="Sync\SyncService.cs" /> <Compile Include="Sync\SyncService.cs" />
<Compile Include="System\ActivityLogService.cs" /> <Compile Include="System\ActivityLogService.cs" />
<Compile Include="System\ActivityLogWebSocketListener.cs" /> <Compile Include="System\ActivityLogWebSocketListener.cs" />

View File

@ -71,7 +71,7 @@ namespace MediaBrowser.Api.Movies
}).ConfigureAwait(false); }).ConfigureAwait(false);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dto = _dtoService.GetBaseItemDto(item, dtoOptions); var dto = _dtoService.GetBaseItemDto(item, dtoOptions);

View File

@ -121,8 +121,7 @@ namespace MediaBrowser.Api.Movies
{ {
var user = _userManager.GetUserById(request.UserId.Value); var user = _userManager.GetUserById(request.UserId.Value);
var movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId) IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
.Where(i => i is Movie);
movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies); movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
@ -157,7 +156,7 @@ namespace MediaBrowser.Api.Movies
.DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase) .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase)
.ToList(); .ToList();
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
dtoOptions.Fields = request.GetItemFields().ToList(); dtoOptions.Fields = request.GetItemFields().ToList();
@ -174,13 +173,11 @@ namespace MediaBrowser.Api.Movies
(request.UserId.HasValue ? user.RootFolder : (request.UserId.HasValue ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
var fields = request.GetItemFields().ToList(); Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
var inputItems = user == null var inputItems = user == null
? _libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id) ? _libraryManager.RootFolder.GetRecursiveChildren(filter)
: user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id); : user.RootFolder.GetRecursiveChildren(user, filter);
inputItems = inputItems.Where(includeInSearch);
var list = inputItems.ToList(); var list = inputItems.ToList();
@ -226,9 +223,11 @@ namespace MediaBrowser.Api.Movies
returnItems = returnItems.Take(request.Limit.Value); returnItems = returnItems.Take(request.Limit.Value);
} }
var dtoOptions = GetDtoOptions(request);
var result = new ItemsResult var result = new ItemsResult
{ {
Items = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(), Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
TotalRecordCount = items.Count TotalRecordCount = items.Count
}; };
@ -351,7 +350,7 @@ namespace MediaBrowser.Api.Movies
BaselineItemName = director, BaselineItemName = director,
CategoryId = director.GetMD5().ToString("N"), CategoryId = director.GetMD5().ToString("N"),
RecommendationType = type, RecommendationType = type,
Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() Items = _dtoService.GetBaseItemDtos(items, dtoOptions, user).ToArray()
}; };
} }
} }
@ -375,7 +374,7 @@ namespace MediaBrowser.Api.Movies
BaselineItemName = name, BaselineItemName = name,
CategoryId = name.GetMD5().ToString("N"), CategoryId = name.GetMD5().ToString("N"),
RecommendationType = type, RecommendationType = type,
Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() Items = _dtoService.GetBaseItemDtos(items, dtoOptions, user).ToArray()
}; };
} }
} }
@ -399,7 +398,7 @@ namespace MediaBrowser.Api.Movies
BaselineItemName = item.Name, BaselineItemName = item.Name,
CategoryId = item.Id.ToString("N"), CategoryId = item.Id.ToString("N"),
RecommendationType = type, RecommendationType = type,
Items = similar.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() Items = _dtoService.GetBaseItemDtos(similar, dtoOptions, user).ToArray()
}; };
} }
} }

View File

@ -84,7 +84,9 @@ namespace MediaBrowser.Api.Movies
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetSimilarTrailers request) public object Get(GetSimilarTrailers request)
{ {
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, var dtoOptions = GetDtoOptions(request);
var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo, _itemRepo,
_libraryManager, _libraryManager,
_userDataRepository, _userDataRepository,
@ -119,9 +121,9 @@ namespace MediaBrowser.Api.Movies
var pagedItems = ApplyPaging(request, itemsArray); var pagedItems = ApplyPaging(request, itemsArray);
var fields = request.GetItemFields().ToList(); var dtoOptions = GetDtoOptions(request);
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(); var returnItems = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ToArray();
return new ItemsResult return new ItemsResult
{ {

View File

@ -50,7 +50,9 @@ namespace MediaBrowser.Api.Music
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetSimilarAlbums request) public object Get(GetSimilarAlbums request)
{ {
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, var dtoOptions = GetDtoOptions(request);
var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo, _itemRepo,
_libraryManager, _libraryManager,
_userDataRepository, _userDataRepository,
@ -75,14 +77,14 @@ namespace MediaBrowser.Api.Music
var album1 = (MusicAlbum)item1; var album1 = (MusicAlbum)item1;
var album2 = (MusicAlbum)item2; var album2 = (MusicAlbum)item2;
var artists1 = album1.GetRecursiveChildren() var artists1 = album1.GetRecursiveChildren(i => i is IHasArtist)
.OfType<Audio>() .Cast<IHasArtist>()
.SelectMany(i => i.AllArtists) .SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToList(); .ToList();
var artists2 = album2.GetRecursiveChildren() var artists2 = album2.GetRecursiveChildren(i => i is IHasArtist)
.OfType<Audio>() .Cast<IHasArtist>()
.SelectMany(i => i.AllArtists) .SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);

View File

@ -73,44 +73,44 @@ namespace MediaBrowser.Api.Music
public object Get(GetInstantMixFromArtistId request) public object Get(GetInstantMixFromArtistId request)
{ {
var item = (MusicArtist)_libraryManager.GetItemById(request.Id); var item = _libraryManager.GetItemById(request.Id);
var user = _userManager.GetUserById(request.UserId.Value); var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromArtist(item.Name, user); var items = _musicManager.GetInstantMixFromItem(item, user);
return GetResult(items, user, request); return GetResult(items, user, request);
} }
public object Get(GetInstantMixFromMusicGenreId request) public object Get(GetInstantMixFromMusicGenreId request)
{ {
var item = (MusicGenre)_libraryManager.GetItemById(request.Id); var item = _libraryManager.GetItemById(request.Id);
var user = _userManager.GetUserById(request.UserId.Value); var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromGenres(new[] { item.Name }, user); var items = _musicManager.GetInstantMixFromItem(item, user);
return GetResult(items, user, request); return GetResult(items, user, request);
} }
public object Get(GetInstantMixFromSong request) public object Get(GetInstantMixFromSong request)
{ {
var item = (Audio)_libraryManager.GetItemById(request.Id); var item = _libraryManager.GetItemById(request.Id);
var user = _userManager.GetUserById(request.UserId.Value); var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromSong(item, user); var items = _musicManager.GetInstantMixFromItem(item, user);
return GetResult(items, user, request); return GetResult(items, user, request);
} }
public object Get(GetInstantMixFromAlbum request) public object Get(GetInstantMixFromAlbum request)
{ {
var album = (MusicAlbum)_libraryManager.GetItemById(request.Id); var album = _libraryManager.GetItemById(request.Id);
var user = _userManager.GetUserById(request.UserId.Value); var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromAlbum(album, user); var items = _musicManager.GetInstantMixFromItem(album, user);
return GetResult(items, user, request); return GetResult(items, user, request);
} }
@ -121,7 +121,7 @@ namespace MediaBrowser.Api.Music
var user = _userManager.GetUserById(request.UserId.Value); var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromPlaylist(playlist, user); var items = _musicManager.GetInstantMixFromItem(playlist, user);
return GetResult(items, user, request); return GetResult(items, user, request);
} }
@ -146,8 +146,6 @@ namespace MediaBrowser.Api.Music
private object GetResult(IEnumerable<Audio> items, User user, BaseGetSimilarItems request) private object GetResult(IEnumerable<Audio> items, User user, BaseGetSimilarItems request)
{ {
var fields = request.GetItemFields().ToList();
var list = items.ToList(); var list = items.ToList();
var result = new ItemsResult var result = new ItemsResult
@ -155,10 +153,9 @@ namespace MediaBrowser.Api.Music
TotalRecordCount = list.Count TotalRecordCount = list.Count
}; };
var dtos = list.Take(request.Limit ?? list.Count) var dtoOptions = GetDtoOptions(request);
.Select(i => _dtoService.GetBaseItemDto(i, fields, user));
result.Items = dtos.ToArray(); result.Items = _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user).ToArray();
return ToOptimizedResult(result); return ToOptimizedResult(result);
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Devices;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
@ -66,14 +67,16 @@ namespace MediaBrowser.Api.Playback
protected ILiveTvManager LiveTvManager { get; private set; } protected ILiveTvManager LiveTvManager { get; private set; }
protected IDlnaManager DlnaManager { get; private set; } protected IDlnaManager DlnaManager { get; private set; }
protected IDeviceManager DeviceManager { get; private set; }
protected IChannelManager ChannelManager { get; private set; } protected IChannelManager ChannelManager { get; private set; }
protected ISubtitleEncoder SubtitleEncoder { get; private set; } protected ISubtitleEncoder SubtitleEncoder { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class. /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary> /// </summary>
protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder) protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager)
{ {
DeviceManager = deviceManager;
SubtitleEncoder = subtitleEncoder; SubtitleEncoder = subtitleEncoder;
ChannelManager = channelManager; ChannelManager = channelManager;
DlnaManager = dlnaManager; DlnaManager = dlnaManager;
@ -119,7 +122,7 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private string GetOutputFilePath(StreamState state) private string GetOutputFilePath(StreamState state)
{ {
var folder = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower()); var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
var outputFileExtension = GetOutputFileExtension(state); var outputFileExtension = GetOutputFileExtension(state);
@ -320,13 +323,13 @@ namespace MediaBrowser.Api.Playback
switch (qualitySetting) switch (qualitySetting)
{ {
case EncodingQuality.HighSpeed: case EncodingQuality.HighSpeed:
param += " -crf 23"; param += " -subq 0 -crf 23";
break; break;
case EncodingQuality.HighQuality: case EncodingQuality.HighQuality:
param += " -crf 20"; param += " -subq 3 -crf 20";
break; break;
case EncodingQuality.MaxQuality: case EncodingQuality.MaxQuality:
param += " -crf 18"; param += " -subq 6 -crf 18";
break; break;
} }
} }
@ -349,6 +352,41 @@ namespace MediaBrowser.Api.Playback
} }
} }
// h264 (h264_qsv)
else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
switch (qualitySetting)
{
case EncodingQuality.HighSpeed:
param = "-preset 7";
break;
case EncodingQuality.HighQuality:
param = "-preset 4";
break;
case EncodingQuality.MaxQuality:
param = "-preset 1";
break;
}
}
// h264 (libnvenc)
else if (string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
{
switch (qualitySetting)
{
case EncodingQuality.HighSpeed:
param = "-preset high-performance";
break;
case EncodingQuality.HighQuality:
param = "";
break;
case EncodingQuality.MaxQuality:
param = "-preset high-quality";
break;
}
}
// webm // webm
else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{ {
@ -425,11 +463,51 @@ namespace MediaBrowser.Api.Playback
} }
if (!string.IsNullOrEmpty(state.VideoRequest.Level)) if (!string.IsNullOrEmpty(state.VideoRequest.Level))
{
// h264_qsv and libnvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
if (String.Equals(H264Encoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || String.Equals(H264Encoder, "libnvenc", StringComparison.OrdinalIgnoreCase))
{
switch (state.VideoRequest.Level)
{
case "30":
param += " -level 3";
break;
case "31":
param += " -level 3.1";
break;
case "32":
param += " -level 3.2";
break;
case "40":
param += " -level 4";
break;
case "41":
param += " -level 4.1";
break;
case "42":
param += " -level 4.2";
break;
case "50":
param += " -level 5";
break;
case "51":
param += " -level 5.1";
break;
case "52":
param += " -level 5.2";
break;
default:
param += " -level " + state.VideoRequest.Level;
break;
}
}
else
{ {
param += " -level " + state.VideoRequest.Level; param += " -level " + state.VideoRequest.Level;
} }
}
return param; return "-pix_fmt yuv420p " + param;
} }
protected string GetAudioFilterParam(StreamState state, bool isHls) protected string GetAudioFilterParam(StreamState state, bool isHls)
@ -567,6 +645,11 @@ namespace MediaBrowser.Api.Playback
} }
} }
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
filters[filters.Count - 1] += ":flags=fast_bilinear";
}
var output = string.Empty; var output = string.Empty;
if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream) if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
@ -606,7 +689,7 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.SubtitleStream.Language)) if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
{ {
var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language); var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath);
if (!string.IsNullOrEmpty(charenc)) if (!string.IsNullOrEmpty(charenc))
{ {
@ -834,7 +917,7 @@ namespace MediaBrowser.Api.Playback
{ {
if (SupportsThrottleWithStream) if (SupportsThrottleWithStream)
{ {
var url = "http://localhost:" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(UsCulture) + "/mediabrowser/videos/" + state.Request.Id + "/stream?static=true&Throttle=true&mediaSourceId=" + state.Request.MediaSourceId; var url = "http://localhost:" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(UsCulture) + "/videos/" + state.Request.Id + "/stream?static=true&Throttle=true&mediaSourceId=" + state.Request.MediaSourceId;
url += "&transcodingJobId=" + transcodingJobId; url += "&transcodingJobId=" + transcodingJobId;
@ -1183,6 +1266,22 @@ namespace MediaBrowser.Api.Playback
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
} }
// h264_qsv
if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
{
if (hasFixedResolution)
{
if (isHls)
{
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
}
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
}
return string.Format(" -b:v {0} -maxrate ({0}*1.2) -bufsize ({0}*2)", bitrate.Value.ToString(UsCulture));
}
// H264 // H264
if (hasFixedResolution) if (hasFixedResolution)
{ {
@ -1991,9 +2090,26 @@ namespace MediaBrowser.Api.Playback
headers[key] = Request.Headers[key]; headers[key] = Request.Headers[key];
} }
state.DeviceProfile = string.IsNullOrWhiteSpace(state.Request.DeviceProfileId) ? if (!string.IsNullOrWhiteSpace(state.Request.DeviceProfileId))
DlnaManager.GetProfile(headers) : {
DlnaManager.GetProfile(state.Request.DeviceProfileId); state.DeviceProfile = DlnaManager.GetProfile(state.Request.DeviceProfileId);
}
else
{
if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
{
var caps = DeviceManager.GetCapabilities(state.Request.DeviceId);
if (caps != null)
{
state.DeviceProfile = caps.DeviceProfile;
}
else
{
state.DeviceProfile = DlnaManager.GetProfile(headers);
}
}
}
var profile = state.DeviceProfile; var profile = state.DeviceProfile;

View File

@ -1,186 +0,0 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using ServiceStack;
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback
{
[Route("/Videos/{Id}/index.bif", "GET")]
public class GetBifFile
{
[ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string MediaSourceId { get; set; }
[ApiMember(Name = "MaxWidth", Description = "Optional. The maximum horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? MaxWidth { get; set; }
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
public class BifService : BaseApiService
{
private readonly IServerApplicationPaths _appPaths;
private readonly ILibraryManager _libraryManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
public BifService(IServerApplicationPaths appPaths, ILibraryManager libraryManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
{
_appPaths = appPaths;
_libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
_fileSystem = fileSystem;
}
public object Get(GetBifFile request)
{
return ToStaticFileResult(GetBifFile(request).Result);
}
private async Task<string> GetBifFile(GetBifFile request)
{
var widthVal = request.MaxWidth.HasValue ? request.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty;
var item = _libraryManager.GetItemById(request.Id);
var mediaSources = ((IHasMediaSources)item).GetMediaSources(false).ToList();
var mediaSource = mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)) ?? mediaSources.First();
var path = Path.Combine(_appPaths.ImageCachePath, "bif", request.Id, request.MediaSourceId, widthVal, "index.bif");
if (File.Exists(path))
{
return path;
}
var protocol = mediaSource.Protocol;
var inputPath = MediaEncoderHelpers.GetInputArgument(mediaSource.Path, protocol, null, mediaSource.PlayableStreamFileNames);
var semaphore = GetLock(path);
await semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (File.Exists(path))
{
return path;
}
await _mediaEncoder.ExtractVideoImagesOnInterval(inputPath, protocol, mediaSource.Video3DFormat,
TimeSpan.FromSeconds(10), Path.GetDirectoryName(path), "img_", request.MaxWidth, CancellationToken.None)
.ConfigureAwait(false);
var images = new DirectoryInfo(Path.GetDirectoryName(path))
.EnumerateFiles()
.Where(img => string.Equals(img.Extension, ".jpg", StringComparison.Ordinal))
.OrderBy(i => i.FullName)
.ToList();
using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
var magicNumber = new byte[] { 0x89, 0x42, 0x49, 0x46, 0x0d, 0x0a, 0x1a, 0x0a };
await fs.WriteAsync(magicNumber, 0, magicNumber.Length);
// version
var bytes = GetBytes(0);
await fs.WriteAsync(bytes, 0, bytes.Length);
// image count
bytes = GetBytes(images.Count);
await fs.WriteAsync(bytes, 0, bytes.Length);
// interval in ms
bytes = GetBytes(10000);
await fs.WriteAsync(bytes, 0, bytes.Length);
// reserved
for (var i = 20; i <= 63; i++)
{
bytes = new byte[] { 0x00 };
await fs.WriteAsync(bytes, 0, bytes.Length);
}
// write the bif index
var index = 0;
long imageOffset = 64 + (8 * images.Count) + 8;
foreach (var img in images)
{
bytes = GetBytes(index);
await fs.WriteAsync(bytes, 0, bytes.Length);
bytes = GetBytes(imageOffset);
await fs.WriteAsync(bytes, 0, bytes.Length);
imageOffset += img.Length;
index++;
}
bytes = new byte[] { 0xff, 0xff, 0xff, 0xff };
await fs.WriteAsync(bytes, 0, bytes.Length);
bytes = GetBytes(imageOffset);
await fs.WriteAsync(bytes, 0, bytes.Length);
// write the images
foreach (var img in images)
{
using (var imgStream = _fileSystem.GetFileStream(img.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
{
await imgStream.CopyToAsync(fs).ConfigureAwait(false);
}
}
}
return path;
}
finally
{
semaphore.Release();
}
}
private byte[] GetBytes(int value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (!BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return bytes;
}
private byte[] GetBytes(long value)
{
var intVal = Convert.ToInt32(value);
return GetBytes(intVal);
//byte[] bytes = BitConverter.GetBytes(value);
//if (BitConverter.IsLittleEndian)
// Array.Reverse(bytes);
//return bytes;
}
private static readonly ConcurrentDictionary<string, SemaphoreSlim> SemaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
/// <summary>
/// Gets the lock.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>System.Object.</returns>
private static SemaphoreSlim GetLock(string filename)
{
return SemaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
}
}
}

View File

@ -1,20 +1,20 @@
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Api.Playback.Hls namespace MediaBrowser.Api.Playback.Hls
{ {
@ -23,8 +23,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary> /// </summary>
public abstract class BaseHlsService : BaseStreamingService public abstract class BaseHlsService : BaseStreamingService
{ {
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder) protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
{ {
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Devices;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
@ -62,38 +63,31 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService public class DynamicHlsService : BaseHlsService
{ {
protected INetworkManager NetworkManager { get; private set; } public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager)
public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, INetworkManager networkManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
{ {
NetworkManager = networkManager; NetworkManager = networkManager;
} }
public object Get(GetMasterHlsVideoStream request) protected INetworkManager NetworkManager { get; private set; }
{
var result = GetAsync(request, "GET").Result;
return result; public Task<object> Get(GetMasterHlsVideoStream request)
{
return GetAsync(request, "GET");
} }
public object Head(GetMasterHlsVideoStream request) public Task<object> Head(GetMasterHlsVideoStream request)
{ {
var result = GetAsync(request, "HEAD").Result; return GetAsync(request, "HEAD");
return result;
} }
public object Get(GetMainHlsVideoStream request) public Task<object> Get(GetMainHlsVideoStream request)
{ {
var result = GetPlaylistAsync(request, "main").Result; return GetPlaylistAsync(request, "main");
return result;
} }
public object Get(GetDynamicHlsVideoSegment request) public Task<object> Get(GetDynamicHlsVideoSegment request)
{ {
return GetDynamicSegment(request, request.SegmentId).Result; return GetDynamicSegment(request, request.SegmentId);
} }
private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId) private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId)
@ -213,7 +207,7 @@ namespace MediaBrowser.Api.Playback.Hls
try try
{ {
File.Delete(file.FullName); FileSystem.DeleteFile(file.FullName);
} }
catch (IOException ex) catch (IOException ex)
{ {

View File

@ -1,10 +1,6 @@
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Model.Dlna;
using ServiceStack; using ServiceStack;
using System;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback.Hls namespace MediaBrowser.Api.Playback.Hls
{ {
@ -66,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_appPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file); file = Path.Combine(_appPaths.TranscodingTempPath, file);
return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
} }
@ -85,7 +81,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var file = request.SegmentId + Path.GetExtension(Request.PathInfo); var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_appPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file); file = Path.Combine(_appPaths.TranscodingTempPath, file);
return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
} }

View File

@ -2,6 +2,7 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
@ -50,14 +51,13 @@ namespace MediaBrowser.Api.Playback.Hls
public class MpegDashService : BaseHlsService public class MpegDashService : BaseHlsService
{ {
protected INetworkManager NetworkManager { get; private set; } public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager)
public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, INetworkManager networkManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
{ {
NetworkManager = networkManager; NetworkManager = networkManager;
} }
protected INetworkManager NetworkManager { get; private set; }
public object Get(GetMasterManifest request) public object Get(GetMasterManifest request)
{ {
var result = GetAsync(request, "GET").Result; var result = GetAsync(request, "GET").Result;
@ -489,7 +489,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
try try
{ {
File.Delete(file.FullName); FileSystem.DeleteFile(file.FullName);
} }
catch (IOException ex) catch (IOException ex)
{ {
@ -625,7 +625,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
{ {
// test url http://192.168.1.2:8096/mediabrowser/videos/233e8905d559a8f230db9bffd2ac9d6d/master.mpd?mediasourceid=233e8905d559a8f230db9bffd2ac9d6d&videocodec=h264&audiocodec=aac&maxwidth=1280&videobitrate=500000&audiobitrate=128000&profile=baseline&level=3 // test url http://192.168.1.2:8096/videos/233e8905d559a8f230db9bffd2ac9d6d/master.mpd?mediasourceid=233e8905d559a8f230db9bffd2ac9d6d&videocodec=h264&audiocodec=aac&maxwidth=1280&videobitrate=500000&audiobitrate=128000&profile=baseline&level=3
// Good info on i-frames http://blog.streamroot.io/encode-multi-bitrate-videos-mpeg-dash-mse-based-media-players/ // Good info on i-frames http://blog.streamroot.io/encode-multi-bitrate-videos-mpeg-dash-mse-based-media-players/
var threads = GetNumberOfThreads(state, false); var threads = GetNumberOfThreads(state, false);

View File

@ -1,11 +1,11 @@
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using ServiceStack; using ServiceStack;
using System; using System;
@ -57,8 +57,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary> /// </summary>
public class VideoHlsService : BaseHlsService public class VideoHlsService : BaseHlsService
{ {
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder) public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
{ {
} }
@ -71,7 +70,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var file = request.SegmentId + Path.GetExtension(Request.PathInfo); var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file); file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
return ResultFactory.GetStaticFileResult(Request, file); return ResultFactory.GetStaticFileResult(Request, file);
} }

View File

@ -2,6 +2,7 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -31,7 +32,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary> /// </summary>
public class AudioService : BaseProgressiveStreamingService public class AudioService : BaseProgressiveStreamingService
{ {
public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, imageProcessor, httpClient) public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager, imageProcessor, httpClient)
{ {
} }

View File

@ -1,8 +1,8 @@
using System.Linq; using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -15,6 +15,7 @@ using ServiceStack.Web;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -28,8 +29,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected readonly IImageProcessor ImageProcessor; protected readonly IImageProcessor ImageProcessor;
protected readonly IHttpClient HttpClient; protected readonly IHttpClient HttpClient;
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
{ {
ImageProcessor = imageProcessor; ImageProcessor = imageProcessor;
HttpClient = httpClient; HttpClient = httpClient;

View File

@ -2,6 +2,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -62,7 +63,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary> /// </summary>
public class VideoService : BaseProgressiveStreamingService public class VideoService : BaseProgressiveStreamingService
{ {
public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, imageProcessor, httpClient) public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager, imageProcessor, httpClient)
{ {
} }
@ -96,6 +97,7 @@ namespace MediaBrowser.Api.Playback.Progressive
if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase)) if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase))
{ {
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
format = " -f mp4 -movflags frag_keyframe+empty_moov"; format = " -f mp4 -movflags frag_keyframe+empty_moov";
} }

View File

@ -39,8 +39,11 @@ namespace MediaBrowser.Api.Playback
/// </summary> /// </summary>
/// <param name="responseStream">The response stream.</param> /// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream) public void WriteTo(Stream responseStream)
{
using (_response)
{ {
_response.Content.CopyTo(responseStream, 819200); _response.Content.CopyTo(responseStream, 819200);
} }
} }
}
} }

View File

@ -152,8 +152,9 @@ namespace MediaBrowser.Api
items = items.Take(request.Limit.Value).ToArray(); items = items.Take(request.Limit.Value).ToArray();
} }
var dtos = items var dtoOptions = GetDtoOptions(request);
.Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user))
var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user)
.ToArray(); .ToArray();
var index = 0; var index = 0;

View File

@ -1,5 +1,4 @@
using System.Threading; using MediaBrowser.Common;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Security; using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
@ -8,12 +7,12 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web; using ServiceStack.Web;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Api namespace MediaBrowser.Api
@ -236,8 +235,7 @@ namespace MediaBrowser.Api
{ {
// We need to parse this manually because we told service stack not to with IRequiresRequestStream // We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var pathInfo = PathInfo.Parse(Request.PathInfo); var id = new Guid(GetPathValue(1));
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var plugin = _appHost.Plugins.First(p => p.Id == id); var plugin = _appHost.Plugins.First(p => p.Id == id);

View File

@ -0,0 +1,9 @@

namespace MediaBrowser.Api.Reports
{
public enum ReportFieldType
{
String,
Boolean
}
}

View File

@ -0,0 +1,33 @@
using ServiceStack;
namespace MediaBrowser.Api.Reports
{
public class BaseReportRequest : IReturn<ReportResult>
{
/// <summary>
/// Specify this to localize the search to a specific item or folder. Omit to use the root.
/// </summary>
/// <value>The parent id.</value>
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ParentId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
/// <value>The start index.</value>
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? StartIndex { get; set; }
/// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
}
[Route("/Reports/Items", "GET", Summary = "Gets reports based on library items")]
public class GetItemReport : BaseReportRequest
{
}
}

View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace MediaBrowser.Api.Reports
{
public class ReportResult
{
public List<List<string>> Rows { get; set; }
public List<ReportFieldType> Columns { get; set; }
public ReportResult()
{
Rows = new List<List<string>>();
Columns = new List<ReportFieldType>();
}
}
}

View File

@ -0,0 +1,64 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Querying;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Reports
{
public class ReportsService : BaseApiService
{
private readonly ILibraryManager _libraryManager;
public ReportsService(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public async Task<object> Get(GetItemReport request)
{
var queryResult = await GetQueryResult(request).ConfigureAwait(false);
var reportResult = GetReportResult(queryResult);
return ToOptimizedResult(reportResult);
}
private ReportResult GetReportResult(QueryResult<BaseItem> queryResult)
{
var reportResult = new ReportResult();
// Fill rows and columns
return reportResult;
}
private Task<QueryResult<BaseItem>> GetQueryResult(BaseReportRequest request)
{
// Placeholder in case needed later
User user = null;
var parentItem = string.IsNullOrEmpty(request.ParentId) ?
(user == null ? _libraryManager.RootFolder : user.RootFolder) :
_libraryManager.GetItemById(request.ParentId);
return ((Folder)parentItem).GetItems(GetItemsQuery(request, user));
}
private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user)
{
var query = new InternalItemsQuery
{
User = user,
CollapseBoxSetItems = false
};
// Set query values based on request
// Example
//query.IncludeItemTypes = new[] {"Movie"};
return query;
}
}
}

View File

@ -32,6 +32,9 @@ namespace MediaBrowser.Api.ScheduledTasks
{ {
[ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsHidden { get; set; } public bool? IsHidden { get; set; }
[ApiMember(Name = "IsEnabled", Description = "Optional filter tasks that are enabled, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsEnabled { get; set; }
} }
/// <summary> /// <summary>
@ -132,6 +135,25 @@ namespace MediaBrowser.Api.ScheduledTasks
}); });
} }
if (request.IsEnabled.HasValue)
{
var val = request.IsEnabled.Value;
result = result.Where(i =>
{
var isEnabled = true;
var configurableTask = i.ScheduledTask as IConfigurableScheduledTask;
if (configurableTask != null)
{
isEnabled = configurableTask.IsEnabled;
}
return isEnabled == val;
});
}
var infos = result var infos = result
.Select(ScheduledTaskHelpers.GetTaskInfo) .Select(ScheduledTaskHelpers.GetTaskInfo)
.ToList(); .ToList();
@ -202,8 +224,7 @@ namespace MediaBrowser.Api.ScheduledTasks
{ {
// We need to parse this manually because we told service stack not to with IRequiresRequestStream // We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var pathInfo = PathInfo.Parse(Request.PathInfo); var id = GetPathValue(1);
var id = pathInfo.GetArgumentValue<string>(1);
var task = TaskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id)); var task = TaskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id));

View File

@ -194,29 +194,24 @@ namespace MediaBrowser.Api
{ {
result.Series = season.Series.Name; result.Series = season.Series.Name;
result.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode); result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count;
} }
var series = item as Series; var series = item as Series;
if (series != null) if (series != null)
{ {
result.EpisodeCount = series.GetRecursiveChildren().Count(i => i is Episode); result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count;
} }
var album = item as MusicAlbum; var album = item as MusicAlbum;
if (album != null) if (album != null)
{ {
var songs = album.GetRecursiveChildren().OfType<Audio>().ToList(); result.SongCount = album.Tracks.Count();
result.SongCount = songs.Count; result.Artists = album.Artists.ToArray();
result.AlbumArtist = album.AlbumArtists.FirstOrDefault();
result.Artists = songs.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
result.AlbumArtist = songs.SelectMany(i => i.AlbumArtists).FirstOrDefault(i => !string.IsNullOrEmpty(i));
} }
var song = item as Audio; var song = item as Audio;

View File

@ -1,5 +1,4 @@
using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;

View File

@ -243,8 +243,13 @@ namespace MediaBrowser.Api.Session
[ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsSync { get; set; } public bool SupportsSync { get; set; }
[ApiMember(Name = "SupportsUniqueIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsUniqueIdentifier { get; set; } public bool SupportsPersistentIdentifier { get; set; }
public PostCapabilities()
{
SupportsPersistentIdentifier = true;
}
} }
[Route("/Sessions/Capabilities/Full", "POST", Summary = "Updates capabilities for a device")] [Route("/Sessions/Capabilities/Full", "POST", Summary = "Updates capabilities for a device")]
@ -556,7 +561,7 @@ namespace MediaBrowser.Api.Session
SupportsSync = request.SupportsSync, SupportsSync = request.SupportsSync,
SupportsUniqueIdentifier = request.SupportsUniqueIdentifier SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
}); });
} }

View File

@ -57,6 +57,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Gets the similar items. /// Gets the similar items.
/// </summary> /// </summary>
/// <param name="dtoOptions">The dto options.</param>
/// <param name="userManager">The user manager.</param> /// <param name="userManager">The user manager.</param>
/// <param name="itemRepository">The item repository.</param> /// <param name="itemRepository">The item repository.</param>
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
@ -67,7 +68,7 @@ namespace MediaBrowser.Api
/// <param name="includeInSearch">The include in search.</param> /// <param name="includeInSearch">The include in search.</param>
/// <param name="getSimilarityScore">The get similarity score.</param> /// <param name="getSimilarityScore">The get similarity score.</param>
/// <returns>ItemsResult.</returns> /// <returns>ItemsResult.</returns>
internal static ItemsResult GetSimilarItemsResult(IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore) internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
{ {
var user = request.UserId.HasValue ? userManager.GetUserById(request.UserId.Value) : null; var user = request.UserId.HasValue ? userManager.GetUserById(request.UserId.Value) : null;
@ -75,13 +76,13 @@ namespace MediaBrowser.Api
(request.UserId.HasValue ? user.RootFolder : (request.UserId.HasValue ? user.RootFolder :
libraryManager.RootFolder) : libraryManager.GetItemById(request.Id); libraryManager.RootFolder) : libraryManager.GetItemById(request.Id);
var fields = request.GetItemFields().ToList(); Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
var inputItems = user == null var inputItems = user == null
? libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id) ? libraryManager.RootFolder.GetRecursiveChildren(filter)
: user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id); : user.RootFolder.GetRecursiveChildren(user, filter);
var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore) var items = GetSimilaritems(item, inputItems, getSimilarityScore)
.ToList(); .ToList();
IEnumerable<BaseItem> returnItems = items; IEnumerable<BaseItem> returnItems = items;
@ -93,7 +94,7 @@ namespace MediaBrowser.Api
var result = new ItemsResult var result = new ItemsResult
{ {
Items = returnItems.Select(i => dtoService.GetBaseItemDto(i, fields, user)).ToArray(), Items = dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
TotalRecordCount = items.Count TotalRecordCount = items.Count
}; };

View File

@ -62,6 +62,9 @@ namespace MediaBrowser.Api
{ {
_config.Configuration.IsStartupWizardCompleted = true; _config.Configuration.IsStartupWizardCompleted = true;
_config.Configuration.EnableLocalizedGuids = true; _config.Configuration.EnableLocalizedGuids = true;
_config.Configuration.MergeMetadataAndImagesByName = true;
_config.Configuration.EnableStandaloneMetadata = true;
_config.Configuration.EnableLibraryMetadataSubFolder = true;
_config.SaveConfiguration(); _config.SaveConfiguration();
} }

View File

@ -0,0 +1,120 @@
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Sync;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Sync
{
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
class SyncJobWebSocketListener : BasePeriodicWebSocketListener<CompleteSyncJobInfo, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
protected override string Name
{
get { return "SyncJob"; }
}
private readonly ISyncManager _syncManager;
private string _jobId;
public SyncJobWebSocketListener(ILogger logger, ISyncManager syncManager)
: base(logger)
{
_syncManager = syncManager;
_syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
_syncManager.SyncJobUpdated += _syncManager_SyncJobUpdated;
_syncManager.SyncJobItemCreated += _syncManager_SyncJobItemCreated;
_syncManager.SyncJobItemUpdated += _syncManager_SyncJobItemUpdated;
}
void _syncManager_SyncJobItemUpdated(object sender, GenericEventArgs<SyncJobItem> e)
{
if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
{
SendData(false);
}
}
void _syncManager_SyncJobItemCreated(object sender, GenericEventArgs<SyncJobItem> e)
{
if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
{
SendData(true);
}
}
protected override void ParseMessageParams(string[] values)
{
base.ParseMessageParams(values);
if (values.Length > 0)
{
_jobId = values[0];
}
}
void _syncManager_SyncJobUpdated(object sender, GenericEventArgs<SyncJob> e)
{
if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
{
SendData(false);
}
}
void _syncManager_SyncJobCancelled(object sender, GenericEventArgs<SyncJob> e)
{
if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
{
SendData(true);
}
}
/// <summary>
/// Gets the data to send.
/// </summary>
/// <param name="state">The state.</param>
/// <returns>Task{SystemInfo}.</returns>
protected override Task<CompleteSyncJobInfo> GetDataToSend(WebSocketListenerState state)
{
var job = _syncManager.GetJob(_jobId);
var items = _syncManager.GetJobItems(new SyncJobItemQuery
{
AddMetadata = true,
JobId = _jobId
});
var info = new CompleteSyncJobInfo
{
Job = job,
JobItems = items.Items.ToList()
};
return Task.FromResult(info);
}
protected override bool SendOnTimer
{
get
{
return false;
}
}
protected override void Dispose(bool dispose)
{
_syncManager.SyncJobCancelled -= _syncManager_SyncJobCancelled;
_syncManager.SyncJobUpdated -= _syncManager_SyncJobUpdated;
base.Dispose(dispose);
}
}
}

View File

@ -0,0 +1,101 @@
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Sync;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Sync
{
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
class SyncJobsWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SyncJob>, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
protected override string Name
{
get { return "SyncJobs"; }
}
private readonly ISyncManager _syncManager;
private string _userId;
private string _targetId;
public SyncJobsWebSocketListener(ILogger logger, ISyncManager syncManager)
: base(logger)
{
_syncManager = syncManager;
_syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
_syncManager.SyncJobCreated += _syncManager_SyncJobCreated;
_syncManager.SyncJobUpdated += _syncManager_SyncJobUpdated;
}
protected override void ParseMessageParams(string[] values)
{
base.ParseMessageParams(values);
if (values.Length > 0)
{
_userId = values[0];
}
if (values.Length > 1)
{
_targetId = values[1];
}
}
void _syncManager_SyncJobUpdated(object sender, Model.Events.GenericEventArgs<SyncJob> e)
{
SendData(false);
}
void _syncManager_SyncJobCreated(object sender, Model.Events.GenericEventArgs<SyncJobCreationResult> e)
{
SendData(true);
}
void _syncManager_SyncJobCancelled(object sender, Model.Events.GenericEventArgs<SyncJob> e)
{
SendData(true);
}
/// <summary>
/// Gets the data to send.
/// </summary>
/// <param name="state">The state.</param>
/// <returns>Task{SystemInfo}.</returns>
protected override async Task<IEnumerable<SyncJob>> GetDataToSend(WebSocketListenerState state)
{
var jobs = await _syncManager.GetJobs(new SyncJobQuery
{
UserId = _userId,
TargetId = _targetId
}).ConfigureAwait(false);
return jobs.Items;
}
protected override bool SendOnTimer
{
get
{
return false;
}
}
protected override void Dispose(bool dispose)
{
_syncManager.SyncJobCancelled -= _syncManager_SyncJobCancelled;
_syncManager.SyncJobCreated -= _syncManager_SyncJobCreated;
_syncManager.SyncJobUpdated -= _syncManager_SyncJobUpdated;
base.Dispose(dispose);
}
}
}

View File

@ -2,7 +2,6 @@
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Sync; using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync; using MediaBrowser.Model.Sync;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
@ -38,6 +37,34 @@ namespace MediaBrowser.Api.Sync
{ {
} }
[Route("/Sync/JobItems/{Id}/Enable", "POST", Summary = "Enables a cancelled or queued sync job item")]
public class EnableSyncJobItem : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sync/JobItems/{Id}/MarkForRemoval", "POST", Summary = "Marks a job item for removal")]
public class MarkJobItemForRemoval : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sync/JobItems/{Id}/UnmarkForRemoval", "POST", Summary = "Unmarks a job item for removal")]
public class UnmarkJobItemForRemoval : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sync/JobItems/{Id}", "DELETE", Summary = "Cancels a sync job item")]
public class CancelSyncJobItem : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
[Route("/Sync/Jobs", "GET", Summary = "Gets sync jobs.")] [Route("/Sync/Jobs", "GET", Summary = "Gets sync jobs.")]
public class GetSyncJobs : SyncJobQuery, IReturn<QueryResult<SyncJob>> public class GetSyncJobs : SyncJobQuery, IReturn<QueryResult<SyncJob>>
{ {
@ -85,6 +112,16 @@ namespace MediaBrowser.Api.Sync
public string Id { get; set; } public string Id { get; set; }
} }
[Route("/Sync/JobItems/{Id}/AdditionalFiles", "GET", Summary = "Gets a sync job item file")]
public class GetSyncJobItemAdditionalFile
{
[ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Name { get; set; }
}
[Route("/Sync/OfflineActions", "POST", Summary = "Reports an action that occurred while offline.")] [Route("/Sync/OfflineActions", "POST", Summary = "Reports an action that occurred while offline.")]
public class ReportOfflineActions : List<UserAction>, IReturnVoid public class ReportOfflineActions : List<UserAction>, IReturnVoid
{ {
@ -169,11 +206,14 @@ namespace MediaBrowser.Api.Sync
{ {
var jobItem = _syncManager.GetJobItem(request.Id); var jobItem = _syncManager.GetJobItem(request.Id);
if (jobItem.Status != SyncJobItemStatus.Transferring) if (jobItem.Status < SyncJobItemStatus.ReadyToTransfer)
{ {
throw new ArgumentException("The job item is not yet ready for transfer."); throw new ArgumentException("The job item is not yet ready for transfer.");
} }
var task = _syncManager.ReportSyncJobItemTransferBeginning(request.Id);
Task.WaitAll(task);
return ToStaticFileResult(jobItem.OutputPath); return ToStaticFileResult(jobItem.OutputPath);
} }
@ -198,10 +238,11 @@ namespace MediaBrowser.Api.Sync
} }
}; };
var dtos = request.ItemIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) var items = request.ItemIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(_libraryManager.GetItemById) .Select(_libraryManager.GetItemById)
.Where(i => i != null) .Where(i => i != null);
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions))
var dtos = _dtoService.GetBaseItemDtos(items, dtoOptions)
.ToList(); .ToList();
result.Options = SyncHelper.GetSyncOptions(dtos); result.Options = SyncHelper.GetSyncOptions(dtos);
@ -243,5 +284,52 @@ namespace MediaBrowser.Api.Sync
Task.WaitAll(task); Task.WaitAll(task);
} }
public object Get(GetSyncJobItemAdditionalFile request)
{
var jobItem = _syncManager.GetJobItem(request.Id);
if (jobItem.Status < SyncJobItemStatus.ReadyToTransfer)
{
throw new ArgumentException("The job item is not yet ready for transfer.");
}
var file = jobItem.AdditionalFiles.FirstOrDefault(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
if (file == null)
{
throw new ArgumentException("Sync job additional file not found.");
}
return ToStaticFileResult(file.Path);
}
public void Post(EnableSyncJobItem request)
{
var task = _syncManager.ReEnableJobItem(request.Id);
Task.WaitAll(task);
}
public void Delete(CancelSyncJobItem request)
{
var task = _syncManager.CancelJobItem(request.Id);
Task.WaitAll(task);
}
public void Post(MarkJobItemForRemoval request)
{
var task = _syncManager.MarkJobItemForRemoval(request.Id);
Task.WaitAll(task);
}
public void Post(UnmarkJobItemForRemoval request)
{
var task = _syncManager.UnmarkJobItemForRemoval(request.Id);
Task.WaitAll(task);
}
} }
} }

View File

@ -156,6 +156,20 @@ namespace MediaBrowser.Api
[ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AdjacentTo { get; set; } public string AdjacentTo { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
/// </summary>
/// <value>The start index.</value>
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? StartIndex { get; set; }
/// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; }
} }
[Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")] [Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
@ -238,7 +252,9 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetSimilarShows request) public object Get(GetSimilarShows request)
{ {
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, var dtoOptions = GetDtoOptions(request);
var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo, _itemRepo,
_libraryManager, _libraryManager,
_userDataManager, _userDataManager,
@ -254,10 +270,10 @@ namespace MediaBrowser.Api
{ {
var user = _userManager.GetUserById(request.UserId); var user = _userManager.GetUserById(request.UserId);
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId) var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode);
.OfType<Episode>();
var itemsList = _libraryManager.Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending) var itemsList = _libraryManager
.Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
.Cast<Episode>() .Cast<Episode>()
.ToList(); .ToList();
@ -270,9 +286,9 @@ namespace MediaBrowser.Api
var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit); var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
var options = request.GetDtoOptions(); var options = GetDtoOptions(request);
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, options, user)).ToArray(); var returnItems = _dtoService.GetBaseItemDtos(pagedItems, options, user).ToArray();
var result = new ItemsResult var result = new ItemsResult
{ {
@ -301,9 +317,9 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.UserId); var user = _userManager.GetUserById(request.UserId);
var options = request.GetDtoOptions(); var options = GetDtoOptions(request);
var returnItems = result.Items.Select(i => _dtoService.GetBaseItemDto(i, options, user)).ToArray(); var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user).ToArray();
return ToOptimizedSerializedResultUsingCache(new ItemsResult return ToOptimizedSerializedResultUsingCache(new ItemsResult
{ {
@ -365,9 +381,9 @@ namespace MediaBrowser.Api
.Cast<Season>(); .Cast<Season>();
} }
var fields = request.GetItemFields().ToList(); var dtoOptions = GetDtoOptions(request);
var returnItems = seasons.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user)
.ToArray(); .ToArray();
return new ItemsResult return new ItemsResult
@ -411,7 +427,18 @@ namespace MediaBrowser.Api
IEnumerable<Episode> episodes; IEnumerable<Episode> episodes;
if (string.IsNullOrEmpty(request.SeasonId)) if (!string.IsNullOrWhiteSpace(request.SeasonId))
{
var season = _libraryManager.GetItemById(new Guid(request.SeasonId)) as Season;
if (season == null)
{
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId);
}
episodes = season.GetEpisodes(user);
}
else if (request.Season.HasValue)
{ {
var series = _libraryManager.GetItemById(request.Id) as Series; var series = _libraryManager.GetItemById(request.Id) as Series;
@ -424,14 +451,14 @@ namespace MediaBrowser.Api
} }
else else
{ {
var season = _libraryManager.GetItemById(new Guid(request.SeasonId)) as Season; var series = _libraryManager.GetItemById(request.Id) as Series;
if (season == null) if (series == null)
{ {
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId); throw new ResourceNotFoundException("No series exists with Id " + request.Id);
} }
episodes = season.GetEpisodes(user); episodes = series.GetEpisodes(user);
} }
// Filter after the fact in case the ui doesn't want them // Filter after the fact in case the ui doesn't want them
@ -448,24 +475,27 @@ namespace MediaBrowser.Api
episodes = episodes.Where(i => i.IsVirtualUnaired == val); episodes = episodes.Where(i => i.IsVirtualUnaired == val);
} }
IEnumerable<BaseItem> returnItems = episodes;
// This must be the last filter // This must be the last filter
if (!string.IsNullOrEmpty(request.AdjacentTo)) if (!string.IsNullOrEmpty(request.AdjacentTo))
{ {
episodes = UserViewBuilder.FilterForAdjacency(episodes, request.AdjacentTo) returnItems = UserViewBuilder.FilterForAdjacency(returnItems, request.AdjacentTo);
.Cast<Episode>();
} }
var fields = request.GetItemFields().ToList(); returnItems = _libraryManager.ReplaceVideosWithPrimaryVersions(returnItems);
episodes = _libraryManager.ReplaceVideosWithPrimaryVersions(episodes).Cast<Episode>(); var pagedItems = ApplyPaging(returnItems, request.StartIndex, request.Limit);
var returnItems = episodes.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) var dtoOptions = GetDtoOptions(request);
var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user)
.ToArray(); .ToArray();
return new ItemsResult return new ItemsResult
{ {
TotalRecordCount = returnItems.Length, TotalRecordCount = dtos.Length,
Items = returnItems Items = dtos
}; };
} }
} }

View File

@ -83,7 +83,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = GetArtist(request.Name, LibraryManager); var item = GetArtist(request.Name, LibraryManager);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {
@ -130,8 +130,8 @@ namespace MediaBrowser.Api.UserLibrary
if (request is GetAlbumArtists) if (request is GetAlbumArtists)
{ {
return items return items
.Where(i => !i.IsFolder)
.OfType<IHasAlbumArtist>() .OfType<IHasAlbumArtist>()
.Where(i => !(i is MusicAlbum))
.SelectMany(i => i.AlbumArtists) .SelectMany(i => i.AlbumArtists)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => .Select(name =>
@ -150,8 +150,8 @@ namespace MediaBrowser.Api.UserLibrary
} }
return items return items
.Where(i => !i.IsFolder)
.OfType<IHasArtist>() .OfType<IHasArtist>()
.Where(i => !(i is MusicAlbum))
.SelectMany(i => i.AllArtists) .SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => .Select(name =>

View File

@ -56,46 +56,52 @@ namespace MediaBrowser.Api.UserLibrary
protected ItemsResult GetResult(GetItemsByName request) protected ItemsResult GetResult(GetItemsByName request)
{ {
User user = null; User user = null;
BaseItem item; BaseItem parentItem;
List<BaseItem> libraryItems; List<BaseItem> libraryItems;
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {
user = UserManager.GetUserById(request.UserId.Value); user = UserManager.GetUserById(request.UserId.Value);
item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId); parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList(); libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
} }
else else
{ {
item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId); parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
libraryItems = LibraryManager.RootFolder.GetRecursiveChildren().ToList();
libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList();
} }
IEnumerable<BaseItem> items; IEnumerable<BaseItem> items;
if (item.IsFolder) var excludeItemTypes = request.GetExcludeItemTypes();
var includeItemTypes = request.GetIncludeItemTypes();
var mediaTypes = request.GetMediaTypes();
Func<BaseItem, bool> filter = i => FilterItem(request, i, excludeItemTypes, includeItemTypes, mediaTypes);
if (parentItem.IsFolder)
{ {
var folder = (Folder)item; var folder = (Folder)parentItem;
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {
items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user, true); items = request.Recursive ?
folder.GetRecursiveChildren(user, filter) :
folder.GetChildren(user, true).Where(filter);
} }
else else
{ {
items = request.Recursive ? folder.GetRecursiveChildren() : folder.Children; items = request.Recursive ?
folder.GetRecursiveChildren(filter) :
folder.Children.Where(filter);
} }
} }
else else
{ {
items = new[] { item }; items = new[] { parentItem }.Where(filter);
} }
items = FilterItems(request, items);
var extractedItems = GetAllItems(request, items); var extractedItems = GetAllItems(request, items);
var filteredItems = FilterItems(request, extractedItems, user); var filteredItems = FilterItems(request, extractedItems, user);
@ -129,7 +135,7 @@ namespace MediaBrowser.Api.UserLibrary
var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList())); var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList()));
var dtoOptions = request.GetDtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2)); var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2));
@ -290,33 +296,41 @@ namespace MediaBrowser.Api.UserLibrary
/// Filters the items. /// Filters the items.
/// </summary> /// </summary>
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
/// <param name="items">The items.</param> /// <param name="f">The f.</param>
/// <param name="excludeItemTypes">The exclude item types.</param>
/// <param name="includeItemTypes">The include item types.</param>
/// <param name="mediaTypes">The media types.</param>
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
protected virtual IEnumerable<BaseItem> FilterItems(GetItemsByName request, IEnumerable<BaseItem> items) protected bool FilterItem(GetItemsByName request, BaseItem f, string[] excludeItemTypes, string[] includeItemTypes, string[] mediaTypes)
{ {
// Exclude item types // Exclude item types
if (!string.IsNullOrEmpty(request.ExcludeItemTypes)) if (excludeItemTypes.Length > 0)
{ {
var vals = request.ExcludeItemTypes.Split(','); if (excludeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); {
return false;
}
} }
// Include item types // Include item types
if (!string.IsNullOrEmpty(request.IncludeItemTypes)) if (includeItemTypes.Length > 0)
{ {
var vals = request.IncludeItemTypes.Split(','); if (!includeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); {
return false;
}
} }
// Include MediaTypes // Include MediaTypes
if (!string.IsNullOrEmpty(request.MediaTypes)) if (mediaTypes.Length > 0)
{ {
var vals = request.MediaTypes.Split(','); if (!mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
items = items.Where(f => vals.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)); return false;
}
} }
return items; return true;
} }
/// <summary> /// <summary>

View File

@ -69,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = GetGameGenre(request.Name, LibraryManager); var item = GetGameGenre(request.Name, LibraryManager);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {

View File

@ -74,7 +74,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = GetGenre(request.Name, LibraryManager); var item = GetGenre(request.Name, LibraryManager);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {

View File

@ -169,8 +169,6 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ExcludeLocationTypes { get; set; } public string ExcludeLocationTypes { get; set; }
public bool IncludeIndexContainers { get; set; }
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; } public bool? IsMissing { get; set; }
@ -321,14 +319,14 @@ namespace MediaBrowser.Api.UserLibrary
var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false); var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
var isFiltered = result.Item2; var isFiltered = result.Item2;
var dtoOptions = request.GetDtoOptions(); var dtoOptions = GetDtoOptions(request);
if (isFiltered) if (isFiltered)
{ {
return new ItemsResult return new ItemsResult
{ {
TotalRecordCount = result.Item1.TotalRecordCount, TotalRecordCount = result.Item1.TotalRecordCount,
Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() Items = _dtoService.GetBaseItemDtos(result.Item1.Items, dtoOptions, user).ToArray()
}; };
} }
@ -362,7 +360,7 @@ namespace MediaBrowser.Api.UserLibrary
var pagedItems = ApplyPaging(request, itemsArray); var pagedItems = ApplyPaging(request, itemsArray);
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); var returnItems = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ToArray();
return new ItemsResult return new ItemsResult
{ {
@ -396,53 +394,30 @@ namespace MediaBrowser.Api.UserLibrary
else if (request.Recursive) else if (request.Recursive)
{ {
if (user == null) var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
{
items = ((Folder)item).RecursiveChildren;
items = _libraryManager.ReplaceVideosWithPrimaryVersions(items);
}
else
{
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user));
return new Tuple<QueryResult<BaseItem>, bool>(result, true); return new Tuple<QueryResult<BaseItem>, bool>(result, true);
} }
}
else else
{ {
if (user == null) if (user == null)
{ {
items = ((Folder)item).Children; var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
items = _libraryManager.ReplaceVideosWithPrimaryVersions(items); return new Tuple<QueryResult<BaseItem>, bool>(result, true);
} }
else
{
var userRoot = item as UserRootFolder; var userRoot = item as UserRootFolder;
if (userRoot == null) if (userRoot == null)
{ {
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)); var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
return new Tuple<QueryResult<BaseItem>, bool>(result, true); return new Tuple<QueryResult<BaseItem>, bool>(result, true);
} }
items = ((Folder)item).GetChildren(user, true); items = ((Folder)item).GetChildren(user, true);
} }
}
if (request.IncludeIndexContainers)
{
var list = items.ToList();
var containers = list.Select(i => i.IndexContainer)
.Where(i => i != null);
list.AddRange(containers);
items = list.Distinct();
}
return new Tuple<QueryResult<BaseItem>, bool>(new QueryResult<BaseItem> return new Tuple<QueryResult<BaseItem>, bool>(new QueryResult<BaseItem>
{ {
@ -464,7 +439,7 @@ namespace MediaBrowser.Api.UserLibrary
SortBy = request.GetOrderBy(), SortBy = request.GetOrderBy(),
SortOrder = request.SortOrder ?? SortOrder.Ascending, SortOrder = request.SortOrder ?? SortOrder.Ascending,
Filter = (i, u) => ApplyAdditionalFilters(request, i, u, true, _libraryManager), Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager),
Limit = request.Limit, Limit = request.Limit,
StartIndex = request.StartIndex, StartIndex = request.StartIndex,

View File

@ -69,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = GetMusicGenre(request.Name, LibraryManager); var item = GetMusicGenre(request.Name, LibraryManager);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {

View File

@ -4,7 +4,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using ServiceStack; using ServiceStack;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -86,7 +85,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = GetPerson(request.Name, LibraryManager); var item = GetPerson(request.Name, LibraryManager);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {

View File

@ -73,7 +73,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = GetStudio(request.Name, LibraryManager); var item = GetStudio(request.Name, LibraryManager);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {

View File

@ -228,7 +228,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary> /// </summary>
/// <value>The user id.</value> /// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public Guid UserId { get; set; } public string UserId { get; set; }
[ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int Limit { get; set; } public int Limit { get; set; }
@ -304,74 +304,17 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var user = _userManager.GetUserById(request.UserId); var user = _userManager.GetUserById(request.UserId);
// Avoid implicitly captured closure var list = _userViewManager.GetLatestItems(new LatestItemsQuery
var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ?
GetItemsConfiguredForLatest(user) :
GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId);
libraryItems = libraryItems.OrderByDescending(i => i.DateCreated)
.Where(i => i.LocationType != LocationType.Virtual);
//if (request.IsFolder.HasValue)
//{
//var val = request.IsFolder.Value;
libraryItems = libraryItems.Where(f => f.IsFolder == false);
//}
if (!string.IsNullOrEmpty(request.IncludeItemTypes))
{ {
var vals = request.IncludeItemTypes.Split(','); GroupItems = request.GroupItems,
libraryItems = libraryItems.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
} IsPlayed = request.IsPlayed,
Limit = request.Limit,
ParentId = request.ParentId,
UserId = request.UserId
});
var currentUser = user; var options = GetDtoOptions(request);
if (request.IsPlayed.HasValue)
{
var takeLimit = request.Limit * 20;
var val = request.IsPlayed.Value;
libraryItems = libraryItems.Where(f => f.IsPlayed(currentUser) == val)
.Take(takeLimit);
}
// Avoid implicitly captured closure
var items = libraryItems
.ToList();
var list = new List<Tuple<BaseItem, List<BaseItem>>>();
foreach (var item in items)
{
// Only grab the index container for media
var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer;
if (container == null)
{
list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item }));
}
else
{
var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id == container.Id);
if (current != null)
{
current.Item2.Add(item);
}
else
{
list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item }));
}
}
if (list.Count >= request.Limit)
{
break;
}
}
var options = request.GetDtoOptions();
var dtos = list.Select(i => var dtos = list.Select(i =>
{ {
@ -394,15 +337,6 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(dtos.ToList()); return ToOptimizedResult(dtos.ToList());
} }
private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user)
{
return user.RootFolder.GetChildren(user, true)
.OfType<Folder>()
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
.SelectMany(i => i.GetRecursiveChildren(user))
.DistinctBy(i => i.Id);
}
public async Task<object> Get(GetUserViews request) public async Task<object> Get(GetUserViews request)
{ {
var user = _userManager.GetUserById(request.UserId); var user = _userManager.GetUserById(request.UserId);
@ -420,7 +354,7 @@ namespace MediaBrowser.Api.UserLibrary
var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false); var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray(); .ToArray();
@ -447,14 +381,13 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the child tree // Get them from the child tree
if (series != null) if (series != null)
{ {
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
// Avoid implicitly captured closure // Avoid implicitly captured closure
var currentUser = user; var currentUser = user;
var dtos = series var dtos = series
.GetRecursiveChildren() .GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
.Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
.OrderBy(i => .OrderBy(i =>
{ {
if (i.PremiereDate.HasValue) if (i.PremiereDate.HasValue)
@ -479,7 +412,7 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the db // Get them from the db
if (movie != null) if (movie != null)
{ {
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = movie.SpecialFeatureIds var dtos = movie.SpecialFeatureIds
.Select(_libraryManager.GetItemById) .Select(_libraryManager.GetItemById)
@ -518,11 +451,10 @@ namespace MediaBrowser.Api.UserLibrary
trailerIds = hasTrailers.GetTrailerIds(); trailerIds = hasTrailers.GetTrailerIds();
} }
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = trailerIds var dtos = trailerIds
.Select(_libraryManager.GetItemById) .Select(_libraryManager.GetItemById)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
return dtos.ToList(); return dtos.ToList();
@ -539,7 +471,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@ -557,7 +489,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = user.RootFolder; var item = user.RootFolder;
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@ -577,7 +509,7 @@ namespace MediaBrowser.Api.UserLibrary
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray(); .ToArray();

View File

@ -73,7 +73,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
var item = LibraryManager.GetYear(request.Year); var item = LibraryManager.GetYear(request.Year);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue) if (request.UserId.HasValue)
{ {

View File

@ -11,7 +11,6 @@ using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Controller;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -56,6 +55,21 @@ namespace MediaBrowser.Api
public string Id { get; set; } public string Id { get; set; }
} }
/// <summary>
/// Class GetUser
/// </summary>
[Route("/Users/{Id}/Offline", "GET", Summary = "Gets an offline user record by Id")]
[Authenticated]
public class GetOfflineUser : IReturn<UserDto>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
/// <summary> /// <summary>
/// Class DeleteUser /// Class DeleteUser
/// </summary> /// </summary>
@ -148,6 +162,32 @@ namespace MediaBrowser.Api
public bool ResetPassword { get; set; } public bool ResetPassword { get; set; }
} }
/// <summary>
/// Class UpdateUserEasyPassword
/// </summary>
[Route("/Users/{Id}/EasyPassword", "POST", Summary = "Updates a user's easy password")]
[Authenticated]
public class UpdateUserEasyPassword : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the new password.
/// </summary>
/// <value>The new password.</value>
public string NewPassword { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [reset password].
/// </summary>
/// <value><c>true</c> if [reset password]; otherwise, <c>false</c>.</value>
public bool ResetPassword { get; set; }
}
/// <summary> /// <summary>
/// Class UpdateUser /// Class UpdateUser
/// </summary> /// </summary>
@ -294,7 +334,7 @@ namespace MediaBrowser.Api
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp)) .Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToList(); .ToList();
return ToOptimizedSerializedResultUsingCache(result); return ToOptimizedResult(result);
} }
/// <summary> /// <summary>
@ -313,7 +353,23 @@ namespace MediaBrowser.Api
var result = _userManager.GetUserDto(user, Request.RemoteIp); var result = _userManager.GetUserDto(user, Request.RemoteIp);
return ToOptimizedSerializedResultUsingCache(result); return ToOptimizedResult(result);
}
public object Get(GetOfflineUser request)
{
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
var result = _userManager.GetOfflineUserDto(user, auth.DeviceId);
return ToOptimizedResult(result);
} }
/// <summary> /// <summary>
@ -410,6 +466,8 @@ namespace MediaBrowser.Api
public async Task PostAsync(UpdateUserPassword request) public async Task PostAsync(UpdateUserPassword request)
{ {
AssertCanUpdateUser(request.Id);
var user = _userManager.GetUserById(request.Id); var user = _userManager.GetUserById(request.Id);
if (user == null) if (user == null)
@ -434,6 +492,33 @@ namespace MediaBrowser.Api
} }
} }
public void Post(UpdateUserEasyPassword request)
{
var task = PostAsync(request);
Task.WaitAll(task);
}
public async Task PostAsync(UpdateUserEasyPassword request)
{
AssertCanUpdateUser(request.Id);
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
if (request.ResetPassword)
{
await _userManager.ResetEasyPassword(user).ConfigureAwait(false);
}
else
{
await _userManager.ChangeEasyPassword(user, request.NewPassword).ConfigureAwait(false);
}
}
/// <summary> /// <summary>
/// Posts the specified request. /// Posts the specified request.
/// </summary> /// </summary>
@ -449,14 +534,15 @@ namespace MediaBrowser.Api
{ {
// We need to parse this manually because we told service stack not to with IRequiresRequestStream // We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var pathInfo = PathInfo.Parse(Request.PathInfo); var id = GetPathValue(1);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
AssertCanUpdateUser(id);
var dtoUser = request; var dtoUser = request;
var user = _userManager.GetUserById(id); var user = _userManager.GetUserById(id);
var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ? var task = string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal) ?
_userManager.UpdateUser(user) : _userManager.UpdateUser(user) :
_userManager.RenameUser(user, dtoUser.Name); _userManager.RenameUser(user, dtoUser.Name);
@ -500,11 +586,29 @@ namespace MediaBrowser.Api
public void Post(UpdateUserConfiguration request) public void Post(UpdateUserConfiguration request)
{ {
AssertCanUpdateUser(request.Id);
var task = _userManager.UpdateConfiguration(request.Id, request); var task = _userManager.UpdateConfiguration(request.Id, request);
Task.WaitAll(task); Task.WaitAll(task);
} }
private void AssertCanUpdateUser(string userId)
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
// If they're going to update the record of another user, they must be an administrator
if (!string.Equals(userId, auth.UserId, StringComparison.OrdinalIgnoreCase))
{
var authenticatedUser = _userManager.GetUserById(auth.UserId);
if (!authenticatedUser.Policy.IsAdministrator)
{
throw new SecurityException("Unauthorized access.");
}
}
}
public void Post(UpdateUserPolicy request) public void Post(UpdateUserPolicy request)
{ {
var task = UpdateUserPolicy(request); var task = UpdateUserPolicy(request);

View File

@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using ServiceStack; using ServiceStack;
using System; using System;
@ -80,7 +79,7 @@ namespace MediaBrowser.Api
: _libraryManager.RootFolder) : _libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id); : _libraryManager.GetItemById(request.Id);
var dtoOptions = new DtoOptions(); var dtoOptions = GetDtoOptions(request);
var video = (Video)item; var video = (Video)item;

View File

@ -475,7 +475,7 @@ namespace MediaBrowser.Common.Implementations
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager); SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager);
RegisterSingleInstance(SecurityManager); RegisterSingleInstance(SecurityManager);
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager); InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
RegisterSingleInstance(InstallationManager); RegisterSingleInstance(InstallationManager);
ZipClient = new ZipClient(); ZipClient = new ZipClient();

View File

@ -690,7 +690,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{ {
try try
{ {
File.Delete(file); _fileSystem.DeleteFile(file);
} }
catch (IOException) catch (IOException)
{ {

View File

@ -1,4 +1,4 @@
using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
@ -270,8 +270,8 @@ namespace MediaBrowser.Common.Implementations.IO
File.Copy(temp1, file2, true); File.Copy(temp1, file2, true);
File.Copy(temp2, file1, true); File.Copy(temp2, file1, true);
File.Delete(temp1); DeleteFile(temp1);
File.Delete(temp2); DeleteFile(temp2);
} }
/// <summary> /// <summary>
@ -409,5 +409,25 @@ namespace MediaBrowser.Common.Implementations.IO
//return Path.IsPathRooted(path); //return Path.IsPathRooted(path);
} }
public void DeleteFile(string path, bool sendToRecycleBin)
{
File.Delete(path);
}
public void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin)
{
Directory.Delete(path, recursive);
}
public void DeleteFile(string path)
{
DeleteFile(path, false);
}
public void DeleteDirectory(string path, bool recursive)
{
DeleteDirectory(path, recursive, false);
}
} }
} }

View File

@ -48,21 +48,21 @@
<RunPostBuildEvent>Always</RunPostBuildEvent> <RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="NLog, Version=3.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=3.2.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.3.1.0.0\lib\net45\NLog.dll</HintPath> <HintPath>..\packages\NLog.3.2.0.0\lib\net45\NLog.dll</HintPath>
</Reference> </Reference>
<Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL"> <Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath> <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
</Reference> </Reference>
<Reference Include="SimpleInjector, Version=2.6.1.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> <Reference Include="SimpleInjector, Version=2.7.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SimpleInjector.2.6.1\lib\net45\SimpleInjector.dll</HintPath> <HintPath>..\packages\SimpleInjector.2.7.0\lib\net45\SimpleInjector.dll</HintPath>
</Reference> </Reference>
<Reference Include="SimpleInjector.Diagnostics, Version=2.6.1.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> <Reference Include="SimpleInjector.Diagnostics, Version=2.7.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SimpleInjector.2.6.1\lib\net45\SimpleInjector.Diagnostics.dll</HintPath> <HintPath>..\packages\SimpleInjector.2.7.0\lib\net45\SimpleInjector.Diagnostics.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />

View File

@ -18,11 +18,64 @@ namespace MediaBrowser.Common.Implementations.Networking
Logger = logger; Logger = logger;
} }
private volatile List<string> _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
/// <summary> /// <summary>
/// Gets the machine's local ip address /// Gets the machine's local ip address
/// </summary> /// </summary>
/// <returns>IPAddress.</returns> /// <returns>IPAddress.</returns>
public IEnumerable<string> GetLocalIpAddresses() public IEnumerable<string> GetLocalIpAddresses()
{
if (_localIpAddresses == null)
{
lock (_localIpAddressSyncLock)
{
if (_localIpAddresses == null)
{
var addresses = GetLocalIpAddressesInternal().ToList();
_localIpAddresses = addresses;
BindEvents();
return addresses;
}
}
}
return _localIpAddresses;
}
private void BindEvents()
{
NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;
NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged;
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
}
void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
Logger.Debug("NetworkAvailabilityChanged fired. Resetting cached network info.");
lock (_localIpAddressSyncLock)
{
_localIpAddresses = null;
}
}
void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{
Logger.Debug("NetworkAddressChanged fired. Resetting cached network info.");
lock (_localIpAddressSyncLock)
{
_localIpAddresses = null;
}
}
private IEnumerable<string> GetLocalIpAddressesInternal()
{ {
var list = GetIPsDefault() var list = GetIPsDefault()
.Where(i => !IPAddress.IsLoopback(i)) .Where(i => !IPAddress.IsLoopback(i))
@ -53,6 +106,11 @@ namespace MediaBrowser.Common.Implementations.Networking
// Private address space: // Private address space:
// http://en.wikipedia.org/wiki/Private_network // http://en.wikipedia.org/wiki/Private_network
if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase))
{
return Is172AddressPrivate(endpoint);
}
return return
// If url was requested with computer name, we may see this // If url was requested with computer name, we may see this
@ -61,11 +119,23 @@ namespace MediaBrowser.Common.Implementations.Networking
endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase); endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
} }
private bool Is172AddressPrivate(string endpoint)
{
for (var i = 16; i <= 31; i++)
{
if (endpoint.StartsWith("172." + i.ToString(CultureInfo.InvariantCulture) + ".", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public bool IsInLocalNetwork(string endpoint) public bool IsInLocalNetwork(string endpoint)
{ {
return IsInLocalNetworkInternal(endpoint, true); return IsInLocalNetworkInternal(endpoint, true);

View File

@ -108,13 +108,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// </summary> /// </summary>
private TaskResult _lastExecutionResult; private TaskResult _lastExecutionResult;
/// <summary> /// <summary>
/// The _last execution resultinitialized
/// </summary>
private bool _lastExecutionResultinitialized;
/// <summary>
/// The _last execution result sync lock /// The _last execution result sync lock
/// </summary> /// </summary>
private object _lastExecutionResultSyncLock = new object(); private readonly object _lastExecutionResultSyncLock = new object();
/// <summary> /// <summary>
/// Gets the last execution result. /// Gets the last execution result.
/// </summary> /// </summary>
@ -123,7 +119,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{ {
get get
{ {
LazyInitializer.EnsureInitialized(ref _lastExecutionResult, ref _lastExecutionResultinitialized, ref _lastExecutionResultSyncLock, () => if (_lastExecutionResult == null)
{
lock (_lastExecutionResultSyncLock)
{
if (_lastExecutionResult == null)
{ {
var path = GetHistoryFilePath(); var path = GetHistoryFilePath();
@ -134,27 +134,24 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {
// File doesn't exist. No biggie // File doesn't exist. No biggie
return null;
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
// File doesn't exist. No biggie // File doesn't exist. No biggie
return null;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Error deserializing {0}", ex, path); Logger.ErrorException("Error deserializing {0}", ex, path);
return null;
} }
}); }
}
}
return _lastExecutionResult; return _lastExecutionResult;
} }
private set private set
{ {
_lastExecutionResult = value; _lastExecutionResult = value;
_lastExecutionResultinitialized = value != null;
} }
} }
@ -227,13 +224,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// </summary> /// </summary>
private IEnumerable<ITaskTrigger> _triggers; private IEnumerable<ITaskTrigger> _triggers;
/// <summary> /// <summary>
/// The _triggers initialized
/// </summary>
private bool _triggersInitialized;
/// <summary>
/// The _triggers sync lock /// The _triggers sync lock
/// </summary> /// </summary>
private object _triggersSyncLock = new object(); private readonly object _triggersSyncLock = new object();
/// <summary> /// <summary>
/// Gets the triggers that define when the task will run /// Gets the triggers that define when the task will run
/// </summary> /// </summary>
@ -243,7 +236,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{ {
get get
{ {
LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, LoadTriggers); if (_triggers == null)
{
lock (_triggersSyncLock)
{
if (_triggers == null)
{
_triggers = LoadTriggers();
}
}
}
return _triggers; return _triggers;
} }
@ -262,8 +264,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
_triggers = value.ToList(); _triggers = value.ToList();
_triggersInitialized = true;
ReloadTriggerEvents(false); ReloadTriggerEvents(false);
SaveTriggers(_triggers); SaveTriggers(_triggers);
@ -335,12 +335,30 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
trigger.Start(false); trigger.Start(false);
} }
private Task _currentTask;
/// <summary> /// <summary>
/// Executes the task /// Executes the task
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.InvalidOperationException">Cannot execute a Task that is already running</exception> /// <exception cref="System.InvalidOperationException">Cannot execute a Task that is already running</exception>
public async Task Execute() public async Task Execute()
{
var task = ExecuteInternal();
_currentTask = task;
try
{
await task.ConfigureAwait(false);
}
finally
{
_currentTask = null;
}
}
private async Task ExecuteInternal()
{ {
// Cancel the current execution, if any // Cancel the current execution, if any
if (CurrentCancellationTokenSource != null) if (CurrentCancellationTokenSource != null)
@ -544,6 +562,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
Id = Id Id = Id
}; };
var hasKey = ScheduledTask as IHasKey;
if (hasKey != null)
{
result.Key = hasKey.Key;
}
if (ex != null) if (ex != null)
{ {
result.ErrorMessage = ex.Message; result.ErrorMessage = ex.Message;
@ -579,14 +603,60 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{ {
DisposeTriggers(); DisposeTriggers();
if (State == TaskState.Running) var wassRunning = State == TaskState.Running;
var startTime = CurrentExecutionStartTime;
var token = CurrentCancellationTokenSource;
if (token != null)
{ {
OnTaskCompleted(CurrentExecutionStartTime, DateTime.UtcNow, TaskCompletionStatus.Aborted, null); try
{
Logger.Debug(Name + ": Cancelling");
token.Cancel();
}
catch (Exception ex)
{
Logger.ErrorException("Error calling CancellationToken.Cancel();", ex);
}
}
var task = _currentTask;
if (task != null)
{
try
{
Logger.Debug(Name + ": Waiting on Task");
var exited = Task.WaitAll(new[] { task }, 2000);
if (exited)
{
Logger.Debug(Name + ": Task exited");
}
else
{
Logger.Debug(Name + ": Timed out waiting for task to stop");
}
}
catch (Exception ex)
{
Logger.ErrorException("Error calling Task.WaitAll();", ex);
}
} }
if (CurrentCancellationTokenSource != null) if (token != null)
{ {
CurrentCancellationTokenSource.Dispose(); try
{
Logger.Debug(Name + ": Disposing CancellationToken");
token.Dispose();
}
catch (Exception ex)
{
Logger.ErrorException("Error calling CancellationToken.Dispose();", ex);
}
}
if (wassRunning)
{
OnTaskCompleted(startTime, DateTime.UtcNow, TaskCompletionStatus.Aborted, null);
} }
} }
} }

View File

@ -125,7 +125,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
{ {
try try
{ {
File.Delete(path); _fileSystem.DeleteFile(path);
} }
catch (IOException ex) catch (IOException ex)
{ {

View File

@ -76,7 +76,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
File.Delete(file.FullName); _fileSystem.DeleteFile(file.FullName);
index++; index++;
} }

View File

@ -1,6 +1,7 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Implementations.Security; using MediaBrowser.Common.Implementations.Security;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
@ -106,6 +107,7 @@ namespace MediaBrowser.Common.Implementations.Updates
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly ISecurityManager _securityManager; private readonly ISecurityManager _securityManager;
private readonly IConfigurationManager _config; private readonly IConfigurationManager _config;
private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// Gets the application host. /// Gets the application host.
@ -113,7 +115,7 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <value>The application host.</value> /// <value>The application host.</value>
private readonly IApplicationHost _applicationHost; private readonly IApplicationHost _applicationHost;
public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config) public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem)
{ {
if (logger == null) if (logger == null)
{ {
@ -129,6 +131,7 @@ namespace MediaBrowser.Common.Implementations.Updates
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_securityManager = securityManager; _securityManager = securityManager;
_config = config; _config = config;
_fileSystem = fileSystem;
_logger = logger; _logger = logger;
} }
@ -570,7 +573,7 @@ namespace MediaBrowser.Common.Implementations.Updates
try try
{ {
File.Delete(tempFile); _fileSystem.DeleteFile(tempFile);
} }
catch (IOException e) catch (IOException e)
{ {
@ -591,7 +594,7 @@ namespace MediaBrowser.Common.Implementations.Updates
// Remove it the quick way for now // Remove it the quick way for now
_applicationHost.RemovePlugin(plugin); _applicationHost.RemovePlugin(plugin);
File.Delete(plugin.AssemblyFilePath); _fileSystem.DeleteFile(plugin.AssemblyFilePath);
OnPluginUninstalled(plugin); OnPluginUninstalled(plugin);

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NLog" version="3.1.0.0" targetFramework="net45" /> <package id="NLog" version="3.2.0.0" targetFramework="net45" />
<package id="SimpleInjector" version="2.6.1" targetFramework="net45" /> <package id="SimpleInjector" version="2.7.0" targetFramework="net45" />
</packages> </packages>

View File

@ -26,36 +26,6 @@ namespace MediaBrowser.Common.Extensions
return Regex.Replace(htmlString, pattern, string.Empty).Trim(); return Regex.Replace(htmlString, pattern, string.Empty).Trim();
} }
/// <summary>
/// Replaces the specified STR.
/// </summary>
/// <param name="str">The STR.</param>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
/// <param name="comparison">The comparison.</param>
/// <returns>System.String.</returns>
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
{
var sb = new StringBuilder();
var previousIndex = 0;
var index = str.IndexOf(oldValue, comparison);
while (index != -1)
{
sb.Append(str.Substring(previousIndex, index - previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = str.IndexOf(oldValue, index, comparison);
}
sb.Append(str.Substring(previousIndex));
return sb.ToString();
}
public static string RemoveDiacritics(this string text) public static string RemoveDiacritics(this string text)
{ {
return String.Concat( return String.Concat(

View File

@ -133,5 +133,33 @@ namespace MediaBrowser.Common.IO
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns>
bool IsPathFile(string path); bool IsPathFile(string path);
/// <summary>
/// Deletes the file.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="sendToRecycleBin">if set to <c>true</c> [send to recycle bin].</param>
void DeleteFile(string path, bool sendToRecycleBin);
/// <summary>
/// Deletes the directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
/// <param name="sendToRecycleBin">if set to <c>true</c> [send to recycle bin].</param>
void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin);
/// <summary>
/// Deletes the file.
/// </summary>
/// <param name="path">The path.</param>
void DeleteFile(string path);
/// <summary>
/// Deletes the directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
void DeleteDirectory(string path, bool recursive);
} }
} }

View File

@ -51,5 +51,12 @@ namespace MediaBrowser.Common.Net
/// <param name="endpoint">The endpoint.</param> /// <param name="endpoint">The endpoint.</param>
/// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns>
bool IsInLocalNetwork(string endpoint); bool IsInLocalNetwork(string endpoint);
/// <summary>
/// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
/// </summary>
/// <param name="certificatePath">The path to generate the certificate.</param>
/// <param name="hostname">The common name for the certificate.</param>
void GenerateSelfSignedSslCertificate(string certificatePath, string hostname);
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
{ {
@ -13,11 +14,21 @@ namespace MediaBrowser.Controller.Channels
public string OriginalChannelName { get; set; } public string OriginalChannelName { get; set; }
public override bool IsVisible(User user) public override bool IsVisible(User user)
{
if (user.Policy.BlockedChannels != null)
{ {
if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{ {
return false; return false;
} }
}
else
{
if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
return base.IsVisible(user); return base.IsVisible(user);
} }
@ -50,7 +61,22 @@ namespace MediaBrowser.Controller.Channels
protected override string GetInternalMetadataPath(string basePath) protected override string GetInternalMetadataPath(string basePath)
{ {
return System.IO.Path.Combine(basePath, "channels", Id.ToString("N"), "metadata"); return GetInternalMetadataPath(basePath, Id);
}
public static string GetInternalMetadataPath(string basePath, Guid id)
{
return System.IO.Path.Combine(basePath, "channels", id.ToString("N"), "metadata");
}
public override bool CanDelete()
{
return false;
}
protected override bool IsAllowTagFilterEnforced()
{
return false;
} }
} }
} }

View File

@ -3,10 +3,10 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
{ {
@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Channels
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
} }
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
return ExternalId; return ExternalId;
} }
@ -89,5 +89,10 @@ namespace MediaBrowser.Controller.Channels
return list; return list;
} }
public override bool CanDelete()
{
return false;
}
} }
} }

View File

@ -1,11 +1,10 @@
using System; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Channels; using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
{ {
@ -40,7 +39,7 @@ namespace MediaBrowser.Controller.Channels
return false; return false;
} }
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
return ExternalId; return ExternalId;
} }
@ -76,5 +75,10 @@ namespace MediaBrowser.Controller.Channels
{ {
return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N")); return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
} }
public override bool CanDelete()
{
return false;
}
} }
} }

View File

@ -4,11 +4,11 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
{ {
@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Channels
public List<ChannelMediaInfo> ChannelMediaSources { get; set; } public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
if (ContentType == ChannelMediaContentType.MovieExtra) if (ContentType == ChannelMediaContentType.MovieExtra)
{ {
@ -119,5 +119,10 @@ namespace MediaBrowser.Controller.Channels
{ {
return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N")); return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
} }
public override bool CanDelete()
{
return false;
}
} }
} }

View File

@ -60,5 +60,12 @@ namespace MediaBrowser.Controller.Collections
/// <param name="userId">The user identifier.</param> /// <param name="userId">The user identifier.</param>
/// <returns>Folder.</returns> /// <returns>Folder.</returns>
Folder GetCollectionsFolder(string userId); Folder GetCollectionsFolder(string userId);
/// <summary>
/// Gets the collections.
/// </summary>
/// <param name="user">The user.</param>
/// <returns>IEnumerable&lt;BoxSet&gt;.</returns>
IEnumerable<BoxSet> GetCollections(User user);
} }
} }

View File

@ -0,0 +1,10 @@
using MediaBrowser.Model.Devices;
namespace MediaBrowser.Controller.Devices
{
public class CameraImageUploadInfo
{
public LocalFileInfo FileInfo { get; set; }
public DeviceInfo Device { get; set; }
}
}

View File

@ -3,7 +3,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -15,6 +14,10 @@ namespace MediaBrowser.Controller.Devices
/// Occurs when [device options updated]. /// Occurs when [device options updated].
/// </summary> /// </summary>
event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated; event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
/// <summary>
/// Occurs when [camera image uploaded].
/// </summary>
event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
/// <summary> /// <summary>
/// Registers the device. /// Registers the device.

View File

@ -62,8 +62,9 @@ namespace MediaBrowser.Controller.Dlna
/// </summary> /// </summary>
/// <param name="headers">The headers.</param> /// <param name="headers">The headers.</param>
/// <param name="serverUuId">The server uu identifier.</param> /// <param name="serverUuId">The server uu identifier.</param>
/// <param name="serverAddress">The server address.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId); string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId, string serverAddress);
/// <summary> /// <summary>
/// Gets the icon. /// Gets the icon.

View File

@ -0,0 +1,7 @@

namespace MediaBrowser.Controller.Dlna
{
public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
{
}
}

View File

@ -17,6 +17,7 @@ namespace MediaBrowser.Controller.Dto
public List<ImageType> ImageTypes { get; set; } public List<ImageType> ImageTypes { get; set; }
public int ImageTypeLimit { get; set; } public int ImageTypeLimit { get; set; }
public bool EnableImages { get; set; } public bool EnableImages { get; set; }
public string DeviceId { get; set; }
public DtoOptions() public DtoOptions()
{ {

View File

@ -45,6 +45,17 @@ namespace MediaBrowser.Controller.Dto
/// <returns>BaseItemDto.</returns> /// <returns>BaseItemDto.</returns>
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null); BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
/// <summary>
/// Gets the base item dtos.
/// </summary>
/// <param name="items">The items.</param>
/// <param name="options">The options.</param>
/// <param name="user">The user.</param>
/// <param name="owner">The owner.</param>
/// <returns>IEnumerable&lt;BaseItemDto&gt;.</returns>
IEnumerable<BaseItemDto> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null,
BaseItem owner = null);
/// <summary> /// <summary>
/// Gets the chapter information dto. /// Gets the chapter information dto.
/// </summary> /// </summary>

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
[Obsolete]
public class AdultVideo : Video, IHasProductionLocations, IHasTaglines
{
public List<string> ProductionLocations { get; set; }
public List<string> Taglines { get; set; }
public AdultVideo()
{
Taglines = new List<string>();
ProductionLocations = new List<string>();
}
}
}

View File

@ -32,6 +32,11 @@ namespace MediaBrowser.Controller.Entities
} }
} }
public override bool CanDelete()
{
return false;
}
/// <summary> /// <summary>
/// The _virtual children /// The _virtual children
/// </summary> /// </summary>
@ -66,7 +71,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var path = ContainingFolderPath; var path = ContainingFolderPath;
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService) var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
{ {
FileInfo = new DirectoryInfo(path), FileInfo = new DirectoryInfo(path),
Path = path, Path = path,

View File

@ -4,11 +4,11 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio namespace MediaBrowser.Controller.Entities.Audio
{ {
@ -80,6 +80,15 @@ namespace MediaBrowser.Controller.Entities.Audio
} }
} }
[IgnoreDataMember]
protected override bool SupportsOwnedItems
{
get
{
return false;
}
}
[IgnoreDataMember] [IgnoreDataMember]
public override Folder LatestItemsIndexContainer public override Folder LatestItemsIndexContainer
{ {
@ -104,6 +113,13 @@ namespace MediaBrowser.Controller.Entities.Audio
} }
} }
public override bool CanDownload()
{
var locationType = LocationType;
return locationType != LocationType.Remote &&
locationType != LocationType.Virtual;
}
/// <summary> /// <summary>
/// Gets or sets the artist. /// Gets or sets the artist.
/// </summary> /// </summary>
@ -169,7 +185,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
var parent = FindParent<MusicAlbum>(); var parent = FindParent<MusicAlbum>();
@ -186,7 +202,7 @@ namespace MediaBrowser.Controller.Entities.Audio
} }
} }
return base.GetUserDataKey(); return base.CreateUserDataKey();
} }
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)
@ -223,7 +239,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{ {
Id = i.Id.ToString("N"), Id = i.Id.ToString("N"),
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
MediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(), MediaStreams = MediaSourceManager.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
Name = i.Name, Name = i.Name,
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path, Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks, RunTimeTicks = i.RunTimeTicks,

View File

@ -1,11 +1,11 @@
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio namespace MediaBrowser.Controller.Entities.Audio
{ {
@ -52,14 +52,14 @@ namespace MediaBrowser.Controller.Entities.Audio
} }
} }
public List<string> AlbumArtists { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public string AlbumArtist public string AlbumArtist
{ {
get { return AlbumArtists.FirstOrDefault(); } get { return AlbumArtists.FirstOrDefault(); }
} }
public List<string> AlbumArtists { get; set; }
/// <summary> /// <summary>
/// Gets the tracks. /// Gets the tracks.
/// </summary> /// </summary>
@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{ {
get get
{ {
return RecursiveChildren.OfType<Audio>(); return GetRecursiveChildren(i => i is Audio).Cast<Audio>();
} }
} }
@ -136,7 +136,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
var id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); var id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
@ -152,7 +152,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return "MusicAlbum-Musicbrainz-" + id; return "MusicAlbum-Musicbrainz-" + id;
} }
return base.GetUserDataKey(); return base.CreateUserDataKey();
} }
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)
@ -173,17 +173,12 @@ namespace MediaBrowser.Controller.Entities.Audio
id.ArtistProviderIds = artist.ProviderIds; id.ArtistProviderIds = artist.ProviderIds;
} }
id.SongInfos = RecursiveChildren.OfType<Audio>() id.SongInfos = GetRecursiveChildren(i => i is Audio)
.Cast<Audio>()
.Select(i => i.GetLookupInfo()) .Select(i => i.GetLookupInfo())
.ToList(); .ToList();
return id; return id;
} }
} }
[Obsolete]
public class MusicAlbumDisc : Folder
{
}
} }

View File

@ -1,14 +1,13 @@
using System.Runtime.Serialization; using MediaBrowser.Controller.Providers;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio namespace MediaBrowser.Controller.Entities.Audio
{ {
@ -20,6 +19,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public bool IsAccessedByName { get; set; } public bool IsAccessedByName { get; set; }
public List<string> ProductionLocations { get; set; } public List<string> ProductionLocations { get; set; }
[IgnoreDataMember]
public override bool IsFolder public override bool IsFolder
{ {
get get
@ -34,6 +34,11 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; } get { return true; }
} }
public override bool CanDelete()
{
return !IsAccessedByName;
}
protected override IEnumerable<BaseItem> ActualChildren protected override IEnumerable<BaseItem> ActualChildren
{ {
get get
@ -68,7 +73,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
return GetUserDataKey(this); return GetUserDataKey(this);
} }
@ -78,6 +83,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// If the item is a folder, it returns the folder itself /// If the item is a folder, it returns the folder itself
/// </summary> /// </summary>
/// <value>The containing folder path.</value> /// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath public override string ContainingFolderPath
{ {
get get
@ -90,6 +96,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets a value indicating whether this instance is owned item. /// Gets a value indicating whether this instance is owned item.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem public override bool IsOwnedItem
{ {
get get
@ -122,50 +129,27 @@ namespace MediaBrowser.Controller.Entities.Audio
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{ {
var items = RecursiveChildren.ToList(); var items = GetRecursiveChildren().ToList();
var songs = items.OfType<Audio>().ToList(); var songs = items.OfType<Audio>().ToList();
var others = items.Except(songs).ToList(); var others = items.Except(songs).ToList();
var totalItems = songs.Count + others.Count; var totalItems = songs.Count + others.Count;
var percentages = new Dictionary<Guid, double>(totalItems); var numComplete = 0;
var tasks = new List<Task>();
// Refresh songs // Refresh songs
foreach (var item in songs) foreach (var item in songs)
{ {
if (tasks.Count >= 2)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var innerProgress = new ActionableProgress<double>();
// Avoid implicitly captured closure await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
var currentChild = item;
innerProgress.RegisterAction(p =>
{
lock (percentages)
{
percentages[currentChild.Id] = p / 100;
var percent = percentages.Values.Sum(); numComplete++;
double percent = numComplete;
percent /= totalItems; percent /= totalItems;
percent *= 100; progress.Report(percent * 100);
progress.Report(percent);
} }
});
var taskChild = item;
tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
// Refresh current item // Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
@ -175,27 +159,13 @@ namespace MediaBrowser.Controller.Entities.Audio
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// Avoid implicitly captured closure
var currentChild = item;
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
lock (percentages)
{
percentages[currentChild.Id] = 1;
var percent = percentages.Values.Sum(); numComplete++;
double percent = numComplete;
percent /= totalItems; percent /= totalItems;
percent *= 100; progress.Report(percent * 100);
progress.Report(percent);
} }
}
progress.Report(100);
}
private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
progress.Report(100); progress.Report(100);
} }
@ -204,7 +174,8 @@ namespace MediaBrowser.Controller.Entities.Audio
{ {
var info = GetItemLookupInfo<ArtistInfo>(); var info = GetItemLookupInfo<ArtistInfo>();
info.SongInfos = RecursiveChildren.OfType<Audio>() info.SongInfos = GetRecursiveChildren(i => i is Audio)
.Cast<Audio>()
.Select(i => i.GetLookupInfo()) .Select(i => i.GetLookupInfo())
.ToList(); .ToList();
@ -213,9 +184,16 @@ namespace MediaBrowser.Controller.Entities.Audio
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems) public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{ {
return inputItems.OfType<IHasArtist>() return inputItems.Where(GetItemFilter());
.Where(i => i.HasArtist(Name)) }
.Cast<BaseItem>();
public Func<BaseItem, bool> GetItemFilter()
{
return i =>
{
var hasArtist = i as IHasArtist;
return hasArtist != null && hasArtist.HasArtist(Name);
};
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
return "MusicGenre-" + Name; return "MusicGenre-" + Name;
} }
@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// If the item is a folder, it returns the folder itself /// If the item is a folder, it returns the folder itself
/// </summary> /// </summary>
/// <value>The containing folder path.</value> /// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath public override string ContainingFolderPath
{ {
get get
@ -38,10 +39,16 @@ namespace MediaBrowser.Controller.Entities.Audio
} }
} }
public override bool CanDelete()
{
return false;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is owned item. /// Gets a value indicating whether this instance is owned item.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem public override bool IsOwnedItem
{ {
get get
@ -52,7 +59,12 @@ namespace MediaBrowser.Controller.Entities.Audio
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems) public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{ {
return inputItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase)); return inputItems.Where(GetItemFilter());
}
public Func<BaseItem, bool> GetItemFilter()
{
return i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
} }
} }
} }

View File

@ -239,6 +239,38 @@ namespace MediaBrowser.Controller.Entities
get { return this.GetImagePath(ImageType.Primary); } get { return this.GetImagePath(ImageType.Primary); }
} }
public virtual bool CanDelete()
{
var locationType = LocationType;
return locationType != LocationType.Remote &&
locationType != LocationType.Virtual;
}
public virtual bool IsAuthorizedToDelete(User user)
{
return user.Policy.EnableContentDeletion;
}
public bool CanDelete(User user)
{
return CanDelete() && IsAuthorizedToDelete(user);
}
public virtual bool CanDownload()
{
return false;
}
public virtual bool IsAuthorizedToDownload(User user)
{
return user.Policy.EnableContentDownloading;
}
public bool CanDownload(User user)
{
return CanDownload() && IsAuthorizedToDownload(user);
}
/// <summary> /// <summary>
/// Gets or sets the date created. /// Gets or sets the date created.
/// </summary> /// </summary>
@ -268,6 +300,7 @@ namespace MediaBrowser.Controller.Entities
public static IChannelManager ChannelManager { get; set; } public static IChannelManager ChannelManager { get; set; }
public static ICollectionManager CollectionManager { get; set; } public static ICollectionManager CollectionManager { get; set; }
public static IImageProcessor ImageProcessor { get; set; } public static IImageProcessor ImageProcessor { get; set; }
public static IMediaSourceManager MediaSourceManager { get; set; }
/// <summary> /// <summary>
/// Returns a <see cref="System.String" /> that represents this instance. /// Returns a <see cref="System.String" /> that represents this instance.
@ -359,7 +392,7 @@ namespace MediaBrowser.Controller.Entities
{ {
get get
{ {
if (!string.IsNullOrEmpty(ForcedSortName)) if (!string.IsNullOrWhiteSpace(ForcedSortName))
{ {
return ForcedSortName; return ForcedSortName;
} }
@ -379,21 +412,19 @@ namespace MediaBrowser.Controller.Entities
public string GetInternalMetadataPath() public string GetInternalMetadataPath()
{ {
return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath); var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
return GetInternalMetadataPath(basePath);
} }
protected virtual string GetInternalMetadataPath(string basePath) protected virtual string GetInternalMetadataPath(string basePath)
{ {
var idString = Id.ToString("N"); var idString = Id.ToString("N");
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString); if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder)
}
public static string GetInternalMetadataPathForId(Guid id)
{ {
var idString = id.ToString("N"); basePath = System.IO.Path.Combine(basePath, "library");
}
var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString); return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
} }
@ -692,7 +723,7 @@ namespace MediaBrowser.Controller.Entities
var requiresSave = false; var requiresSave = false;
if (IsFolder || Parent != null) if (SupportsOwnedItems)
{ {
try try
{ {
@ -724,6 +755,12 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
protected virtual bool SupportsOwnedItems
{
get { return IsFolder || Parent != null; }
}
/// <summary> /// <summary>
/// Refreshes owned items such as trailers, theme videos, special features, etc. /// Refreshes owned items such as trailers, theme videos, special features, etc.
/// Returns true or false indicating if changes were found. /// Returns true or false indicating if changes were found.
@ -889,11 +926,24 @@ namespace MediaBrowser.Controller.Entities
get { return null; } get { return null; }
} }
private string _userDataKey;
/// <summary> /// <summary>
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public virtual string GetUserDataKey() public string GetUserDataKey()
{
if (string.IsNullOrWhiteSpace(_userDataKey))
{
var key = CreateUserDataKey();
_userDataKey = key;
return key;
}
return _userDataKey;
}
protected virtual string CreateUserDataKey()
{ {
return Id.ToString(); return Id.ToString();
} }
@ -905,6 +955,12 @@ namespace MediaBrowser.Controller.Entities
return current.IsInMixedFolder == newItem.IsInMixedFolder; return current.IsInMixedFolder == newItem.IsInMixedFolder;
} }
public void AfterMetadataRefresh()
{
_sortName = null;
_userDataKey = null;
}
/// <summary> /// <summary>
/// Gets the preferred metadata language. /// Gets the preferred metadata language.
/// </summary> /// </summary>
@ -1024,7 +1080,8 @@ namespace MediaBrowser.Controller.Entities
if (hasTags != null) if (hasTags != null)
{ {
if (user.Policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) var policy = user.Policy;
if (policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
{ {
return false; return false;
} }
@ -1033,6 +1090,11 @@ namespace MediaBrowser.Controller.Entities
return true; return true;
} }
protected virtual bool IsAllowTagFilterEnforced()
{
return true;
}
/// <summary> /// <summary>
/// Gets the block unrated value. /// Gets the block unrated value.
/// </summary> /// </summary>
@ -1060,6 +1122,23 @@ namespace MediaBrowser.Controller.Entities
return IsParentalAllowed(user); return IsParentalAllowed(user);
} }
public virtual bool IsVisibleStandalone(User user)
{
if (!IsVisible(user))
{
return false;
}
if (Parents.Any(i => !i.IsVisible(user)))
{
return false;
}
// TODO: Need some work here, e.g. is in user library, for channels, can user access channel, etc.
return true;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is folder. /// Gets a value indicating whether this instance is folder.
/// </summary> /// </summary>
@ -1146,7 +1225,7 @@ namespace MediaBrowser.Controller.Entities
if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType)) if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType))
{ {
return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i => return LibraryManager.RootFolder.GetRecursiveChildren(i =>
{ {
if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase)) if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase))
{ {
@ -1164,7 +1243,8 @@ namespace MediaBrowser.Controller.Entities
} }
return false; return false;
});
}).FirstOrDefault();
} }
return null; return null;
@ -1458,7 +1538,7 @@ namespace MediaBrowser.Controller.Entities
currentFile.Attributes &= ~FileAttributes.Hidden; currentFile.Attributes &= ~FileAttributes.Hidden;
} }
currentFile.Delete(); FileSystem.DeleteFile(currentFile.FullName);
} }
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
@ -1703,6 +1783,9 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public virtual bool BeforeMetadataRefresh() public virtual bool BeforeMetadataRefresh()
{ {
_userDataKey = null;
_sortName = null;
var hasChanges = false; var hasChanges = false;
if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path)) if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))

View File

@ -11,5 +11,10 @@ namespace MediaBrowser.Controller.Entities
{ {
get { return null; } get { return null; }
} }
public override bool CanDelete()
{
return false;
}
} }
} }

View File

@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
@ -37,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
Tags = new List<string>(); Tags = new List<string>();
} }
public override bool CanDownload()
{
var locationType = LocationType;
return locationType != LocationType.Remote &&
locationType != LocationType.Virtual;
}
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)
{ {
return config.BlockUnratedItems.Contains(UnratedItem.Book); return config.BlockUnratedItems.Contains(UnratedItem.Book);

View File

@ -35,6 +35,11 @@ namespace MediaBrowser.Controller.Entities
} }
} }
public override bool CanDelete()
{
return false;
}
public string CollectionType { get; set; } public string CollectionType { get; set; }
/// <summary> /// <summary>
@ -86,7 +91,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var path = ContainingFolderPath; var path = ContainingFolderPath;
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService) var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
{ {
FileInfo = new DirectoryInfo(path), FileInfo = new DirectoryInfo(path),
Path = path, Path = path,
@ -121,12 +126,6 @@ namespace MediaBrowser.Controller.Entities
return args; return args;
} }
// Cache this since it will be used a lot
/// <summary>
/// The null task result
/// </summary>
private static readonly Task NullTaskResult = Task.FromResult<object>(null);
/// <summary> /// <summary>
/// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes
/// ***Currently does not contain logic to maintain items that are unavailable in the file system*** /// ***Currently does not contain logic to maintain items that are unavailable in the file system***
@ -138,7 +137,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="refreshOptions">The refresh options.</param> /// <param name="refreshOptions">The refresh options.</param>
/// <param name="directoryService">The directory service.</param> /// <param name="directoryService">The directory service.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
protected override async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{ {
var list = PhysicalLocationsList.ToList(); var list = PhysicalLocationsList.ToList();
@ -146,8 +145,10 @@ namespace MediaBrowser.Controller.Entities
if (!list.SequenceEqual(PhysicalLocationsList)) if (!list.SequenceEqual(PhysicalLocationsList))
{ {
await UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); return UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
} }
return Task.FromResult(true);
} }
/// <summary> /// <summary>

View File

@ -6,7 +6,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MoreLinq;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -15,13 +14,14 @@ using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
/// <summary> /// <summary>
/// Class Folder /// Class Folder
/// </summary> /// </summary>
public class Folder : BaseItem, IHasThemeMedia, IHasTags public class Folder : BaseItem, IHasThemeMedia, IHasTags, IHasPreferredMetadataLanguage
{ {
public static IUserManager UserManager { get; set; } public static IUserManager UserManager { get; set; }
public static IUserViewManager UserViewManager { get; set; } public static IUserViewManager UserViewManager { get; set; }
@ -30,6 +30,14 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> ThemeVideoIds { get; set; } public List<Guid> ThemeVideoIds { get; set; }
public List<string> Tags { get; set; } public List<string> Tags { get; set; }
public string PreferredMetadataLanguage { get; set; }
/// <summary>
/// Gets or sets the preferred metadata country code.
/// </summary>
/// <value>The preferred metadata country code.</value>
public string PreferredMetadataCountryCode { get; set; }
public Folder() public Folder()
{ {
LinkedChildren = new List<LinkedChild>(); LinkedChildren = new List<LinkedChild>();
@ -72,6 +80,19 @@ namespace MediaBrowser.Controller.Entities
} }
} }
protected override bool IsAllowTagFilterEnforced()
{
if (this is ICollectionFolder)
{
return false;
}
if (this is UserView)
{
return false;
}
return true;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance is physical root. /// Gets or sets a value indicating whether this instance is physical root.
/// </summary> /// </summary>
@ -98,6 +119,7 @@ namespace MediaBrowser.Controller.Entities
public virtual List<LinkedChild> LinkedChildren { get; set; } public virtual List<LinkedChild> LinkedChildren { get; set; }
[IgnoreDataMember]
protected virtual bool SupportsShortcutChildren protected virtual bool SupportsShortcutChildren
{ {
get { return true; } get { return true; }
@ -237,14 +259,13 @@ namespace MediaBrowser.Controller.Entities
protected virtual IEnumerable<string> GetIndexByOptions() protected virtual IEnumerable<string> GetIndexByOptions()
{ {
return new List<string> { return new List<string> {
{LocalizedStrings.Instance.GetString("NoneDispPref")}, {"None"},
{LocalizedStrings.Instance.GetString("PerformerDispPref")}, {"Performer"},
{LocalizedStrings.Instance.GetString("GenreDispPref")}, {"Genre"},
{LocalizedStrings.Instance.GetString("DirectorDispPref")}, {"Director"},
{LocalizedStrings.Instance.GetString("YearDispPref")}, {"Year"},
{LocalizedStrings.Instance.GetString("StudioDispPref")} {"Studio"}
}; };
} }
/// <summary> /// <summary>
@ -275,7 +296,17 @@ namespace MediaBrowser.Controller.Entities
{ {
get get
{ {
return _children ?? (_children = LoadChildrenInternal()); if (_children == null)
{
lock (_childrenSyncLock)
{
if (_children == null)
{
_children = LoadChildrenInternal();
}
}
}
return _children;
} }
} }
@ -301,7 +332,9 @@ namespace MediaBrowser.Controller.Entities
public override bool IsVisible(User user) public override bool IsVisible(User user)
{ {
if (this is ICollectionFolder) if (this is ICollectionFolder && !(this is BasePluginFolder))
{
if (user.Policy.BlockedMediaFolders != null)
{ {
if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) || if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) ||
@ -311,6 +344,14 @@ namespace MediaBrowser.Controller.Entities
return false; return false;
} }
} }
else
{
if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
}
return base.IsVisible(user); return base.IsVisible(user);
} }
@ -345,12 +386,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true) public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true)
{ {
return ValidateChildrenWithCancellationSupport(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService); return ValidateChildrenInternal(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService);
}
private Task ValidateChildrenWithCancellationSupport(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
return ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
} }
private Dictionary<Guid, BaseItem> GetActualChildrenDictionary() private Dictionary<Guid, BaseItem> GetActualChildrenDictionary()
@ -540,18 +576,15 @@ namespace MediaBrowser.Controller.Entities
var children = ActualChildren.ToList(); var children = ActualChildren.ToList();
var percentages = new Dictionary<Guid, double>(children.Count); var percentages = new Dictionary<Guid, double>(children.Count);
var numComplete = 0;
var tasks = new List<Task>(); var count = children.Count;
foreach (var child in children) foreach (var child in children)
{ {
if (tasks.Count >= 2)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
if (child.IsFolder)
{
var innerProgress = new ActionableProgress<double>(); var innerProgress = new ActionableProgress<double>();
// Avoid implicitly captured closure // Avoid implicitly captured closure
@ -562,28 +595,30 @@ namespace MediaBrowser.Controller.Entities
{ {
percentages[currentChild.Id] = p / 100; percentages[currentChild.Id] = p / 100;
var percent = percentages.Values.Sum(); var innerPercent = percentages.Values.Sum();
percent /= children.Count; innerPercent /= count;
percent *= 100; innerPercent *= 100;
progress.Report(percent); progress.Report(innerPercent);
} }
}); });
if (child.IsFolder)
{
await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken) await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
else else
{ {
// Avoid implicitly captured closure await RefreshChildMetadata(child, refreshOptions, false, new Progress<double>(), cancellationToken)
var taskChild = child; .ConfigureAwait(false);
}
tasks.Add(Task.Run(async () => await RefreshChildMetadata(taskChild, refreshOptions, false, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
} numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
} }
await Task.WhenAll(tasks).ConfigureAwait(false);
progress.Report(100); progress.Report(100);
} }
@ -648,7 +683,7 @@ namespace MediaBrowser.Controller.Entities
} }
}); });
await child.ValidateChildrenWithCancellationSupport(innerProgress, cancellationToken, true, false, null, directoryService) await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }
@ -678,12 +713,12 @@ namespace MediaBrowser.Controller.Entities
path = System.IO.Path.GetDirectoryName(path); path = System.IO.Path.GetDirectoryName(path);
} }
if (ContainsPath(LibraryManager.GetDefaultVirtualFolders(), originalPath)) if (ContainsPath(LibraryManager.GetVirtualFolders(), originalPath))
{ {
return true; return true;
} }
return UserManager.Users.Any(user => ContainsPath(LibraryManager.GetVirtualFolders(user), originalPath)); return ContainsPath(LibraryManager.GetVirtualFolders(), originalPath);
} }
/// <summary> /// <summary>
@ -731,28 +766,6 @@ namespace MediaBrowser.Controller.Entities
return childrenItems; return childrenItems;
} }
/// <summary>
/// Retrieves the child.
/// </summary>
/// <param name="child">The child.</param>
/// <returns>BaseItem.</returns>
private BaseItem RetrieveChild(Guid child)
{
var item = LibraryManager.GetItemById(child);
if (item != null)
{
if (item is IByReferenceItem)
{
return LibraryManager.GetOrAddByReferenceItem(item);
}
item.Parent = this;
}
return item;
}
private BaseItem RetrieveChild(BaseItem child) private BaseItem RetrieveChild(BaseItem child)
{ {
if (child.Id == Guid.Empty) if (child.Id == Guid.Empty)
@ -786,18 +799,31 @@ namespace MediaBrowser.Controller.Entities
{ {
var user = query.User; var user = query.User;
var items = query.Recursive Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
? GetRecursiveChildren(user)
: GetChildren(user, true);
var result = SortAndFilter(items, query); IEnumerable<BaseItem> items;
if (query.User == null)
{
items = query.Recursive
? GetRecursiveChildren(filter)
: Children.Where(filter);
}
else
{
items = query.Recursive
? GetRecursiveChildren(user, filter)
: GetChildren(user, true).Where(filter);
}
var result = PostFilterAndSort(items, query);
return Task.FromResult(result); return Task.FromResult(result);
} }
protected QueryResult<BaseItem> SortAndFilter(IEnumerable<BaseItem> items, InternalItemsQuery query) protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)
{ {
return UserViewBuilder.SortAndFilter(items, this, null, query, LibraryManager, UserDataManager); return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
} }
/// <summary> /// <summary>
@ -822,11 +848,11 @@ namespace MediaBrowser.Controller.Entities
//the true root should return our users root folder children //the true root should return our users root folder children
if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, includeLinkedChildren); if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, includeLinkedChildren);
var list = new List<BaseItem>(); var result = new Dictionary<Guid, BaseItem>();
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, includeHidden, false); AddChildren(user, includeLinkedChildren, result, includeHidden, false, null);
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; return result.Values;
} }
protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
@ -839,31 +865,30 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <param name="list">The list.</param> /// <param name="result">The result.</param>
/// <param name="includeHidden">if set to <c>true</c> [include hidden].</param> /// <param name="includeHidden">if set to <c>true</c> [include hidden].</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param>
/// <param name="filter">The filter.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool includeHidden, bool recursive) private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool includeHidden, bool recursive, Func<BaseItem, bool> filter)
{ {
var hasLinkedChildren = false;
foreach (var child in GetEligibleChildrenForRecursiveChildren(user)) foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
{ {
if (child.IsVisible(user)) if (child.IsVisible(user))
{ {
if (includeHidden || !child.IsHiddenFromUser(user)) if (includeHidden || !child.IsHiddenFromUser(user))
{ {
list.Add(child); if (filter == null || filter(child))
{
result[child.Id] = child;
}
} }
if (recursive && child.IsFolder) if (recursive && child.IsFolder)
{ {
var folder = (Folder)child; var folder = (Folder)child;
if (folder.AddChildrenToList(user, includeLinkedChildren, list, includeHidden, true)) folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter);
{
hasLinkedChildren = true;
}
} }
} }
} }
@ -874,14 +899,13 @@ namespace MediaBrowser.Controller.Entities
{ {
if (child.IsVisible(user)) if (child.IsVisible(user))
{ {
hasLinkedChildren = true; if (filter == null || filter(child))
{
list.Add(child); result[child.Id] = child;
}
} }
} }
} }
return hasLinkedChildren;
} }
/// <summary> /// <summary>
@ -891,18 +915,23 @@ namespace MediaBrowser.Controller.Entities
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
/// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentNullException"></exception>
public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true) public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
{
return GetRecursiveChildren(user, i => true);
}
public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter)
{ {
if (user == null) if (user == null)
{ {
throw new ArgumentNullException("user"); throw new ArgumentNullException("user");
} }
var list = new List<BaseItem>(); var result = new Dictionary<Guid, BaseItem>();
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, true); AddChildren(user, true, result, false, true, filter);
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; return result.Values;
} }
/// <summary> /// <summary>
@ -910,10 +939,15 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <returns>IList{BaseItem}.</returns> /// <returns>IList{BaseItem}.</returns>
public IList<BaseItem> GetRecursiveChildren() public IList<BaseItem> GetRecursiveChildren()
{
return GetRecursiveChildren(i => true);
}
public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
{ {
var list = new List<BaseItem>(); var list = new List<BaseItem>();
AddChildrenToList(list, true, null); AddChildrenToList(list, true, filter);
return list; return list;
} }
@ -1022,6 +1056,15 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i.Item2 != null); .Where(i => i.Item2 != null);
} }
[IgnoreDataMember]
protected override bool SupportsOwnedItems
{
get
{
return base.SupportsOwnedItems || SupportsShortcutChildren;
}
}
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken) protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{ {
var changesFound = false; var changesFound = false;
@ -1126,8 +1169,7 @@ namespace MediaBrowser.Controller.Entities
bool resetPosition) bool resetPosition)
{ {
// Sweep through recursively and update status // Sweep through recursively and update status
var tasks = GetRecursiveChildren(user, true) var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Select(c => c.MarkPlayed(user, datePlayed, resetPosition)); .Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
@ -1141,8 +1183,7 @@ namespace MediaBrowser.Controller.Entities
public override async Task MarkUnplayed(User user) public override async Task MarkUnplayed(User user)
{ {
// Sweep through recursively and update status // Sweep through recursively and update status
var tasks = GetRecursiveChildren(user, true) var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Select(c => c.MarkUnplayed(user)); .Select(c => c.MarkUnplayed(user));
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
@ -1171,15 +1212,15 @@ namespace MediaBrowser.Controller.Entities
return this; return this;
} }
return RecursiveChildren.FirstOrDefault(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) || return GetRecursiveChildren(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) ||
(!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) || (!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) ||
i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)); i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
.FirstOrDefault();
} }
public override bool IsPlayed(User user) public override bool IsPlayed(User user)
{ {
return GetRecursiveChildren(user) return GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.All(i => i.IsPlayed(user)); .All(i => i.IsPlayed(user));
} }
@ -1206,8 +1247,7 @@ namespace MediaBrowser.Controller.Entities
} }
else else
{ {
children = folder.GetRecursiveChildren(user) children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual);
.Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual);
} }
// Loop through each recursive child // Loop through each recursive child

View File

@ -1,10 +1,10 @@
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -38,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> LocalTrailerIds { get; set; } public List<Guid> LocalTrailerIds { get; set; }
public List<Guid> RemoteTrailerIds { get; set; } public List<Guid> RemoteTrailerIds { get; set; }
public override bool CanDownload()
{
var locationType = LocationType;
return locationType != LocationType.Remote &&
locationType != LocationType.Virtual;
}
/// <summary> /// <summary>
/// Gets or sets the tags. /// Gets or sets the tags.
/// </summary> /// </summary>
@ -88,7 +95,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public List<string> MultiPartGameFiles { get; set; } public List<string> MultiPartGameFiles { get; set; }
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
var id = this.GetProviderId(MetadataProviders.Gamesdb); var id = this.GetProviderId(MetadataProviders.Gamesdb);
@ -96,7 +103,7 @@ namespace MediaBrowser.Controller.Entities
{ {
return "Game-Gamesdb-" + id; return "Game-Gamesdb-" + id;
} }
return base.GetUserDataKey(); return base.CreateUserDataKey();
} }
public override IEnumerable<string> GetDeletePaths() public override IEnumerable<string> GetDeletePaths()

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -10,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
return "GameGenre-" + Name; return "GameGenre-" + Name;
} }
@ -20,6 +21,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself /// If the item is a folder, it returns the folder itself
/// </summary> /// </summary>
/// <value>The containing folder path.</value> /// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath public override string ContainingFolderPath
{ {
get get
@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets a value indicating whether this instance is owned item. /// Gets a value indicating whether this instance is owned item.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem public override bool IsOwnedItem
{ {
get get
@ -40,9 +43,19 @@ namespace MediaBrowser.Controller.Entities
} }
} }
public override bool CanDelete()
{
return false;
}
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems) public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{ {
return inputItems.Where(i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase)); return inputItems.Where(GetItemFilter());
}
public Func<BaseItem, bool> GetItemFilter()
{
return i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
} }
} }
} }

View File

@ -35,13 +35,13 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
if (!string.IsNullOrEmpty(GameSystemName)) if (!string.IsNullOrEmpty(GameSystemName))
{ {
return "GameSystem-" + GameSystemName; return "GameSystem-" + GameSystemName;
} }
return base.GetUserDataKey(); return base.CreateUserDataKey();
} }
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities.Audio; using System.Runtime.Serialization;
using MediaBrowser.Controller.Entities.Audio;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public override string GetUserDataKey() protected override string CreateUserDataKey()
{ {
return "Genre-" + Name; return "Genre-" + Name;
} }
@ -24,6 +25,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself /// If the item is a folder, it returns the folder itself
/// </summary> /// </summary>
/// <value>The containing folder path.</value> /// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath public override string ContainingFolderPath
{ {
get get
@ -32,10 +34,16 @@ namespace MediaBrowser.Controller.Entities
} }
} }
public override bool CanDelete()
{
return false;
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is owned item. /// Gets a value indicating whether this instance is owned item.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem public override bool IsOwnedItem
{ {
get get
@ -46,7 +54,12 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems) public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{ {
return inputItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase)); return inputItems.Where(GetItemFilter());
}
public Func<BaseItem, bool> GetItemFilter()
{
return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
} }
} }
} }

View File

@ -54,5 +54,10 @@ namespace MediaBrowser.Controller.Entities
/// Gets the item identities. /// Gets the item identities.
/// </summary> /// </summary>
List<IItemIdentity> Identities { get; set; } List<IItemIdentity> Identities { get; set; }
/// <summary>
/// Afters the metadata refresh.
/// </summary>
void AfterMetadataRefresh();
} }
} }

Some files were not shown because too many files have changed in this diff Show More