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.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
@ -39,6 +39,7 @@ namespace MediaBrowser.Api
private readonly IServerConfigurationManager _config;
private readonly ISessionManager _sessionManager;
private readonly IFileSystem _fileSystem;
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
@ -48,11 +49,12 @@ namespace MediaBrowser.Api
/// <param name="logger">The logger.</param>
/// <param name="sessionManager">The session manager.</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;
_sessionManager = sessionManager;
_config = config;
_fileSystem = fileSystem;
Instance = this;
}
@ -86,12 +88,12 @@ namespace MediaBrowser.Api
/// </summary>
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)
.ToList())
{
File.Delete(file);
_fileSystem.DeleteFile(file);
}
}
@ -462,7 +464,7 @@ namespace MediaBrowser.Api
/// <param name="outputFilePath">The output file path.</param>
private void DeleteProgressivePartialStreamFiles(string outputFilePath)
{
File.Delete(outputFilePath);
_fileSystem.DeleteFile(outputFilePath);
}
/// <summary>
@ -479,13 +481,13 @@ namespace MediaBrowser.Api
.ToList();
Exception e = null;
foreach (var file in filesToDelete)
{
try
{
Logger.Info("Deleting HLS file {0}", file);
File.Delete(file);
_fileSystem.DeleteFile(file);
}
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.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
@ -21,7 +24,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The logger.</value>
public ILogger Logger { get; set; }
/// <summary>
/// Gets or sets the HTTP result factory.
/// </summary>
@ -35,6 +38,7 @@ namespace MediaBrowser.Api
public IRequest Request { get; set; }
public ISessionContext SessionContext { get; set; }
public IAuthorizationContext AuthorizationContext { get; set; }
public string GetHeader(string name)
{
@ -109,6 +113,37 @@ namespace MediaBrowser.Api
private readonly char[] _dashReplaceChars = { '?', '/', '&' };
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)
{
return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager));
@ -139,11 +174,11 @@ namespace MediaBrowser.Api
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))
{
var folder = (Folder) libraryManager.GetItemById(new Guid(parentId));
var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
if (userId.HasValue)
{
@ -154,10 +189,13 @@ namespace MediaBrowser.Api
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)
{
@ -168,10 +206,16 @@ namespace MediaBrowser.Api
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>
@ -187,8 +231,9 @@ namespace MediaBrowser.Api
return name;
}
return libraryManager.RootFolder.RecursiveChildren
.OfType<Audio>()
return libraryManager.RootFolder
.GetRecursiveChildren(i => i is IHasArtist)
.Cast<IHasArtist>()
.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i =>
@ -229,8 +274,8 @@ namespace MediaBrowser.Api
return name;
}
return libraryManager.RootFolder.GetRecursiveChildren()
.OfType<Game>()
return libraryManager.RootFolder
.GetRecursiveChildren(i => i is Game)
.SelectMany(i => i.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i =>
@ -252,7 +297,8 @@ namespace MediaBrowser.Api
return name;
}
return libraryManager.RootFolder.GetRecursiveChildren()
return libraryManager.RootFolder
.GetRecursiveChildren()
.SelectMany(i => i.Studios)
.Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i =>
@ -274,7 +320,8 @@ namespace MediaBrowser.Api
return name;
}
return libraryManager.RootFolder.GetRecursiveChildren()
return libraryManager.RootFolder
.GetRecursiveChildren()
.SelectMany(i => i.People)
.Select(i => i.Name)
.Distinct(StringComparer.OrdinalIgnoreCase)
@ -287,6 +334,20 @@ namespace MediaBrowser.Api
}) ?? 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>
/// Gets the name of the item by.
/// </summary>
@ -294,7 +355,6 @@ namespace MediaBrowser.Api
/// <param name="type">The type.</param>
/// <param name="libraryManager">The library manager.</param>
/// <returns>Task{BaseItem}.</returns>
/// <exception cref="System.ArgumentException"></exception>
protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager)
{
BaseItem item;

View file

@ -8,7 +8,12 @@ namespace MediaBrowser.Api
public class GetBrandingOptions : IReturn<BrandingOptions>
{
}
[Route("/Branding/Css", "GET", Summary = "Gets custom css")]
public class GetBrandingCss
{
}
public class BrandingService : BaseApiService
{
private readonly IConfigurationManager _config;
@ -24,5 +29,12 @@ namespace MediaBrowser.Api
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)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var key = pathInfo.GetArgumentValue<string>(2);
var key = GetPathValue(2);
var configurationType = _configurationManager.GetConfigurationType(key);
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")]
public string SendingUserId { get; set; }
[ApiMember(Name = "ExcludeLibraries", Description = "ExcludeLibraries", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string ExcludedLibraries { get; set; }
[ApiMember(Name = "EnabledLibraries", Description = "EnabledLibraries", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string EnabledLibraries { get; set; }
[ApiMember(Name = "ExcludedChannels", Description = "ExcludedChannels", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string ExcludedChannels { get; set; }
[ApiMember(Name = "EnabledChannels", Description = "EnabledChannels", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string EnabledChannels { get; set; }
[ApiMember(Name = "EnableLiveTv", Description = "EnableLiveTv", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public bool EnableLiveTv { get; set; }
@ -91,12 +91,12 @@ namespace MediaBrowser.Api
public object Post(CreateConnectInvite request)
{
var excludeLibraries = (request.ExcludedLibraries ?? string.Empty)
var enabledLibraries = (request.EnabledLibraries ?? string.Empty)
.Split(',')
.Where(i => !string.IsNullOrWhiteSpace(i))
.ToArray();
var excludedChannels = (request.ExcludedChannels ?? string.Empty)
var enabledChannels = (request.EnabledChannels ?? string.Empty)
.Split(',')
.Where(i => !string.IsNullOrWhiteSpace(i))
.ToArray();
@ -105,8 +105,8 @@ namespace MediaBrowser.Api
{
ConnectUserName = request.ConnectUsername,
SendingUserId = request.SendingUserId,
ExcludedLibraries = excludeLibraries,
ExcludedChannels = excludedChannels,
EnabledLibraries = enabledLibraries,
EnabledChannels = enabledChannels,
EnableLiveTv = request.EnableLiveTv
});
}

View file

@ -1,6 +1,5 @@
using MediaBrowser.Controller.Dlna;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
@ -19,19 +18,31 @@ namespace MediaBrowser.Api.Dlna
public string UuId { get; set; }
}
[Route("/Dlna/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.xml", "GET", Summary = "Gets dlna content directory xml")]
[Route("/Dlna/{UuId}/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
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/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")]
[Route("/Dlna/{UuId}/connectionmanager/connectionmanager.xml", "GET", Summary = "Gets dlna connection manager xml")]
[Route("/Dlna/{UuId}/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")]
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
{
[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; }
}
[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
{
[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; }
}
[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
{
[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; }
}
[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
{
[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; }
}
[Route("/Dlna/{UuId}/icons/{Filename}", "GET", Summary = "Gets a server icon")]
[Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
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")]
public string Filename { get; set; }
}
@ -75,17 +106,21 @@ namespace MediaBrowser.Api.Dlna
private readonly IDlnaManager _dlnaManager;
private readonly IContentDirectory _contentDirectory;
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;
_contentDirectory = contentDirectory;
_connectionManager = connectionManager;
_mediaReceiverRegistrar = mediaReceiverRegistrar;
}
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");
}
@ -97,6 +132,13 @@ namespace MediaBrowser.Api.Dlna
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)
{
var xml = _connectionManager.GetServiceXml(GetRequestHeaders());
@ -104,6 +146,13 @@ namespace MediaBrowser.Api.Dlna
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)
{
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)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(2);
var id = GetPathValue(2);
using (var reader = new StreamReader(requestStream))
{
@ -172,6 +220,11 @@ namespace MediaBrowser.Api.Dlna
return ProcessEventRequest(_connectionManager);
}
public object Any(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(_mediaReceiverRegistrar);
}
private object ProcessEventRequest(IEventManager eventManager)
{
var subscriptionId = GetHeader("SID");

View file

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

View file

@ -1,8 +1,4 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Linq;

namespace MediaBrowser.Api
{
public interface IHasDtoOptions : IHasItemFields
@ -13,27 +9,4 @@ namespace MediaBrowser.Api
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.Entities;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
@ -396,8 +395,7 @@ namespace MediaBrowser.Api.Images
public object Get(GetItemByNameImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var type = pathInfo.GetArgumentValue<string>(0);
var type = GetPathValue(0);
var item = GetItemByName(request.Name, type, _libraryManager);
@ -406,8 +404,7 @@ namespace MediaBrowser.Api.Images
public object Head(GetItemByNameImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var type = pathInfo.GetArgumentValue<string>(0);
var type = GetPathValue(0);
var item = GetItemByName(request.Name, type, _libraryManager);
@ -420,10 +417,9 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
public void Post(PostUserImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(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);
@ -438,10 +434,9 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
public void Post(PostItemImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(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);

View file

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

View file

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

View file

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

View file

@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Library
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.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@ -226,6 +224,18 @@ namespace MediaBrowser.Api.Library
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>
/// Class LibraryService
/// </summary>
@ -272,8 +282,8 @@ namespace MediaBrowser.Api.Library
items = items.Where(i => i.IsHidden == val).ToList();
}
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
var result = new ItemsResult
{
TotalRecordCount = items.Count,
@ -289,6 +299,28 @@ namespace MediaBrowser.Api.Library
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)
{
var item = _libraryManager.GetItemById(request.Id);
@ -344,10 +376,10 @@ namespace MediaBrowser.Api.Library
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
BaseItem parent = item.Parent;
while (parent != null)
{
if (user != null)
@ -392,52 +424,43 @@ namespace MediaBrowser.Api.Library
/// <returns>System.Object.</returns>
public object Get(GetItemCounts request)
{
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
.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 filteredItems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i.LocationType != LocationType.Virtual && FilterItem(i, request, request.UserId));
var counts = new ItemCounts
{
AlbumCount = albums.Count,
EpisodeCount = episodes.Count,
GameCount = games.Count,
GameSystemCount = filteredItems.OfType<GameSystem>().Count(),
MovieCount = movies.Count,
SeriesCount = series.Count,
SongCount = songs.Count,
MusicVideoCount = musicVideos.Count,
BoxSetCount = boxsets.Count,
BookCount = books.Count,
AlbumCount = filteredItems.Count(i => i is MusicAlbum),
EpisodeCount = filteredItems.Count(i => i is Episode),
GameCount = filteredItems.Count(i => i is Game),
GameSystemCount = filteredItems.Count(i => i is GameSystem),
MovieCount = filteredItems.Count(i => i is Movie),
SeriesCount = filteredItems.Count(i => i is Series),
SongCount = filteredItems.Count(i => i is Audio),
MusicVideoCount = filteredItems.Count(i => i is MusicVideo),
BoxSetCount = filteredItems.Count(i => i is BoxSet),
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);
}
private IEnumerable<T> FilterItems<T>(IEnumerable<T> items, GetItemCounts request, Guid userId)
where T : BaseItem
private bool FilterItem(BaseItem item, GetItemCounts request, Guid? userId)
{
if (request.IsFavorite.HasValue)
if (userId.HasValue)
{
var val = request.IsFavorite.Value;
if (request.IsFavorite.HasValue)
{
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>
@ -467,23 +490,9 @@ namespace MediaBrowser.Api.Library
var auth = _authContext.GetAuthorizationInfo(Request);
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();
}
}
else
{
if (!user.Policy.EnableContentDeletion)
{
throw new UnauthorizedAccessException();
}
throw new UnauthorizedAccessException();
}
var task = _libraryManager.DeleteItem(item);
@ -544,7 +553,7 @@ namespace MediaBrowser.Api.Library
ThemeSongsResult = themeSongs,
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)
.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)
.OrderBy(i => i.SortName)
@ -711,13 +720,24 @@ namespace MediaBrowser.Api.Library
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(',');
items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
}
if (includeTypes.Length > 0)
{
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
.ToLookup(i => i.ProductionYear ?? -1)
@ -732,23 +752,22 @@ namespace MediaBrowser.Api.Library
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 item = string.IsNullOrEmpty(id)
? (userId.HasValue
? user.RootFolder
: (Folder)_libraryManager.RootFolder)
: _libraryManager.RootFolder)
: _libraryManager.GetItemById(id);
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
var dtos = GetSoundtrackSongIds(item, inheritFromParent)
.Select(_libraryManager.GetItemById)
.OfType<MusicAlbum>()
.SelectMany(i => i.RecursiveChildren)
.OfType<Audio>()
.SelectMany(i => i.GetRecursiveChildren(a => a is Audio))
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));

View file

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

View file

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

View file

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

View file

@ -121,8 +121,7 @@ namespace MediaBrowser.Api.Movies
{
var user = _userManager.GetUserById(request.UserId.Value);
var movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId)
.Where(i => i is Movie);
IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
@ -157,7 +156,7 @@ namespace MediaBrowser.Api.Movies
.DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase)
.ToList();
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
dtoOptions.Fields = request.GetItemFields().ToList();
@ -174,13 +173,11 @@ namespace MediaBrowser.Api.Movies
(request.UserId.HasValue ? user.RootFolder :
_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
? _libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id)
: user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id);
inputItems = inputItems.Where(includeInSearch);
? _libraryManager.RootFolder.GetRecursiveChildren(filter)
: user.RootFolder.GetRecursiveChildren(user, filter);
var list = inputItems.ToList();
@ -225,10 +222,12 @@ namespace MediaBrowser.Api.Movies
{
returnItems = returnItems.Take(request.Limit.Value);
}
var dtoOptions = GetDtoOptions(request);
var result = new ItemsResult
{
Items = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(),
Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
TotalRecordCount = items.Count
};
@ -351,7 +350,7 @@ namespace MediaBrowser.Api.Movies
BaselineItemName = director,
CategoryId = director.GetMD5().ToString("N"),
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,
CategoryId = name.GetMD5().ToString("N"),
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,
CategoryId = item.Id.ToString("N"),
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>
public object Get(GetSimilarTrailers request)
{
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager,
var dtoOptions = GetDtoOptions(request);
var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
@ -119,9 +121,9 @@ namespace MediaBrowser.Api.Movies
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
{

View file

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

View file

@ -73,44 +73,44 @@ namespace MediaBrowser.Api.Music
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 items = _musicManager.GetInstantMixFromArtist(item.Name, user);
var items = _musicManager.GetInstantMixFromItem(item, user);
return GetResult(items, user, 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 items = _musicManager.GetInstantMixFromGenres(new[] { item.Name }, user);
var items = _musicManager.GetInstantMixFromItem(item, user);
return GetResult(items, user, 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 items = _musicManager.GetInstantMixFromSong(item, user);
var items = _musicManager.GetInstantMixFromItem(item, user);
return GetResult(items, user, 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 items = _musicManager.GetInstantMixFromAlbum(album, user);
var items = _musicManager.GetInstantMixFromItem(album, user);
return GetResult(items, user, request);
}
@ -121,7 +121,7 @@ namespace MediaBrowser.Api.Music
var user = _userManager.GetUserById(request.UserId.Value);
var items = _musicManager.GetInstantMixFromPlaylist(playlist, user);
var items = _musicManager.GetInstantMixFromItem(playlist, user);
return GetResult(items, user, request);
}
@ -146,8 +146,6 @@ namespace MediaBrowser.Api.Music
private object GetResult(IEnumerable<Audio> items, User user, BaseGetSimilarItems request)
{
var fields = request.GetItemFields().ToList();
var list = items.ToList();
var result = new ItemsResult
@ -155,10 +153,9 @@ namespace MediaBrowser.Api.Music
TotalRecordCount = list.Count
};
var dtos = list.Take(request.Limit ?? list.Count)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user));
var dtoOptions = GetDtoOptions(request);
result.Items = dtos.ToArray();
result.Items = _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user).ToArray();
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.IO;
using MediaBrowser.Controller.Channels;
@ -66,14 +67,16 @@ namespace MediaBrowser.Api.Playback
protected ILiveTvManager LiveTvManager { get; private set; }
protected IDlnaManager DlnaManager { get; private set; }
protected IDeviceManager DeviceManager { get; private set; }
protected IChannelManager ChannelManager { get; private set; }
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </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;
ChannelManager = channelManager;
DlnaManager = dlnaManager;
@ -119,8 +122,8 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
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 data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false);
@ -320,13 +323,13 @@ namespace MediaBrowser.Api.Playback
switch (qualitySetting)
{
case EncodingQuality.HighSpeed:
param += " -crf 23";
param += " -subq 0 -crf 23";
break;
case EncodingQuality.HighQuality:
param += " -crf 20";
param += " -subq 3 -crf 20";
break;
case EncodingQuality.MaxQuality:
param += " -crf 18";
param += " -subq 6 -crf 18";
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
else if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{
@ -426,10 +464,50 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Level))
{
param += " -level " + 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;
}
}
return param;
return "-pix_fmt yuv420p " + param;
}
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;
if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
@ -606,7 +689,7 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
{
var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language);
var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath);
if (!string.IsNullOrEmpty(charenc))
{
@ -834,7 +917,7 @@ namespace MediaBrowser.Api.Playback
{
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;
@ -1183,6 +1266,22 @@ namespace MediaBrowser.Api.Playback
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
if (hasFixedResolution)
{
@ -1991,9 +2090,26 @@ namespace MediaBrowser.Api.Playback
headers[key] = Request.Headers[key];
}
state.DeviceProfile = string.IsNullOrWhiteSpace(state.Request.DeviceProfileId) ?
DlnaManager.GetProfile(headers) :
DlnaManager.GetProfile(state.Request.DeviceProfileId);
if (!string.IsNullOrWhiteSpace(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;

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.Net;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Api.Playback.Hls
{
@ -23,8 +23,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
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)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, 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)
{
}

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.Net;
using MediaBrowser.Controller.Channels;
@ -62,38 +63,31 @@ namespace MediaBrowser.Api.Playback.Hls
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, INetworkManager networkManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
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)
{
NetworkManager = networkManager;
}
public object Get(GetMasterHlsVideoStream request)
{
var result = GetAsync(request, "GET").Result;
protected INetworkManager NetworkManager { get; private set; }
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 result;
return GetAsync(request, "HEAD");
}
public object Get(GetMainHlsVideoStream request)
public Task<object> Get(GetMainHlsVideoStream request)
{
var result = GetPlaylistAsync(request, "main").Result;
return result;
return GetPlaylistAsync(request, "main");
}
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)
@ -210,10 +204,10 @@ namespace MediaBrowser.Api.Playback.Hls
{
return;
}
try
{
File.Delete(file.FullName);
FileSystem.DeleteFile(file.FullName);
}
catch (IOException ex)
{

View file

@ -1,10 +1,6 @@
using MediaBrowser.Controller;
using MediaBrowser.Model.Dlna;
using ServiceStack;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback.Hls
{
@ -66,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
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);
}
@ -85,7 +81,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
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);
}

View file

@ -2,6 +2,7 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
@ -50,14 +51,13 @@ namespace MediaBrowser.Api.Playback.Hls
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, INetworkManager networkManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
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)
{
NetworkManager = networkManager;
}
protected INetworkManager NetworkManager { get; private set; }
public object Get(GetMasterManifest request)
{
var result = GetAsync(request, "GET").Result;
@ -489,7 +489,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
try
{
File.Delete(file.FullName);
FileSystem.DeleteFile(file.FullName);
}
catch (IOException ex)
{
@ -625,7 +625,7 @@ namespace MediaBrowser.Api.Playback.Hls
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/
var threads = GetNumberOfThreads(state, false);

View file

@ -1,11 +1,11 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.IO;
using ServiceStack;
using System;
@ -57,8 +57,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
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)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, 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)
{
}
@ -71,7 +70,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
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);
}

View file

@ -2,6 +2,7 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@ -31,7 +32,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
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.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@ -15,6 +15,7 @@ using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -28,8 +29,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected readonly IImageProcessor ImageProcessor;
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)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
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)
{
ImageProcessor = imageProcessor;
HttpClient = httpClient;

View file

@ -2,6 +2,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@ -62,7 +63,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
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))
{
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
format = " -f mp4 -movflags frag_keyframe+empty_moov";
}

View file

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

View file

@ -151,9 +151,10 @@ namespace MediaBrowser.Api
{
items = items.Take(request.Limit.Value).ToArray();
}
var dtos = items
.Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user))
var dtoOptions = GetDtoOptions(request);
var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user)
.ToArray();
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.Security;
using MediaBrowser.Common.Updates;
@ -8,12 +7,12 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
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
// 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(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(1));
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")]
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>
@ -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
.Select(ScheduledTaskHelpers.GetTaskInfo)
.ToList();
@ -202,8 +224,7 @@ namespace MediaBrowser.Api.ScheduledTasks
{
// 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
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(1);
var id = GetPathValue(1);
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.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode);
result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count;
}
var series = item as Series;
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;
if (album != null)
{
var songs = album.GetRecursiveChildren().OfType<Audio>().ToList();
result.SongCount = album.Tracks.Count();
result.SongCount = songs.Count;
result.Artists = songs.SelectMany(i => i.AllArtists)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
result.AlbumArtist = songs.SelectMany(i => i.AlbumArtists).FirstOrDefault(i => !string.IsNullOrEmpty(i));
result.Artists = album.Artists.ToArray();
result.AlbumArtist = album.AlbumArtists.FirstOrDefault();
}
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.Session;
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")]
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")]
public bool SupportsUniqueIdentifier { get; set; }
[ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsPersistentIdentifier { get; set; }
public PostCapabilities()
{
SupportsPersistentIdentifier = true;
}
}
[Route("/Sessions/Capabilities/Full", "POST", Summary = "Updates capabilities for a device")]
@ -556,7 +561,7 @@ namespace MediaBrowser.Api.Session
SupportsSync = request.SupportsSync,
SupportsUniqueIdentifier = request.SupportsUniqueIdentifier
SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
});
}

View file

@ -57,6 +57,7 @@ namespace MediaBrowser.Api
/// <summary>
/// Gets the similar items.
/// </summary>
/// <param name="dtoOptions">The dto options.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="itemRepository">The item repository.</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="getSimilarityScore">The get similarity score.</param>
/// <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;
@ -75,13 +76,13 @@ namespace MediaBrowser.Api
(request.UserId.HasValue ? user.RootFolder :
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
? libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id)
: user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id);
? libraryManager.RootFolder.GetRecursiveChildren(filter)
: user.RootFolder.GetRecursiveChildren(user, filter);
var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore)
var items = GetSimilaritems(item, inputItems, getSimilarityScore)
.ToList();
IEnumerable<BaseItem> returnItems = items;
@ -93,7 +94,7 @@ namespace MediaBrowser.Api
var result = new ItemsResult
{
Items = returnItems.Select(i => dtoService.GetBaseItemDto(i, fields, user)).ToArray(),
Items = dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
TotalRecordCount = items.Count
};
@ -164,7 +165,7 @@ namespace MediaBrowser.Api
// Find common keywords
points += GetKeywords(item1).Where(i => GetKeywords(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
// Find common studios
points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3);

View file

@ -62,6 +62,9 @@ namespace MediaBrowser.Api
{
_config.Configuration.IsStartupWizardCompleted = true;
_config.Configuration.EnableLocalizedGuids = true;
_config.Configuration.MergeMetadataAndImagesByName = true;
_config.Configuration.EnableStandaloneMetadata = true;
_config.Configuration.EnableLibraryMetadataSubFolder = true;
_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.Net;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
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.")]
public class GetSyncJobs : SyncJobQuery, IReturn<QueryResult<SyncJob>>
{
@ -85,6 +112,16 @@ namespace MediaBrowser.Api.Sync
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.")]
public class ReportOfflineActions : List<UserAction>, IReturnVoid
{
@ -169,11 +206,14 @@ namespace MediaBrowser.Api.Sync
{
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.");
}
var task = _syncManager.ReportSyncJobItemTransferBeginning(request.Id);
Task.WaitAll(task);
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)
.Where(i => i != null)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions))
.Where(i => i != null);
var dtos = _dtoService.GetBaseItemDtos(items, dtoOptions)
.ToList();
result.Options = SyncHelper.GetSyncOptions(dtos);
@ -243,5 +284,52 @@ namespace MediaBrowser.Api.Sync
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")]
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")]
@ -238,7 +252,9 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetSimilarShows request)
{
var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager,
var dtoOptions = GetDtoOptions(request);
var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo,
_libraryManager,
_userDataManager,
@ -254,10 +270,10 @@ namespace MediaBrowser.Api
{
var user = _userManager.GetUserById(request.UserId);
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId)
.OfType<Episode>();
var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is 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>()
.ToList();
@ -270,9 +286,9 @@ namespace MediaBrowser.Api
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
{
@ -301,9 +317,9 @@ namespace MediaBrowser.Api
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
{
@ -365,9 +381,9 @@ namespace MediaBrowser.Api
.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();
return new ItemsResult
@ -411,7 +427,18 @@ namespace MediaBrowser.Api
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;
@ -424,14 +451,14 @@ namespace MediaBrowser.Api
}
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
@ -448,24 +475,27 @@ namespace MediaBrowser.Api
episodes = episodes.Where(i => i.IsVirtualUnaired == val);
}
IEnumerable<BaseItem> returnItems = episodes;
// This must be the last filter
if (!string.IsNullOrEmpty(request.AdjacentTo))
{
episodes = UserViewBuilder.FilterForAdjacency(episodes, request.AdjacentTo)
.Cast<Episode>();
returnItems = UserViewBuilder.FilterForAdjacency(returnItems, request.AdjacentTo);
}
var fields = request.GetItemFields().ToList();
returnItems = _libraryManager.ReplaceVideosWithPrimaryVersions(returnItems);
episodes = _libraryManager.ReplaceVideosWithPrimaryVersions(episodes).Cast<Episode>();
var pagedItems = ApplyPaging(returnItems, request.StartIndex, request.Limit);
var dtoOptions = GetDtoOptions(request);
var returnItems = episodes.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user)
.ToArray();
return new ItemsResult
{
TotalRecordCount = returnItems.Length,
Items = returnItems
TotalRecordCount = dtos.Length,
Items = dtos
};
}
}

View file

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

View file

@ -56,46 +56,52 @@ namespace MediaBrowser.Api.UserLibrary
protected ItemsResult GetResult(GetItemsByName request)
{
User user = null;
BaseItem item;
BaseItem parentItem;
List<BaseItem> libraryItems;
if (request.UserId.HasValue)
{
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();
}
else
{
item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList();
parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
libraryItems = LibraryManager.RootFolder.GetRecursiveChildren().ToList();
}
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)
{
items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user, true);
items = request.Recursive ?
folder.GetRecursiveChildren(user, filter) :
folder.GetChildren(user, true).Where(filter);
}
else
{
items = request.Recursive ? folder.GetRecursiveChildren() : folder.Children;
items = request.Recursive ?
folder.GetRecursiveChildren(filter) :
folder.Children.Where(filter);
}
}
else
{
items = new[] { item };
items = new[] { parentItem }.Where(filter);
}
items = FilterItems(request, items);
var extractedItems = GetAllItems(request, items);
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 dtoOptions = request.GetDtoOptions();
var dtoOptions = GetDtoOptions(request);
var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2));
@ -290,33 +296,41 @@ namespace MediaBrowser.Api.UserLibrary
/// Filters the items.
/// </summary>
/// <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>
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
if (!string.IsNullOrEmpty(request.ExcludeItemTypes))
if (excludeItemTypes.Length > 0)
{
var vals = request.ExcludeItemTypes.Split(',');
items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
if (excludeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
// Include item types
if (!string.IsNullOrEmpty(request.IncludeItemTypes))
if (includeItemTypes.Length > 0)
{
var vals = request.IncludeItemTypes.Split(',');
items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
if (!includeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
// Include MediaTypes
if (!string.IsNullOrEmpty(request.MediaTypes))
if (mediaTypes.Length > 0)
{
var vals = request.MediaTypes.Split(',');
items = items.Where(f => vals.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase));
if (!mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
return items;
return true;
}
/// <summary>

View file

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

View file

@ -74,7 +74,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetGenre(request.Name, LibraryManager);
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
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)]
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")]
public bool? IsMissing { get; set; }
@ -321,14 +319,14 @@ namespace MediaBrowser.Api.UserLibrary
var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
var isFiltered = result.Item2;
var dtoOptions = request.GetDtoOptions();
var dtoOptions = GetDtoOptions(request);
if (isFiltered)
{
return new ItemsResult
{
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 returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
var returnItems = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ToArray();
return new ItemsResult
{
@ -396,52 +394,29 @@ namespace MediaBrowser.Api.UserLibrary
else if (request.Recursive)
{
if (user == null)
{
items = ((Folder)item).RecursiveChildren;
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
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
{
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;
if (userRoot == null)
{
var userRoot = item as UserRootFolder;
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
if (userRoot == null)
{
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user));
return new Tuple<QueryResult<BaseItem>, bool>(result, true);
}
items = ((Folder)item).GetChildren(user, true);
return new Tuple<QueryResult<BaseItem>, bool>(result, 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();
items = ((Folder)item).GetChildren(user, true);
}
return new Tuple<QueryResult<BaseItem>, bool>(new QueryResult<BaseItem>
@ -464,7 +439,7 @@ namespace MediaBrowser.Api.UserLibrary
SortBy = request.GetOrderBy(),
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,
StartIndex = request.StartIndex,

View file

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

View file

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

View file

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

View file

@ -228,7 +228,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <value>The user id.</value>
[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")]
public int Limit { get; set; }
@ -259,7 +259,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
public GetLatestMedia()
{
Limit = 20;
@ -304,74 +304,17 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
// Avoid implicitly captured closure
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 list = _userViewManager.GetLatestItems(new LatestItemsQuery
{
var vals = request.IncludeItemTypes.Split(',');
libraryItems = libraryItems.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
}
GroupItems = request.GroupItems,
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;
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 options = GetDtoOptions(request);
var dtos = list.Select(i =>
{
@ -394,15 +337,6 @@ namespace MediaBrowser.Api.UserLibrary
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)
{
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 dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();
@ -447,14 +381,13 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the child tree
if (series != null)
{
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
// Avoid implicitly captured closure
var currentUser = user;
var dtos = series
.GetRecursiveChildren()
.Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
.GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
.OrderBy(i =>
{
if (i.PremiereDate.HasValue)
@ -479,7 +412,7 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the db
if (movie != null)
{
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
var dtos = movie.SpecialFeatureIds
.Select(_libraryManager.GetItemById)
@ -518,11 +451,10 @@ namespace MediaBrowser.Api.UserLibrary
trailerIds = hasTrailers.GetTrailerIds();
}
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
var dtos = trailerIds
.Select(_libraryManager.GetItemById)
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
return dtos.ToList();
@ -539,7 +471,7 @@ namespace MediaBrowser.Api.UserLibrary
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);
@ -557,7 +489,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = user.RootFolder;
var dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
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 dtoOptions = new DtoOptions();
var dtoOptions = GetDtoOptions(request);
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();

View file

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

View file

@ -11,7 +11,6 @@ using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Users;
using ServiceStack;
using ServiceStack.Text.Controller;
using System;
using System.Collections.Generic;
using System.Linq;
@ -56,6 +55,21 @@ namespace MediaBrowser.Api
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>
/// Class DeleteUser
/// </summary>
@ -148,6 +162,32 @@ namespace MediaBrowser.Api
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>
/// Class UpdateUser
/// </summary>
@ -294,7 +334,7 @@ namespace MediaBrowser.Api
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToList();
return ToOptimizedSerializedResultUsingCache(result);
return ToOptimizedResult(result);
}
/// <summary>
@ -313,7 +353,23 @@ namespace MediaBrowser.Api
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>
@ -410,6 +466,8 @@ namespace MediaBrowser.Api
public async Task PostAsync(UpdateUserPassword request)
{
AssertCanUpdateUser(request.Id);
var user = _userManager.GetUserById(request.Id);
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>
/// Posts the specified request.
/// </summary>
@ -449,14 +534,15 @@ namespace MediaBrowser.Api
{
// 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
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = GetPathValue(1);
AssertCanUpdateUser(id);
var dtoUser = request;
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.RenameUser(user, dtoUser.Name);
@ -500,11 +586,29 @@ namespace MediaBrowser.Api
public void Post(UpdateUserConfiguration request)
{
AssertCanUpdateUser(request.Id);
var task = _userManager.UpdateConfiguration(request.Id, request);
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)
{
var task = UpdateUserPolicy(request);

View file

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

View file

@ -475,7 +475,7 @@ namespace MediaBrowser.Common.Implementations
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager);
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);
ZipClient = new ZipClient();

View file

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

View file

@ -1,4 +1,4 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.Logging;
using System;
@ -270,8 +270,8 @@ namespace MediaBrowser.Common.Implementations.IO
File.Copy(temp1, file2, true);
File.Copy(temp2, file1, true);
File.Delete(temp1);
File.Delete(temp2);
DeleteFile(temp1);
DeleteFile(temp2);
}
/// <summary>
@ -409,5 +409,25 @@ namespace MediaBrowser.Common.Implementations.IO
//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>
</PropertyGroup>
<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>
<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 Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
</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>
<HintPath>..\packages\SimpleInjector.2.6.1\lib\net45\SimpleInjector.dll</HintPath>
<HintPath>..\packages\SimpleInjector.2.7.0\lib\net45\SimpleInjector.dll</HintPath>
</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>
<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 Include="System" />
<Reference Include="System.Core" />

View file

@ -18,11 +18,64 @@ namespace MediaBrowser.Common.Implementations.Networking
Logger = logger;
}
private volatile List<string> _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
/// <summary>
/// Gets the machine's local ip address
/// </summary>
/// <returns>IPAddress.</returns>
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()
.Where(i => !IPAddress.IsLoopback(i))
@ -53,6 +106,11 @@ namespace MediaBrowser.Common.Implementations.Networking
// Private address space:
// http://en.wikipedia.org/wiki/Private_network
if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase))
{
return Is172AddressPrivate(endpoint);
}
return
// 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("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("192.168", 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)
{
return IsInLocalNetworkInternal(endpoint, true);
@ -122,7 +192,7 @@ namespace MediaBrowser.Common.Implementations.Networking
return false;
}
public IEnumerable<IPAddress> GetIpAddresses(string hostName)
{
return Dns.GetHostAddresses(hostName);

View file

@ -108,13 +108,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// </summary>
private TaskResult _lastExecutionResult;
/// <summary>
/// The _last execution resultinitialized
/// </summary>
private bool _lastExecutionResultinitialized;
/// <summary>
/// The _last execution result sync lock
/// </summary>
private object _lastExecutionResultSyncLock = new object();
private readonly object _lastExecutionResultSyncLock = new object();
/// <summary>
/// Gets the last execution result.
/// </summary>
@ -123,38 +119,39 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
get
{
LazyInitializer.EnsureInitialized(ref _lastExecutionResult, ref _lastExecutionResultinitialized, ref _lastExecutionResultSyncLock, () =>
if (_lastExecutionResult == null)
{
var path = GetHistoryFilePath();
lock (_lastExecutionResultSyncLock)
{
if (_lastExecutionResult == null)
{
var path = GetHistoryFilePath();
try
{
return JsonSerializer.DeserializeFromFile<TaskResult>(path);
try
{
return JsonSerializer.DeserializeFromFile<TaskResult>(path);
}
catch (DirectoryNotFoundException)
{
// File doesn't exist. No biggie
}
catch (FileNotFoundException)
{
// File doesn't exist. No biggie
}
catch (Exception ex)
{
Logger.ErrorException("Error deserializing {0}", ex, path);
}
}
}
catch (DirectoryNotFoundException)
{
// File doesn't exist. No biggie
return null;
}
catch (FileNotFoundException)
{
// File doesn't exist. No biggie
return null;
}
catch (Exception ex)
{
Logger.ErrorException("Error deserializing {0}", ex, path);
return null;
}
});
}
return _lastExecutionResult;
}
private set
{
_lastExecutionResult = value;
_lastExecutionResultinitialized = value != null;
}
}
@ -227,13 +224,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// </summary>
private IEnumerable<ITaskTrigger> _triggers;
/// <summary>
/// The _triggers initialized
/// </summary>
private bool _triggersInitialized;
/// <summary>
/// The _triggers sync lock
/// </summary>
private object _triggersSyncLock = new object();
private readonly object _triggersSyncLock = new object();
/// <summary>
/// Gets the triggers that define when the task will run
/// </summary>
@ -243,7 +236,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
get
{
LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, LoadTriggers);
if (_triggers == null)
{
lock (_triggersSyncLock)
{
if (_triggers == null)
{
_triggers = LoadTriggers();
}
}
}
return _triggers;
}
@ -262,8 +264,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
_triggers = value.ToList();
_triggersInitialized = true;
ReloadTriggerEvents(false);
SaveTriggers(_triggers);
@ -335,12 +335,30 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
trigger.Start(false);
}
private Task _currentTask;
/// <summary>
/// Executes the task
/// </summary>
/// <returns>Task.</returns>
/// <exception cref="System.InvalidOperationException">Cannot execute a Task that is already running</exception>
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
if (CurrentCancellationTokenSource != null)
@ -544,6 +562,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
Id = Id
};
var hasKey = ScheduledTask as IHasKey;
if (hasKey != null)
{
result.Key = hasKey.Key;
}
if (ex != null)
{
result.ErrorMessage = ex.Message;
@ -579,14 +603,60 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
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
{
File.Delete(path);
_fileSystem.DeleteFile(path);
}
catch (IOException ex)
{

View file

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

View file

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

View file

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

View file

@ -26,36 +26,6 @@ namespace MediaBrowser.Common.Extensions
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)
{
return String.Concat(

View file

@ -133,5 +133,33 @@ namespace MediaBrowser.Common.IO
/// <param name="path">The path.</param>
/// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns>
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>
/// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns>
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.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@ -14,9 +15,19 @@ namespace MediaBrowser.Controller.Channels
public override bool IsVisible(User user)
{
if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
if (user.Policy.BlockedChannels != null)
{
return false;
if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
else
{
if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{
return false;
}
}
return base.IsVisible(user);
@ -50,7 +61,22 @@ namespace MediaBrowser.Controller.Channels
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.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Channels
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
}
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
return ExternalId;
}
@ -89,5 +89,10 @@ namespace MediaBrowser.Controller.Channels
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.Configuration;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@ -40,7 +39,7 @@ namespace MediaBrowser.Controller.Channels
return false;
}
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
return ExternalId;
}
@ -76,5 +75,10 @@ namespace MediaBrowser.Controller.Channels
{
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.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@ -28,8 +28,8 @@ namespace MediaBrowser.Controller.Channels
public string OriginalImageUrl { get; set; }
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
if (ContentType == ChannelMediaContentType.MovieExtra)
{
@ -119,5 +119,10 @@ namespace MediaBrowser.Controller.Channels
{
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>
/// <returns>Folder.</returns>
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.Session;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
@ -15,6 +14,10 @@ namespace MediaBrowser.Controller.Devices
/// Occurs when [device options updated].
/// </summary>
event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
/// <summary>
/// Occurs when [camera image uploaded].
/// </summary>
event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
/// <summary>
/// Registers the device.

View file

@ -62,8 +62,9 @@ namespace MediaBrowser.Controller.Dlna
/// </summary>
/// <param name="headers">The headers.</param>
/// <param name="serverUuId">The server uu identifier.</param>
/// <param name="serverAddress">The server address.</param>
/// <returns>System.String.</returns>
string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId);
string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId, string serverAddress);
/// <summary>
/// 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 int ImageTypeLimit { get; set; }
public bool EnableImages { get; set; }
public string DeviceId { get; set; }
public DtoOptions()
{

View file

@ -44,6 +44,17 @@ namespace MediaBrowser.Controller.Dto
/// <param name="owner">The owner.</param>
/// <returns>BaseItemDto.</returns>
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>
/// Gets the chapter information dto.

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>
/// The _virtual children
/// </summary>
@ -66,7 +71,7 @@ namespace MediaBrowser.Controller.Entities
{
var path = ContainingFolderPath;
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService)
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
{
FileInfo = new DirectoryInfo(path),
Path = path,

View file

@ -4,11 +4,11 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@ -80,6 +80,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
[IgnoreDataMember]
protected override bool SupportsOwnedItems
{
get
{
return false;
}
}
[IgnoreDataMember]
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>
/// Gets or sets the artist.
/// </summary>
@ -169,7 +185,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
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)
@ -223,7 +239,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
Id = i.Id.ToString("N"),
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,
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks,

View file

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

View file

@ -1,14 +1,13 @@
using System.Runtime.Serialization;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@ -19,7 +18,8 @@ namespace MediaBrowser.Controller.Entities.Audio
{
public bool IsAccessedByName { get; set; }
public List<string> ProductionLocations { get; set; }
[IgnoreDataMember]
public override bool IsFolder
{
get
@ -34,6 +34,11 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; }
}
public override bool CanDelete()
{
return !IsAccessedByName;
}
protected override IEnumerable<BaseItem> ActualChildren
{
get
@ -68,7 +73,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
return GetUserDataKey(this);
}
@ -78,6 +83,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// If the item is a folder, it returns the folder itself
/// </summary>
/// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath
{
get
@ -90,6 +96,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets a value indicating whether this instance is owned item.
/// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem
{
get
@ -122,89 +129,53 @@ namespace MediaBrowser.Controller.Entities.Audio
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 others = items.Except(songs).ToList();
var totalItems = songs.Count + others.Count;
var percentages = new Dictionary<Guid, double>(totalItems);
var tasks = new List<Task>();
var numComplete = 0;
// Refresh songs
foreach (var item in songs)
{
if (tasks.Count >= 2)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
}
cancellationToken.ThrowIfCancellationRequested();
var innerProgress = new ActionableProgress<double>();
// Avoid implicitly captured closure
var currentChild = item;
innerProgress.RegisterAction(p =>
{
lock (percentages)
{
percentages[currentChild.Id] = p / 100;
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
var percent = percentages.Values.Sum();
percent /= totalItems;
percent *= 100;
progress.Report(percent);
}
});
var taskChild = item;
tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
numComplete++;
double percent = numComplete;
percent /= totalItems;
progress.Report(percent * 100);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
// Refresh all non-songs
foreach (var item in others)
{
cancellationToken.ThrowIfCancellationRequested();
// Avoid implicitly captured closure
var currentChild = item;
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
lock (percentages)
{
percentages[currentChild.Id] = 1;
var percent = percentages.Values.Sum();
percent /= totalItems;
percent *= 100;
progress.Report(percent);
}
numComplete++;
double percent = numComplete;
percent /= totalItems;
progress.Report(percent * 100);
}
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);
}
public ArtistInfo GetLookupInfo()
{
var info = GetItemLookupInfo<ArtistInfo>();
info.SongInfos = RecursiveChildren.OfType<Audio>()
info.SongInfos = GetRecursiveChildren(i => i is Audio)
.Cast<Audio>()
.Select(i => i.GetLookupInfo())
.ToList();
@ -213,9 +184,16 @@ namespace MediaBrowser.Controller.Entities.Audio
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
{
return inputItems.OfType<IHasArtist>()
.Where(i => i.HasArtist(Name))
.Cast<BaseItem>();
return inputItems.Where(GetItemFilter());
}
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.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
return "MusicGenre-" + Name;
}
@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// If the item is a folder, it returns the folder itself
/// </summary>
/// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath
{
get
@ -38,10 +39,16 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
public override bool CanDelete()
{
return false;
}
/// <summary>
/// Gets a value indicating whether this instance is owned item.
/// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem
{
get
@ -52,7 +59,12 @@ namespace MediaBrowser.Controller.Entities.Audio
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); }
}
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>
/// Gets or sets the date created.
/// </summary>
@ -268,6 +300,7 @@ namespace MediaBrowser.Controller.Entities
public static IChannelManager ChannelManager { get; set; }
public static ICollectionManager CollectionManager { get; set; }
public static IImageProcessor ImageProcessor { get; set; }
public static IMediaSourceManager MediaSourceManager { get; set; }
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
@ -359,7 +392,7 @@ namespace MediaBrowser.Controller.Entities
{
get
{
if (!string.IsNullOrEmpty(ForcedSortName))
if (!string.IsNullOrWhiteSpace(ForcedSortName))
{
return ForcedSortName;
}
@ -379,21 +412,19 @@ namespace MediaBrowser.Controller.Entities
public string GetInternalMetadataPath()
{
return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath);
var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
return GetInternalMetadataPath(basePath);
}
protected virtual string GetInternalMetadataPath(string basePath)
{
var idString = Id.ToString("N");
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
}
public static string GetInternalMetadataPathForId(Guid id)
{
var idString = id.ToString("N");
var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder)
{
basePath = System.IO.Path.Combine(basePath, "library");
}
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
}
@ -692,7 +723,7 @@ namespace MediaBrowser.Controller.Entities
var requiresSave = false;
if (IsFolder || Parent != null)
if (SupportsOwnedItems)
{
try
{
@ -724,6 +755,12 @@ namespace MediaBrowser.Controller.Entities
}
}
[IgnoreDataMember]
protected virtual bool SupportsOwnedItems
{
get { return IsFolder || Parent != null; }
}
/// <summary>
/// Refreshes owned items such as trailers, theme videos, special features, etc.
/// Returns true or false indicating if changes were found.
@ -889,11 +926,24 @@ namespace MediaBrowser.Controller.Entities
get { return null; }
}
private string _userDataKey;
/// <summary>
/// Gets the user data key.
/// </summary>
/// <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();
}
@ -905,6 +955,12 @@ namespace MediaBrowser.Controller.Entities
return current.IsInMixedFolder == newItem.IsInMixedFolder;
}
public void AfterMetadataRefresh()
{
_sortName = null;
_userDataKey = null;
}
/// <summary>
/// Gets the preferred metadata language.
/// </summary>
@ -1024,7 +1080,8 @@ namespace MediaBrowser.Controller.Entities
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;
}
@ -1033,6 +1090,11 @@ namespace MediaBrowser.Controller.Entities
return true;
}
protected virtual bool IsAllowTagFilterEnforced()
{
return true;
}
/// <summary>
/// Gets the block unrated value.
/// </summary>
@ -1060,6 +1122,23 @@ namespace MediaBrowser.Controller.Entities
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>
/// Gets a value indicating whether this instance is folder.
/// </summary>
@ -1146,7 +1225,7 @@ namespace MediaBrowser.Controller.Entities
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))
{
@ -1164,7 +1243,8 @@ namespace MediaBrowser.Controller.Entities
}
return false;
});
}).FirstOrDefault();
}
return null;
@ -1458,7 +1538,7 @@ namespace MediaBrowser.Controller.Entities
currentFile.Attributes &= ~FileAttributes.Hidden;
}
currentFile.Delete();
FileSystem.DeleteFile(currentFile.FullName);
}
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
@ -1703,6 +1783,9 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public virtual bool BeforeMetadataRefresh()
{
_userDataKey = null;
_sortName = null;
var hasChanges = false;
if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))

View file

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

View file

@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
@ -37,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
Tags = new List<string>();
}
public override bool CanDownload()
{
var locationType = LocationType;
return locationType != LocationType.Remote &&
locationType != LocationType.Virtual;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
{
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; }
/// <summary>
@ -86,7 +91,7 @@ namespace MediaBrowser.Controller.Entities
{
var path = ContainingFolderPath;
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService)
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
{
FileInfo = new DirectoryInfo(path),
Path = path,
@ -121,12 +126,6 @@ namespace MediaBrowser.Controller.Entities
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>
/// 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***
@ -138,7 +137,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="refreshOptions">The refresh options.</param>
/// <param name="directoryService">The directory service.</param>
/// <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();
@ -146,8 +145,10 @@ namespace MediaBrowser.Controller.Entities
if (!list.SequenceEqual(PhysicalLocationsList))
{
await UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
return UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
}
return Task.FromResult(true);
}
/// <summary>

View file

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

View file

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

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@ -10,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
return "GameGenre-" + Name;
}
@ -20,6 +21,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
/// </summary>
/// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath
{
get
@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets a value indicating whether this instance is owned item.
/// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem
{
get
@ -40,9 +43,19 @@ namespace MediaBrowser.Controller.Entities
}
}
public override bool CanDelete()
{
return false;
}
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.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
if (!string.IsNullOrEmpty(GameSystemName))
{
return "GameSystem-" + GameSystemName;
}
return base.GetUserDataKey();
return base.CreateUserDataKey();
}
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.Collections.Generic;
using System.Linq;
@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
protected override string CreateUserDataKey()
{
return "Genre-" + Name;
}
@ -24,6 +25,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
/// </summary>
/// <value>The containing folder path.</value>
[IgnoreDataMember]
public override string ContainingFolderPath
{
get
@ -32,10 +34,16 @@ namespace MediaBrowser.Controller.Entities
}
}
public override bool CanDelete()
{
return false;
}
/// <summary>
/// Gets a value indicating whether this instance is owned item.
/// </summary>
/// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
[IgnoreDataMember]
public override bool IsOwnedItem
{
get
@ -46,7 +54,12 @@ namespace MediaBrowser.Controller.Entities
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.
/// </summary>
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