diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/Notifications.cs index 64863eb39e..3cadad6c8d 100644 --- a/Emby.Notifications/Notifications.cs +++ b/Emby.Notifications/Notifications.cs @@ -231,7 +231,7 @@ namespace Emby.Notifications } } - var hasSeries = item as IHasSeriesName; + var hasSeries = item as IHasSeries; if (hasSeries != null) { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 011f3a0fba..c6cfe72141 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -318,7 +318,6 @@ namespace Emby.Server.Implementations private IMediaEncoder MediaEncoder { get; set; } private ISubtitleEncoder SubtitleEncoder { get; set; } - private IConnectManager ConnectManager { get; set; } private ISessionManager SessionManager { get; set; } private ILiveTvManager LiveTvManager { get; set; } @@ -839,8 +838,6 @@ namespace Emby.Server.Implementations } } - protected abstract IConnectManager CreateConnectManager(); - protected virtual IHttpClient CreateHttpClient() { return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, GetDefaultUserAgent); @@ -947,7 +944,7 @@ namespace Emby.Server.Implementations AuthenticationRepository = GetAuthenticationRepository(); RegisterSingleInstance(AuthenticationRepository); - UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager, CryptographyProvider); + UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager, CryptographyProvider); RegisterSingleInstance(UserManager); LibraryManager = new LibraryManager(this, Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); @@ -986,9 +983,6 @@ namespace Emby.Server.Implementations var encryptionManager = new EncryptionManager(); RegisterSingleInstance(encryptionManager); - ConnectManager = CreateConnectManager(); - RegisterSingleInstance(ConnectManager); - DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); RegisterSingleInstance(DeviceManager); @@ -1045,11 +1039,11 @@ namespace Emby.Server.Implementations RegisterSingleInstance(activityLogRepo); RegisterSingleInstance(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo, UserManager)); - var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager, UserManager); + var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); RegisterSingleInstance(authContext); RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); - AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, NetworkManager); + AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, SessionManager, NetworkManager); RegisterSingleInstance(AuthService); SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory, TextEncoding); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index e832c7c6f8..e61c5e2832 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1086,7 +1086,7 @@ namespace Emby.Server.Implementations.Channels } item.ParentId = parentFolderId; - var hasSeries = item as IHasSeriesName; + var hasSeries = item as IHasSeries; if (hasSeries != null) { if (!string.Equals(hasSeries.SeriesName, info.SeriesName, StringComparison.OrdinalIgnoreCase)) @@ -1215,4 +1215,4 @@ namespace Emby.Server.Implementations.Channels return result; } } -} \ No newline at end of file +} diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index bde28c923d..c9495c5747 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -942,7 +942,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@Album", item.Album); saveItemStatement.TryBind("@IsVirtualItem", item.IsVirtualItem); - var hasSeriesName = item as IHasSeriesName; + var hasSeriesName = item as IHasSeries; if (hasSeriesName != null) { saveItemStatement.TryBind("@SeriesName", hasSeriesName.SeriesName); @@ -1757,7 +1757,7 @@ namespace Emby.Server.Implementations.Data } index++; - var hasSeriesName = item as IHasSeriesName; + var hasSeriesName = item as IHasSeries; if (hasSeriesName != null) { if (!reader.IsDBNull(index)) @@ -6441,4 +6441,4 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } -} \ No newline at end of file +} diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 9dfb8cf034..e153d6f71f 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -17,19 +17,17 @@ namespace Emby.Server.Implementations.HttpServer.Security { private readonly IServerConfigurationManager _config; - public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, IConnectManager connectManager, ISessionManager sessionManager, INetworkManager networkManager) + public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, ISessionManager sessionManager, INetworkManager networkManager) { AuthorizationContext = authorizationContext; _config = config; SessionManager = sessionManager; - ConnectManager = connectManager; UserManager = userManager; NetworkManager = networkManager; } public IUserManager UserManager { get; private set; } public IAuthorizationContext AuthorizationContext { get; private set; } - public IConnectManager ConnectManager { get; private set; } public ISessionManager SessionManager { get; private set; } public INetworkManager NetworkManager { get; private set; } diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 75dfa95d98..c3e2d31708 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -13,13 +13,11 @@ namespace Emby.Server.Implementations.HttpServer.Security public class AuthorizationContext : IAuthorizationContext { private readonly IAuthenticationRepository _authRepo; - private readonly IConnectManager _connectManager; private readonly IUserManager _userManager; - public AuthorizationContext(IAuthenticationRepository authRepo, IConnectManager connectManager, IUserManager userManager) + public AuthorizationContext(IAuthenticationRepository authRepo, IUserManager userManager) { _authRepo = authRepo; - _connectManager = connectManager; _userManager = userManager; } diff --git a/Emby.Server.Implementations/Library/ConnectManager.cs b/Emby.Server.Implementations/Library/ConnectManager.cs deleted file mode 100644 index 5df45aa440..0000000000 --- a/Emby.Server.Implementations/Library/ConnectManager.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MediaBrowser.Common.Events; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Connect; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Connect; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Users; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Security; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Session; -using MediaBrowser.Controller.Plugins; - -namespace Emby.Server.Implementations.Library -{ - public class ConnectManager : IConnectManager - { - public ConnectManager() - { - } - - } -} diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 317b64693e..da80a48247 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -74,7 +74,6 @@ namespace Emby.Server.Implementations.Library private readonly Func _imageProcessorFactory; private readonly Func _dtoServiceFactory; - private readonly Func _connectFactory; private readonly IServerApplicationHost _appHost; private readonly IFileSystem _fileSystem; private readonly ICryptoProvider _cryptographyProvider; @@ -82,7 +81,7 @@ namespace Emby.Server.Implementations.Library private IAuthenticationProvider[] _authenticationProviders; private DefaultAuthenticationProvider _defaultAuthenticationProvider; - public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func imageProcessorFactory, Func dtoServiceFactory, Func connectFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ICryptoProvider cryptographyProvider) + public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func imageProcessorFactory, Func dtoServiceFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ICryptoProvider cryptographyProvider) { _logger = logger; UserRepository = userRepository; @@ -90,7 +89,6 @@ namespace Emby.Server.Implementations.Library _networkManager = networkManager; _imageProcessorFactory = imageProcessorFactory; _dtoServiceFactory = dtoServiceFactory; - _connectFactory = connectFactory; _appHost = appHost; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 366d1318bf..0b1dc083d9 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -61,21 +61,6 @@ namespace MediaBrowser.Api public bool? IsAppStoreEnabled { get; set; } } - /// - /// Class GetPackageVersionUpdates - /// - [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")] - [Authenticated(Roles = "Admin")] - public class GetPackageVersionUpdates : IReturn - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "PackageType", Description = "Package type filter (System/UserInstalled)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string PackageType { get; set; } - } - /// /// Class InstallPackage /// @@ -146,30 +131,7 @@ namespace MediaBrowser.Api /// /// The request. /// System.Object. - public async Task Get(GetPackageVersionUpdates request) - { - PackageVersionInfo[] result = null; - - if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase)) - { - result = (await _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).ConfigureAwait(false)).ToArray(); - } - - else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || - string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase)) - { - var updateCheckResult = await _appHost - .CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress()).ConfigureAwait(false); - - if (updateCheckResult.IsUpdateAvailable) - { - result = new PackageVersionInfo[] { updateCheckResult.Package }; - } - } - - return ToOptimizedResult(result ?? new PackageVersionInfo[] { }); - } - + /// /// /// Gets the specified request. /// diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index ac20b5e337..db5d256c47 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -148,7 +148,6 @@ namespace MediaBrowser.Api.Playback DisposeTranscodingThrottler(); DisposeLiveStream(); DisposeLogStream(); - DisposeIsoMount(); TranscodingJob = null; } @@ -251,9 +250,9 @@ namespace MediaBrowser.Api.Playback public DeviceProfile DeviceProfile { get; set; } public TranscodingJob TranscodingJob; - public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) + public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate) { - ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate); + ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, 0, percentComplete, 0, bitRate); } } } diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 2e363136b3..0473dd9245 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -230,7 +230,7 @@ namespace MediaBrowser.Api result.StartDate = program.StartDate; } - var hasSeries = item as IHasSeriesName; + var hasSeries = item as IHasSeries; if (hasSeries != null) { result.Series = hasSeries.SeriesName; diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index b4891ba1a8..7fccbcc9b0 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -51,16 +51,14 @@ namespace MediaBrowser.Api private readonly IServerConfigurationManager _config; private readonly IServerApplicationHost _appHost; private readonly IUserManager _userManager; - private readonly IConnectManager _connectManager; private readonly IMediaEncoder _mediaEncoder; private readonly IHttpClient _httpClient; - public StartupWizardService(IServerConfigurationManager config, IHttpClient httpClient, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, IMediaEncoder mediaEncoder) + public StartupWizardService(IServerConfigurationManager config, IHttpClient httpClient, IServerApplicationHost appHost, IUserManager userManager, IMediaEncoder mediaEncoder) { _config = config; _appHost = appHost; _userManager = userManager; - _connectManager = connectManager; _mediaEncoder = mediaEncoder; _httpClient = httpClient; } diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs new file mode 100644 index 0000000000..310e2aa638 --- /dev/null +++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs @@ -0,0 +1,18 @@ +using System; + +namespace MediaBrowser.Common.Configuration +{ + public class ConfigurationUpdateEventArgs : EventArgs + { + /// + /// Gets or sets the key. + /// + /// The key. + public string Key { get; set; } + /// + /// Gets or sets the new configuration. + /// + /// The new configuration. + public object NewConfiguration { get; set; } + } +} diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs new file mode 100644 index 0000000000..c6256bc5a4 --- /dev/null +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -0,0 +1,84 @@ + +namespace MediaBrowser.Common.Configuration +{ + /// + /// Interface IApplicationPaths + /// + public interface IApplicationPaths + { + /// + /// Gets the path to the program data folder + /// + /// The program data path. + string ProgramDataPath { get; } + + /// + /// Gets the path to the program system folder + /// + /// The program data path. + string ProgramSystemPath { get; } + + /// + /// Gets the folder path to the data directory + /// + /// The data directory. + string DataPath { get; } + + /// + /// Gets the image cache path. + /// + /// The image cache path. + string ImageCachePath { get; } + + /// + /// Gets the path to the plugin directory + /// + /// The plugins path. + string PluginsPath { get; } + + /// + /// Gets the path to the plugin configurations directory + /// + /// The plugin configurations path. + string PluginConfigurationsPath { get; } + + /// + /// Gets the path to where temporary update files will be stored + /// + /// The plugin configurations path. + string TempUpdatePath { get; } + + /// + /// Gets the path to the log directory + /// + /// The log directory path. + string LogDirectoryPath { get; } + + /// + /// Gets the path to the application configuration root directory + /// + /// The configuration directory path. + string ConfigurationDirectoryPath { get; } + + /// + /// Gets the path to the system configuration file + /// + /// The system configuration file path. + string SystemConfigurationFilePath { get; } + + /// + /// Gets the folder path to the cache directory + /// + /// The cache directory. + string CachePath { get; } + + /// + /// Gets the folder path to the temp directory within the cache folder + /// + /// The temp directory. + string TempDirectory { get; } + + string VirtualDataPath { get; } + } + +} diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs new file mode 100644 index 0000000000..6ed6385360 --- /dev/null +++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Common.Configuration +{ + public interface IConfigurationFactory + { + IEnumerable GetConfigurations(); + } + + public class ConfigurationStore + { + public string Key { get; set; } + + public Type ConfigurationType { get; set; } + } + + public interface IValidatingConfiguration + { + void Validate(object oldConfig, object newConfig); + } +} diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs new file mode 100644 index 0000000000..d826a3ee78 --- /dev/null +++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs @@ -0,0 +1,82 @@ +using MediaBrowser.Model.Configuration; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Common.Configuration +{ + public interface IConfigurationManager + { + /// + /// Occurs when [configuration updating]. + /// + event EventHandler NamedConfigurationUpdating; + + /// + /// Occurs when [configuration updated]. + /// + event EventHandler ConfigurationUpdated; + + /// + /// Occurs when [named configuration updated]. + /// + event EventHandler NamedConfigurationUpdated; + + /// + /// Gets or sets the application paths. + /// + /// The application paths. + IApplicationPaths CommonApplicationPaths { get; } + + /// + /// Gets the configuration. + /// + /// The configuration. + BaseApplicationConfiguration CommonConfiguration { get; } + + /// + /// Saves the configuration. + /// + void SaveConfiguration(); + + /// + /// Replaces the configuration. + /// + /// The new configuration. + void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration); + + /// + /// Gets the configuration. + /// + /// The key. + /// System.Object. + object GetConfiguration(string key); + + /// + /// Gets the type of the configuration. + /// + /// The key. + /// Type. + Type GetConfigurationType(string key); + + /// + /// Saves the configuration. + /// + /// The key. + /// The configuration. + void SaveConfiguration(string key, object configuration); + + /// + /// Adds the parts. + /// + /// The factories. + void AddParts(IEnumerable factories); + } + + public static class ConfigurationManagerExtensions + { + public static T GetConfiguration(this IConfigurationManager manager, string key) + { + return (T)manager.GetConfiguration(key); + } + } +} diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs new file mode 100644 index 0000000000..2bb52f0ae5 --- /dev/null +++ b/MediaBrowser.Common/Events/EventHelper.cs @@ -0,0 +1,108 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Events +{ + /// + /// Class EventHelper + /// + public static class EventHelper + { + /// + /// Fires the event. + /// + /// The handler. + /// The sender. + /// The instance containing the event data. + /// The logger. + public static void QueueEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger) + { + if (handler != null) + { + Task.Run(() => + { + try + { + handler(sender, args); + } + catch (Exception ex) + { + logger.ErrorException("Error in event handler", ex); + } + }); + } + } + + /// + /// Queues the event. + /// + /// + /// The handler. + /// The sender. + /// The args. + /// The logger. + public static void QueueEventIfNotNull(EventHandler handler, object sender, T args, ILogger logger) + { + if (handler != null) + { + Task.Run(() => + { + try + { + handler(sender, args); + } + catch (Exception ex) + { + logger.ErrorException("Error in event handler", ex); + } + }); + } + } + + /// + /// Fires the event. + /// + /// The handler. + /// The sender. + /// The instance containing the event data. + /// The logger. + public static void FireEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger) + { + if (handler != null) + { + try + { + handler(sender, args); + } + catch (Exception ex) + { + logger.ErrorException("Error in event handler", ex); + } + } + } + + /// + /// Fires the event. + /// + /// + /// The handler. + /// The sender. + /// The args. + /// The logger. + public static void FireEventIfNotNull(EventHandler handler, object sender, T args, ILogger logger) + { + if (handler != null) + { + try + { + handler(sender, args); + } + catch (Exception ex) + { + logger.ErrorException("Error in event handler", ex); + } + } + } + } +} diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs new file mode 100644 index 0000000000..d7f4424faa --- /dev/null +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -0,0 +1,58 @@ +using System; +using System.Globalization; +using System.Text.RegularExpressions; +using MediaBrowser.Model.Cryptography; + +namespace MediaBrowser.Common.Extensions +{ + /// + /// Class BaseExtensions + /// + public static class BaseExtensions + { + public static ICryptoProvider CryptographyProvider { get; set; } + + /// + /// Strips the HTML. + /// + /// The HTML string. + /// System.String. + public static string StripHtml(this string htmlString) + { + // http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net + const string pattern = @"<(.|\n)*?>"; + + return Regex.Replace(htmlString, pattern, string.Empty).Trim(); + } + + /// + /// Gets the M d5. + /// + /// The STR. + /// Guid. + public static Guid GetMD5(this string str) + { + return CryptographyProvider.GetMD5(str); + } + + /// + /// Gets the MB id. + /// + /// The STR. + /// The type. + /// Guid. + /// type + [Obsolete("Use LibraryManager.GetNewItemId")] + public static Guid GetMBId(this string str, Type type) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + var key = type.FullName + str.ToLower(); + + return key.GetMD5(); + } + } +} diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs new file mode 100644 index 0000000000..89e20b1b41 --- /dev/null +++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs @@ -0,0 +1,63 @@ +using System; + +namespace MediaBrowser.Common.Extensions +{ + /// + /// Class ResourceNotFoundException + /// + public class ResourceNotFoundException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public ResourceNotFoundException() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ResourceNotFoundException(string message) + : base(message) + { + + } + } + + public class RemoteServiceUnavailableException : Exception + { + public RemoteServiceUnavailableException() + { + + } + + public RemoteServiceUnavailableException(string message) + : base(message) + { + + } + } + + public class RateLimitExceededException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public RateLimitExceededException() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public RateLimitExceededException(string message) + : base(message) + { + + } + } +} diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs new file mode 100644 index 0000000000..32b942b60d --- /dev/null +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -0,0 +1,147 @@ +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Updates; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common +{ + /// + /// An interface to be implemented by the applications hosting a kernel + /// + public interface IApplicationHost + { + /// + /// Gets the display name of the operating system. + /// + /// The display name of the operating system. + string OperatingSystemDisplayName { get; } + + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Gets the device identifier. + /// + /// The device identifier. + string SystemId { get; } + + /// + /// Occurs when [application updated]. + /// + event EventHandler> ApplicationUpdated; + + /// + /// Gets or sets a value indicating whether this instance has pending kernel reload. + /// + /// true if this instance has pending kernel reload; otherwise, false. + bool HasPendingRestart { get; } + + bool IsShuttingDown { get; } + + /// + /// Gets a value indicating whether this instance can self restart. + /// + /// true if this instance can self restart; otherwise, false. + bool CanSelfRestart { get; } + + /// + /// Occurs when [has pending restart changed]. + /// + event EventHandler HasPendingRestartChanged; + + /// + /// Notifies the pending restart. + /// + void NotifyPendingRestart(); + + /// + /// Restarts this instance. + /// + void Restart(); + + /// + /// Gets the application version. + /// + /// The application version. + Version ApplicationVersion { get; } + + /// + /// Gets or sets a value indicating whether this instance can self update. + /// + /// true if this instance can self update; otherwise, false. + bool CanSelfUpdate { get; } + + /// + /// Gets the exports. + /// + /// + /// if set to true [manage liftime]. + /// IEnumerable{``0}. + IEnumerable GetExports(bool manageLiftime = true); + + /// + /// Checks for update. + /// + /// Task{CheckForUpdateResult}. + Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress); + + /// + /// Updates the application. + /// + /// Task. + Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress progress); + + /// + /// Resolves this instance. + /// + /// + /// ``0. + T Resolve(); + + /// + /// Resolves this instance. + /// + /// + /// ``0. + T TryResolve(); + + /// + /// Shuts down. + /// + Task Shutdown(); + + /// + /// Gets the plugins. + /// + /// The plugins. + IPlugin[] Plugins { get; } + + /// + /// Removes the plugin. + /// + /// The plugin. + void RemovePlugin(IPlugin plugin); + + /// + /// Inits this instance. + /// + void Init(); + + /// + /// Creates the instance. + /// + /// The type. + /// System.Object. + object CreateInstance(Type type); + + PackageVersionClass SystemUpdateLevel { get; } + + string GetValue(string name); + } +} diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj new file mode 100644 index 0000000000..43f3b0be7a --- /dev/null +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -0,0 +1,16 @@ + + + + + + + + + + + + netstandard2.0 + false + + + diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs new file mode 100644 index 0000000000..c61e88c878 --- /dev/null +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Text; + +namespace MediaBrowser.Common.Net +{ + /// + /// Class HttpRequestOptions + /// + public class HttpRequestOptions + { + /// + /// Gets or sets the URL. + /// + /// The URL. + public string Url { get; set; } + + public CompressionMethod? DecompressionMethod { get; set; } + + /// + /// Gets or sets the accept header. + /// + /// The accept header. + public string AcceptHeader + { + get { return GetHeaderValue("Accept"); } + set + { + RequestHeaders["Accept"] = value; + } + } + /// + /// Gets or sets the cancellation token. + /// + /// The cancellation token. + public CancellationToken CancellationToken { get; set; } + + /// + /// Gets or sets the resource pool. + /// + /// The resource pool. + public SemaphoreSlim ResourcePool { get; set; } + + /// + /// Gets or sets the user agent. + /// + /// The user agent. + public string UserAgent + { + get { return GetHeaderValue("User-Agent"); } + set + { + RequestHeaders["User-Agent"] = value; + } + } + + /// + /// Gets or sets the referrer. + /// + /// The referrer. + public string Referer { get; set; } + + /// + /// Gets or sets the host. + /// + /// The host. + public string Host { get; set; } + + /// + /// Gets or sets the progress. + /// + /// The progress. + public IProgress Progress { get; set; } + + /// + /// Gets or sets a value indicating whether [enable HTTP compression]. + /// + /// true if [enable HTTP compression]; otherwise, false. + public bool EnableHttpCompression { get; set; } + + public Dictionary RequestHeaders { get; private set; } + + public string RequestContentType { get; set; } + + public string RequestContent { get; set; } + public byte[] RequestContentBytes { get; set; } + + public bool BufferContent { get; set; } + + public bool LogRequest { get; set; } + public bool LogRequestAsDebug { get; set; } + public bool LogErrors { get; set; } + public bool LogResponse { get; set; } + public bool LogResponseHeaders { get; set; } + + public bool LogErrorResponseBody { get; set; } + public bool EnableKeepAlive { get; set; } + + public CacheMode CacheMode { get; set; } + public TimeSpan CacheLength { get; set; } + + public int TimeoutMs { get; set; } + public bool EnableDefaultUserAgent { get; set; } + + public bool AppendCharsetToMimeType { get; set; } + public string DownloadFilePath { get; set; } + + private string GetHeaderValue(string name) + { + string value; + + RequestHeaders.TryGetValue(name, out value); + + return value; + } + + /// + /// Initializes a new instance of the class. + /// + public HttpRequestOptions() + { + EnableHttpCompression = true; + + RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + + LogRequest = true; + LogErrors = true; + CacheMode = CacheMode.None; + + TimeoutMs = 20000; + } + + public void SetPostData(IDictionary values) + { + var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key])); + var postContent = string.Join("&", strings.ToArray()); + + RequestContent = postContent; + RequestContentType = "application/x-www-form-urlencoded"; + } + } + + public enum CacheMode + { + None = 0, + Unconditional = 1 + } + + public enum CompressionMethod + { + Deflate, + Gzip + } +} diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs new file mode 100644 index 0000000000..ed941a4474 --- /dev/null +++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace MediaBrowser.Common.Net +{ + /// + /// Class HttpResponseInfo + /// + public class HttpResponseInfo : IDisposable + { + /// + /// Gets or sets the type of the content. + /// + /// The type of the content. + public string ContentType { get; set; } + + /// + /// Gets or sets the response URL. + /// + /// The response URL. + public string ResponseUrl { get; set; } + + /// + /// Gets or sets the content. + /// + /// The content. + public Stream Content { get; set; } + + /// + /// Gets or sets the status code. + /// + /// The status code. + public HttpStatusCode StatusCode { get; set; } + + /// + /// Gets or sets the temp file path. + /// + /// The temp file path. + public string TempFilePath { get; set; } + + /// + /// Gets or sets the length of the content. + /// + /// The length of the content. + public long? ContentLength { get; set; } + + /// + /// Gets or sets the headers. + /// + /// The headers. + public Dictionary Headers { get; set; } + + private readonly IDisposable _disposable; + + public HttpResponseInfo(IDisposable disposable) + { + _disposable = disposable; + Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + public HttpResponseInfo() + { + Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public void Dispose() + { + if (_disposable != null) + { + _disposable.Dispose(); + } + } + } +} diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs new file mode 100644 index 0000000000..cf55119653 --- /dev/null +++ b/MediaBrowser.Common/Net/IHttpClient.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Net +{ + /// + /// Interface IHttpClient + /// + public interface IHttpClient + { + /// + /// Gets the response. + /// + /// The options. + /// Task{HttpResponseInfo}. + Task GetResponse(HttpRequestOptions options); + + /// + /// Gets the specified options. + /// + /// The options. + /// Task{Stream}. + Task Get(HttpRequestOptions options); + + /// + /// Sends the asynchronous. + /// + /// The options. + /// The HTTP method. + /// Task{HttpResponseInfo}. + Task SendAsync(HttpRequestOptions options, string httpMethod); + + /// + /// Posts the specified options. + /// + /// The options. + /// Task{HttpResponseInfo}. + Task Post(HttpRequestOptions options); + + /// + /// Downloads the contents of a given url into a temporary location + /// + /// The options. + /// Task{System.String}. + /// progress + /// + Task GetTempFile(HttpRequestOptions options); + + /// + /// Gets the temporary file response. + /// + /// The options. + /// Task{HttpResponseInfo}. + Task GetTempFileResponse(HttpRequestOptions options); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs new file mode 100644 index 0000000000..b2ff797bcf --- /dev/null +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -0,0 +1,66 @@ +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using System.Collections.Generic; +using System; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Net +{ + public interface INetworkManager + { + event EventHandler NetworkChanged; + + /// + /// Gets a random port number that is currently available + /// + /// System.Int32. + int GetRandomUnusedTcpPort(); + + int GetRandomUnusedUdpPort(); + + Func LocalSubnetsFn { get; set; } + + /// + /// Returns MAC Address from first Network Card in Computer + /// + /// [string] MAC Address + List GetMacAddresses(); + + /// + /// Determines whether [is in private address space] [the specified endpoint]. + /// + /// The endpoint. + /// true if [is in private address space] [the specified endpoint]; otherwise, false. + bool IsInPrivateAddressSpace(string endpoint); + + /// + /// Gets the network shares. + /// + /// The path. + /// IEnumerable{NetworkShare}. + IEnumerable GetNetworkShares(string path); + + /// + /// Gets available devices within the domain + /// + /// PC's in the Domain + IEnumerable GetNetworkDevices(); + + /// + /// Determines whether [is in local network] [the specified endpoint]. + /// + /// The endpoint. + /// true if [is in local network] [the specified endpoint]; otherwise, false. + bool IsInLocalNetwork(string endpoint); + + IpAddressInfo[] GetLocalIpAddresses(); + + IpAddressInfo ParseIpAddress(string ipAddress); + + bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo); + + Task GetHostAddressesAsync(string host); + + bool IsAddressInSubnets(string addressString, string[] subnets); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs new file mode 100644 index 0000000000..82eb6ba4bf --- /dev/null +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -0,0 +1,276 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; +using System; +using System.IO; + +namespace MediaBrowser.Common.Plugins +{ + public abstract class BasePlugin : IPlugin, IPluginAssembly + { + /// + /// Gets the name of the plugin + /// + /// The name. + public abstract string Name { get; } + + /// + /// Gets the description. + /// + /// The description. + public virtual string Description + { + get { return string.Empty; } + } + + /// + /// Gets the unique id. + /// + /// The unique id. + public virtual Guid Id { get; private set; } + + /// + /// Gets the plugin version + /// + /// The version. + public Version Version { get; private set; } + + /// + /// Gets the path to the assembly file + /// + /// The assembly file path. + public string AssemblyFilePath { get; private set; } + + /// + /// Gets the plugin info. + /// + /// PluginInfo. + public virtual PluginInfo GetPluginInfo() + { + var info = new PluginInfo + { + Name = Name, + Version = Version.ToString(), + Description = Description, + Id = Id.ToString() + }; + + return info; + } + + /// + /// Called when just before the plugin is uninstalled from the server. + /// + public virtual void OnUninstalling() + { + + } + + public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion) + { + AssemblyFilePath = assemblyFilePath; + DataFolderPath = dataFolderPath; + Version = assemblyVersion; + } + + public void SetId(Guid assemblyId) + { + Id = assemblyId; + } + + /// + /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed + /// + /// The data folder path. + public string DataFolderPath { get; private set; } + } + + /// + /// Provides a common base class for all plugins + /// + /// The type of the T configuration type. + public abstract class BasePlugin : BasePlugin, IHasPluginConfiguration + where TConfigurationType : BasePluginConfiguration + { + /// + /// Gets the application paths. + /// + /// The application paths. + protected IApplicationPaths ApplicationPaths { get; private set; } + + /// + /// Gets the XML serializer. + /// + /// The XML serializer. + protected IXmlSerializer XmlSerializer { get; private set; } + + /// + /// Gets the type of configuration this plugin uses + /// + /// The type of the configuration. + public Type ConfigurationType + { + get { return typeof(TConfigurationType); } + } + + private Action _directoryCreateFn; + public void SetStartupInfo(Action directoryCreateFn) + { + // hack alert, until the .net core transition is complete + _directoryCreateFn = directoryCreateFn; + } + + /// + /// Gets the name the assembly file + /// + /// The name of the assembly file. + protected string AssemblyFileName + { + get + { + return Path.GetFileName(AssemblyFilePath); + } + } + + /// + /// The _configuration sync lock + /// + private readonly object _configurationSyncLock = new object(); + /// + /// The _configuration + /// + private TConfigurationType _configuration; + /// + /// Gets the plugin's configuration + /// + /// The configuration. + public TConfigurationType Configuration + { + get + { + // Lazy load + if (_configuration == null) + { + lock (_configurationSyncLock) + { + if (_configuration == null) + { + _configuration = LoadConfiguration(); + } + } + } + return _configuration; + } + protected set + { + _configuration = value; + } + } + + private TConfigurationType LoadConfiguration() + { + var path = ConfigurationFilePath; + + try + { + return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path); + } + catch + { + return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType)); + } + } + + /// + /// Gets the name of the configuration file. Subclasses should override + /// + /// The name of the configuration file. + public virtual string ConfigurationFileName + { + get { return Path.ChangeExtension(AssemblyFileName, ".xml"); } + } + + /// + /// Gets the full path to the configuration file + /// + /// The configuration file path. + public string ConfigurationFilePath + { + get + { + return Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The application paths. + /// The XML serializer. + protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + { + ApplicationPaths = applicationPaths; + XmlSerializer = xmlSerializer; + } + + /// + /// The _save lock + /// + private readonly object _configurationSaveLock = new object(); + + /// + /// Saves the current configuration to the file system + /// + public virtual void SaveConfiguration() + { + lock (_configurationSaveLock) + { + _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath)); + + XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath); + } + } + + /// + /// Completely overwrites the current configuration with a new copy + /// Returns true or false indicating success or failure + /// + /// The configuration. + /// configuration + public virtual void UpdateConfiguration(BasePluginConfiguration configuration) + { + if (configuration == null) + { + throw new ArgumentNullException("configuration"); + } + + Configuration = (TConfigurationType)configuration; + + SaveConfiguration(); + } + + /// + /// Gets the plugin's configuration + /// + /// The configuration. + BasePluginConfiguration IHasPluginConfiguration.Configuration + { + get { return Configuration; } + } + + public override PluginInfo GetPluginInfo() + { + var info = base.GetPluginInfo(); + + info.ConfigurationFileName = ConfigurationFileName; + + return info; + } + } + + public interface IPluginAssembly + { + void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion); + void SetId(Guid assemblyId); + } +} diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs new file mode 100644 index 0000000000..bffd211439 --- /dev/null +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -0,0 +1,83 @@ +using MediaBrowser.Model.Plugins; +using System; + +namespace MediaBrowser.Common.Plugins +{ + /// + /// Interface IPlugin + /// + public interface IPlugin + { + /// + /// Gets the name of the plugin + /// + /// The name. + string Name { get; } + + /// + /// Gets the description. + /// + /// The description. + string Description { get; } + + /// + /// Gets the unique id. + /// + /// The unique id. + Guid Id { get; } + + /// + /// Gets the plugin version + /// + /// The version. + Version Version { get; } + + /// + /// Gets the path to the assembly file + /// + /// The assembly file path. + string AssemblyFilePath { get; } + + /// + /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed + /// + /// The data folder path. + string DataFolderPath { get; } + + /// + /// Gets the plugin info. + /// + /// PluginInfo. + PluginInfo GetPluginInfo(); + + /// + /// Called when just before the plugin is uninstalled from the server. + /// + void OnUninstalling(); + } + + public interface IHasPluginConfiguration + { + /// + /// Gets the type of configuration this plugin uses + /// + /// The type of the configuration. + Type ConfigurationType { get; } + + /// + /// Completely overwrites the current configuration with a new copy + /// Returns true or false indicating success or failure + /// + /// The configuration. + /// configuration + void UpdateConfiguration(BasePluginConfiguration configuration); + + /// + /// Gets the plugin's configuration + /// + /// The configuration. + BasePluginConfiguration Configuration { get; } + + void SetStartupInfo(Action directoryCreateFn); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs new file mode 100644 index 0000000000..67347bc157 --- /dev/null +++ b/MediaBrowser.Common/Progress/ActionableProgress.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Common.Progress +{ + /// + /// Class ActionableProgress + /// + /// + public class ActionableProgress : IProgress + { + /// + /// The _actions + /// + private Action _action; + public event EventHandler ProgressChanged; + + /// + /// Registers the action. + /// + /// The action. + public void RegisterAction(Action action) + { + _action = action; + } + + public void Report(T value) + { + if (ProgressChanged != null) + { + ProgressChanged(this, value); + } + + var action = _action; + if (action != null) + { + action(value); + } + } + } + + public class SimpleProgress : IProgress + { + public event EventHandler ProgressChanged; + + public void Report(T value) + { + if (ProgressChanged != null) + { + ProgressChanged(this, value); + } + } + } +} diff --git a/MediaBrowser.Common/Properties/AssemblyInfo.cs b/MediaBrowser.Common/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..09fd68f93a --- /dev/null +++ b/MediaBrowser.Common/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.Common")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.Common")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// \ No newline at end of file diff --git a/MediaBrowser.Common/Security/IRequiresRegistration.cs b/MediaBrowser.Common/Security/IRequiresRegistration.cs new file mode 100644 index 0000000000..7b1667c2e2 --- /dev/null +++ b/MediaBrowser.Common/Security/IRequiresRegistration.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Security +{ + public interface IRequiresRegistration + { + /// + /// Load all registration information required for this entity. + /// Your class should re-load all MBRegistrationRecords when this is called even if they were + /// previously loaded. + /// + /// + Task LoadRegistrationInfoAsync(); + } +} diff --git a/MediaBrowser.Common/Security/ISecurityManager.cs b/MediaBrowser.Common/Security/ISecurityManager.cs new file mode 100644 index 0000000000..b63a9efd09 --- /dev/null +++ b/MediaBrowser.Common/Security/ISecurityManager.cs @@ -0,0 +1,32 @@ +using MediaBrowser.Model.Entities; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Security +{ + public interface ISecurityManager + { + /// + /// Gets a value indicating whether this instance is MB supporter. + /// + /// true if this instance is MB supporter; otherwise, false. + Task IsSupporter(); + + /// + /// Gets or sets the supporter key. + /// + /// The supporter key. + string SupporterKey { get; } + + /// + /// Gets the registration status. Overload to support existing plug-ins. + /// + Task GetRegistrationStatus(string feature); + + /// + /// Register and app store sale with our back-end + /// + /// Json parameters to pass to admin server + Task RegisterAppStoreSale(string parameters); + Task UpdateSupporterKey(string newValue); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Security/PaymentRequiredException.cs b/MediaBrowser.Common/Security/PaymentRequiredException.cs new file mode 100644 index 0000000000..27b3e69613 --- /dev/null +++ b/MediaBrowser.Common/Security/PaymentRequiredException.cs @@ -0,0 +1,8 @@ +using System; + +namespace MediaBrowser.Common.Security +{ + public class PaymentRequiredException : Exception + { + } +} diff --git a/MediaBrowser.Common/Updates/GithubUpdater.cs b/MediaBrowser.Common/Updates/GithubUpdater.cs new file mode 100644 index 0000000000..4275799a98 --- /dev/null +++ b/MediaBrowser.Common/Updates/GithubUpdater.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Updates; + +namespace MediaBrowser.Common.Updates +{ + public class GithubUpdater + { + private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _jsonSerializer; + + public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer) + { + _httpClient = httpClient; + _jsonSerializer = jsonSerializer; + } + + public async Task CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, TimeSpan cacheLength, CancellationToken cancellationToken) + { + var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository); + + var options = new HttpRequestOptions + { + Url = url, + EnableKeepAlive = false, + CancellationToken = cancellationToken, + UserAgent = "Emby/3.0", + BufferContent = false + }; + + if (cacheLength.Ticks > 0) + { + options.CacheMode = CacheMode.Unconditional; + options.CacheLength = cacheLength; + } + + using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)) + { + using (var stream = response.Content) + { + var obj = _jsonSerializer.DeserializeFromStream(stream); + + return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename); + } + } + } + + private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename) + { + if (updateLevel == PackageVersionClass.Release) + { + // Technically all we need to do is check that it's not pre-release + // But let's addititional checks for -beta and -dev to handle builds that might be temporarily tagged incorrectly. + obj = obj.Where(i => !i.prerelease && !i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) && !i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray(); + } + else if (updateLevel == PackageVersionClass.Beta) + { + obj = obj.Where(i => i.prerelease && i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray(); + } + else if (updateLevel == PackageVersionClass.Dev) + { + obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray(); + } + + var availableUpdate = obj + .Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename)) + .Where(i => i != null) + .OrderByDescending(i => Version.Parse(i.AvailableVersion)) + .FirstOrDefault(); + + return availableUpdate ?? new CheckForUpdateResult + { + IsUpdateAvailable = false + }; + } + + private bool MatchesUpdateLevel(RootObject i, PackageVersionClass updateLevel) + { + if (updateLevel == PackageVersionClass.Beta) + { + return i.prerelease && i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase); + } + if (updateLevel == PackageVersionClass.Dev) + { + return !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || + i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase); + } + + // Technically all we need to do is check that it's not pre-release + // But let's addititional checks for -beta and -dev to handle builds that might be temporarily tagged incorrectly. + return !i.prerelease && !i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) && + !i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase); + } + + public async Task> GetLatestReleases(string organzation, string repository, string assetFilename, CancellationToken cancellationToken) + { + var list = new List(); + + var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository); + + var options = new HttpRequestOptions + { + Url = url, + EnableKeepAlive = false, + CancellationToken = cancellationToken, + UserAgent = "Emby/3.0", + BufferContent = false + }; + + using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)) + { + using (var stream = response.Content) + { + var obj = _jsonSerializer.DeserializeFromStream(stream); + + obj = obj.Where(i => (i.assets ?? new List()).Any(a => IsAsset(a, assetFilename, i.tag_name))).ToArray(); + + list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1)); + list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1)); + list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Dev)).OrderByDescending(GetVersion).Take(1)); + + return list; + } + } + } + + public Version GetVersion(RootObject obj) + { + Version version; + if (!Version.TryParse(obj.tag_name, out version)) + { + return new Version(1, 0); + } + + return version; + } + + private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename) + { + Version version; + var versionString = obj.tag_name; + if (!Version.TryParse(versionString, out version)) + { + return null; + } + + if (version < minVersion) + { + return null; + } + + var asset = (obj.assets ?? new List()).FirstOrDefault(i => IsAsset(i, assetFilename, versionString)); + + if (asset == null) + { + return null; + } + + return new CheckForUpdateResult + { + AvailableVersion = version.ToString(), + IsUpdateAvailable = version > minVersion, + Package = new PackageVersionInfo + { + classification = obj.prerelease ? + (obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) : + PackageVersionClass.Release, + name = packageName, + sourceUrl = asset.browser_download_url, + targetFilename = targetFilename, + versionStr = version.ToString(), + requiredVersionStr = "1.0.0", + description = obj.body, + infoUrl = obj.html_url + } + }; + } + + private bool IsAsset(Asset asset, string assetFilename, string version) + { + var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty; + + assetFilename = assetFilename.Replace("{version}", version); + + if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1) + { + return true; + } + + return string.Equals(assetFilename, downloadFilename, StringComparison.OrdinalIgnoreCase); + } + + public class Uploader + { + public string login { get; set; } + public int id { get; set; } + public string avatar_url { get; set; } + public string gravatar_id { get; set; } + public string url { get; set; } + public string html_url { get; set; } + public string followers_url { get; set; } + public string following_url { get; set; } + public string gists_url { get; set; } + public string starred_url { get; set; } + public string subscriptions_url { get; set; } + public string organizations_url { get; set; } + public string repos_url { get; set; } + public string events_url { get; set; } + public string received_events_url { get; set; } + public string type { get; set; } + public bool site_admin { get; set; } + } + + public class Asset + { + public string url { get; set; } + public int id { get; set; } + public string name { get; set; } + public object label { get; set; } + public Uploader uploader { get; set; } + public string content_type { get; set; } + public string state { get; set; } + public int size { get; set; } + public int download_count { get; set; } + public string created_at { get; set; } + public string updated_at { get; set; } + public string browser_download_url { get; set; } + } + + public class Author + { + public string login { get; set; } + public int id { get; set; } + public string avatar_url { get; set; } + public string gravatar_id { get; set; } + public string url { get; set; } + public string html_url { get; set; } + public string followers_url { get; set; } + public string following_url { get; set; } + public string gists_url { get; set; } + public string starred_url { get; set; } + public string subscriptions_url { get; set; } + public string organizations_url { get; set; } + public string repos_url { get; set; } + public string events_url { get; set; } + public string received_events_url { get; set; } + public string type { get; set; } + public bool site_admin { get; set; } + } + + public class RootObject + { + public string url { get; set; } + public string assets_url { get; set; } + public string upload_url { get; set; } + public string html_url { get; set; } + public int id { get; set; } + public string tag_name { get; set; } + public string target_commitish { get; set; } + public string name { get; set; } + public bool draft { get; set; } + public Author author { get; set; } + public bool prerelease { get; set; } + public string created_at { get; set; } + public string published_at { get; set; } + public List assets { get; set; } + public string tarball_url { get; set; } + public string zipball_url { get; set; } + public string body { get; set; } + } + } +} diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs new file mode 100644 index 0000000000..dab38b27c2 --- /dev/null +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -0,0 +1,121 @@ +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Updates; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Updates +{ + public interface IInstallationManager : IDisposable + { + event EventHandler PackageInstalling; + event EventHandler PackageInstallationCompleted; + event EventHandler PackageInstallationFailed; + event EventHandler PackageInstallationCancelled; + + /// + /// The current installations + /// + List> CurrentInstallations { get; set; } + + /// + /// The completed installations + /// + IEnumerable CompletedInstallations { get; } + + /// + /// Occurs when [plugin uninstalled]. + /// + event EventHandler> PluginUninstalled; + + /// + /// Occurs when [plugin updated]. + /// + event EventHandler>> PluginUpdated; + + /// + /// Occurs when [plugin updated]. + /// + event EventHandler> PluginInstalled; + + /// + /// Gets all available packages. + /// + /// The cancellation token. + /// if set to true [with registration]. + /// Type of the package. + /// The application version. + /// Task{List{PackageInfo}}. + Task> GetAvailablePackages(CancellationToken cancellationToken, + bool withRegistration = true, + string packageType = null, + Version applicationVersion = null); + + /// + /// Gets all available packages from a static resource. + /// + /// The cancellation token. + /// Task{List{PackageInfo}}. + Task> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken); + + /// + /// Gets the package. + /// + /// The name. + /// The assembly guid + /// The classification. + /// The version. + /// Task{PackageVersionInfo}. + Task GetPackage(string name, string guid, PackageVersionClass classification, Version version); + + /// + /// Gets the latest compatible version. + /// + /// The name. + /// The assembly guid + /// The current server version. + /// The classification. + /// Task{PackageVersionInfo}. + Task GetLatestCompatibleVersion(string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release); + + /// + /// Gets the latest compatible version. + /// + /// The available packages. + /// The name. + /// The assembly guid + /// The current server version. + /// The classification. + /// PackageVersionInfo. + PackageVersionInfo GetLatestCompatibleVersion(IEnumerable availablePackages, string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release); + + /// + /// Gets the available plugin updates. + /// + /// The current server version. + /// if set to true [with auto update enabled]. + /// The cancellation token. + /// Task{IEnumerable{PackageVersionInfo}}. + Task> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken); + + /// + /// Installs the package. + /// + /// The package. + /// if set to true [is plugin]. + /// The progress. + /// The cancellation token. + /// Task. + /// package + Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress progress, CancellationToken cancellationToken); + + /// + /// Uninstalls a plugin + /// + /// The plugin. + /// + void UninstallPlugin(IPlugin plugin); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs new file mode 100644 index 0000000000..9dc8ead835 --- /dev/null +++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs @@ -0,0 +1,11 @@ +using MediaBrowser.Model.Updates; + +namespace MediaBrowser.Common.Updates +{ + public class InstallationEventArgs + { + public InstallationInfo InstallationInfo { get; set; } + + public PackageVersionInfo PackageVersionInfo { get; set; } + } +} diff --git a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs new file mode 100644 index 0000000000..69dc1ee982 --- /dev/null +++ b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs @@ -0,0 +1,9 @@ +using System; + +namespace MediaBrowser.Common.Updates +{ + public class InstallationFailedEventArgs : InstallationEventArgs + { + public Exception Exception { get; set; } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs new file mode 100644 index 0000000000..6dd8f02d8b --- /dev/null +++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs @@ -0,0 +1,14 @@ +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Dto; + + +namespace MediaBrowser.Controller.Authentication +{ + public class AuthenticationResult + { + public UserDto User { get; set; } + public SessionInfo SessionInfo { get; set; } + public string AccessToken { get; set; } + public string ServerId { get; set; } + } +} diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs new file mode 100644 index 0000000000..becb3ea627 --- /dev/null +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Users; + +namespace MediaBrowser.Controller.Authentication +{ + public interface IAuthenticationProvider + { + string Name { get; } + bool IsEnabled { get; } + Task Authenticate(string username, string password); + Task HasPassword(User user); + Task ChangePassword(User user, string newPassword); + } + + public interface IRequiresResolvedUser + { + Task Authenticate(string username, string password, User resolvedUser); + } + + public interface IHasNewUserPolicy + { + UserPolicy GetNewUserPolicy(); + } + + public class ProviderAuthenticationResult + { + public string Username { get; set; } + public string DisplayName { get; set; } + } +} diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs new file mode 100644 index 0000000000..9cd50db17a --- /dev/null +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -0,0 +1,94 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Querying; +using System; +using System.Linq; +using MediaBrowser.Model.Serialization; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Progress; + +namespace MediaBrowser.Controller.Channels +{ + public class Channel : Folder + { + public override bool IsVisible(User user) + { + if (user.Policy.BlockedChannels != null) + { + 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); + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override SourceType SourceType + { + get { return SourceType.Channel; } + } + + protected override QueryResult GetItemsInternal(InternalItemsQuery query) + { + try + { + query.Parent = this; + query.ChannelIds = new Guid[] { Id }; + + // Don't blow up here because it could cause parent screens with other content to fail + return ChannelManager.GetChannelItemsInternal(query, new SimpleProgress(), CancellationToken.None).Result; + } + catch + { + // Already logged at lower levels + return new QueryResult(); + } + } + + protected override string GetInternalMetadataPath(string basePath) + { + 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; + } + + internal static bool IsChannelVisible(BaseItem channelItem, User user) + { + var channel = ChannelManager.GetChannel(channelItem.ChannelId.ToString("")); + + return channel.IsVisible(user); + } + } +} diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs new file mode 100644 index 0000000000..0de2b9a0cb --- /dev/null +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -0,0 +1,82 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Controller.Channels +{ + public class ChannelItemInfo : IHasProviderIds + { + public string Name { get; set; } + + public string SeriesName { get; set; } + + public string Id { get; set; } + + public DateTime DateModified { get; set; } + + public ChannelItemType Type { get; set; } + + public string OfficialRating { get; set; } + + public string Overview { get; set; } + + public List Genres { get; set; } + public List Studios { get; set; } + public List Tags { get; set; } + + public List People { get; set; } + + public float? CommunityRating { get; set; } + + public long? RunTimeTicks { get; set; } + + public string ImageUrl { get; set; } + public string OriginalTitle { get; set; } + + public ChannelMediaType MediaType { get; set; } + public ChannelFolderType FolderType { get; set; } + + public ChannelMediaContentType ContentType { get; set; } + public ExtraType ExtraType { get; set; } + public List TrailerTypes { get; set; } + + public Dictionary ProviderIds { get; set; } + + public DateTime? PremiereDate { get; set; } + public int? ProductionYear { get; set; } + + public DateTime? DateCreated { get; set; } + + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + + public int? IndexNumber { get; set; } + public int? ParentIndexNumber { get; set; } + + public List MediaSources { get; set; } + + public string HomePageUrl { get; set; } + + public List Artists { get; set; } + + public List AlbumArtists { get; set; } + public bool IsLiveStream { get; set; } + public string Etag { get; set; } + + public ChannelItemInfo() + { + MediaSources = new List(); + TrailerTypes = new List(); + Genres = new List(); + Studios = new List(); + People = new List(); + Tags = new List(); + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + Artists = new List(); + AlbumArtists = new List(); + } + } +} diff --git a/MediaBrowser.Controller/Channels/ChannelItemResult.cs b/MediaBrowser.Controller/Channels/ChannelItemResult.cs new file mode 100644 index 0000000000..f888818118 --- /dev/null +++ b/MediaBrowser.Controller/Channels/ChannelItemResult.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Channels +{ + public class ChannelItemResult + { + public List Items { get; set; } + + public int? TotalRecordCount { get; set; } + + public ChannelItemResult() + { + Items = new List(); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/ChannelItemType.cs b/MediaBrowser.Controller/Channels/ChannelItemType.cs new file mode 100644 index 0000000000..184ce8a767 --- /dev/null +++ b/MediaBrowser.Controller/Channels/ChannelItemType.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Controller.Channels +{ + public enum ChannelItemType + { + Media = 0, + + Folder = 1 + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/ChannelParentalRating.cs b/MediaBrowser.Controller/Channels/ChannelParentalRating.cs new file mode 100644 index 0000000000..d9cc521b38 --- /dev/null +++ b/MediaBrowser.Controller/Channels/ChannelParentalRating.cs @@ -0,0 +1,15 @@ +namespace MediaBrowser.Controller.Channels +{ + public enum ChannelParentalRating + { + GeneralAudience = 0, + + UsPG = 1, + + UsPG13 = 2, + + UsR = 3, + + Adult = 4 + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs new file mode 100644 index 0000000000..c2a51654c7 --- /dev/null +++ b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Controller.Channels +{ + public class ChannelSearchInfo + { + public string SearchTerm { get; set; } + + public string UserId { get; set; } + } + + public class ChannelLatestMediaSearch + { + public string UserId { get; set; } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs new file mode 100644 index 0000000000..dc1d9b00a3 --- /dev/null +++ b/MediaBrowser.Controller/Channels/IChannel.cs @@ -0,0 +1,76 @@ +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Channels +{ + public interface IChannel + { + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Gets the description. + /// + /// The description. + string Description { get; } + + /// + /// Gets the data version. + /// + /// The data version. + string DataVersion { get; } + + /// + /// Gets the home page URL. + /// + /// The home page URL. + string HomePageUrl { get; } + + /// + /// Gets the parental rating. + /// + /// The parental rating. + ChannelParentalRating ParentalRating { get; } + + /// + /// Gets the channel information. + /// + /// ChannelFeatures. + InternalChannelFeatures GetChannelFeatures(); + + /// + /// Determines whether [is enabled for] [the specified user]. + /// + /// The user identifier. + /// true if [is enabled for] [the specified user]; otherwise, false. + bool IsEnabledFor(string userId); + + /// + /// Gets the channel items. + /// + /// The query. + /// The cancellation token. + /// Task{IEnumerable{ChannelItem}}. + Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken); + + /// + /// Gets the channel image. + /// + /// The type. + /// The cancellation token. + /// Task{DynamicImageInfo}. + Task GetChannelImage(ImageType type, CancellationToken cancellationToken); + + /// + /// Gets the supported channel images. + /// + /// IEnumerable{ImageType}. + IEnumerable GetSupportedChannelImages(); + } +} diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs new file mode 100644 index 0000000000..a9839e1fb9 --- /dev/null +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -0,0 +1,89 @@ +using System; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Querying; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Channels +{ + public interface IChannelManager + { + /// + /// Adds the parts. + /// + /// The channels. + void AddParts(IEnumerable channels); + + /// + /// Gets the channel features. + /// + /// The identifier. + /// ChannelFeatures. + ChannelFeatures GetChannelFeatures(string id); + + /// + /// Gets all channel features. + /// + /// IEnumerable{ChannelFeatures}. + ChannelFeatures[] GetAllChannelFeatures(); + + bool EnableMediaSourceDisplay(BaseItem item); + bool CanDelete(BaseItem item); + + Task DeleteItem(BaseItem item); + + /// + /// Gets the channel. + /// + /// The identifier. + /// Channel. + Channel GetChannel(string id); + + /// + /// Gets the channels internal. + /// + /// The query. + /// The cancellation token. + QueryResult GetChannelsInternal(ChannelQuery query); + + /// + /// Gets the channels. + /// + /// The query. + /// The cancellation token. + QueryResult GetChannels(ChannelQuery query); + + /// + /// Gets the latest media. + /// + Task> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken); + + /// + /// Gets the latest media. + /// + Task> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken); + + /// + /// Gets the channel items. + /// + Task> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken); + + /// + /// Gets the channel items internal. + /// + Task> GetChannelItemsInternal(InternalItemsQuery query, IProgress progress, CancellationToken cancellationToken); + + /// + /// Gets the channel item media sources. + /// + /// The item. + /// The cancellation token. + /// Task{IEnumerable{MediaSourceInfo}}. + IEnumerable GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken); + + bool EnableMediaProbe(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Channels/IHasCacheKey.cs b/MediaBrowser.Controller/Channels/IHasCacheKey.cs new file mode 100644 index 0000000000..6376d2f914 --- /dev/null +++ b/MediaBrowser.Controller/Channels/IHasCacheKey.cs @@ -0,0 +1,13 @@ + +namespace MediaBrowser.Controller.Channels +{ + public interface IHasCacheKey + { + /// + /// Gets the cache key. + /// + /// The user identifier. + /// System.String. + string GetCacheKey(string userId); + } +} diff --git a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs new file mode 100644 index 0000000000..a2c63586b1 --- /dev/null +++ b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Controller.Channels +{ + public interface IRequiresMediaInfoCallback + { + /// + /// Gets the channel item media information. + /// + Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs new file mode 100644 index 0000000000..bf9842eb4f --- /dev/null +++ b/MediaBrowser.Controller/Channels/ISearchableChannel.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Channels +{ + public interface ISearchableChannel + { + /// + /// Searches the specified search term. + /// + /// The search information. + /// The cancellation token. + /// Task{IEnumerable{ChannelItemInfo}}. + Task> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken); + } + + public interface ISupportsLatestMedia + { + /// + /// Gets the latest media. + /// + /// The request. + /// The cancellation token. + /// Task{IEnumerable{ChannelItemInfo}}. + Task> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken); + } + + public interface ISupportsDelete + { + bool CanDelete(BaseItem item); + Task DeleteItem(string id, CancellationToken cancellationToken); + } + + public interface IDisableMediaSourceDisplay + { + + } + + public interface ISupportsMediaProbe + { + + } + + public interface IHasFolderAttributes + { + string[] Attributes { get; } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs new file mode 100644 index 0000000000..976808aad9 --- /dev/null +++ b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs @@ -0,0 +1,61 @@ +using System; +using MediaBrowser.Model.Channels; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Channels +{ + public class InternalChannelFeatures + { + /// + /// Gets or sets the media types. + /// + /// The media types. + public List MediaTypes { get; set; } + + /// + /// Gets or sets the content types. + /// + /// The content types. + public List ContentTypes { get; set; } + + /// + /// Represents the maximum number of records the channel allows retrieving at a time + /// + public int? MaxPageSize { get; set; } + + /// + /// Gets or sets the default sort orders. + /// + /// The default sort orders. + public List DefaultSortFields { get; set; } + + /// + /// Indicates if a sort ascending/descending toggle is supported or not. + /// + public bool SupportsSortOrderToggle { get; set; } + /// + /// Gets or sets the automatic refresh levels. + /// + /// The automatic refresh levels. + public int? AutoRefreshLevels { get; set; } + + /// + /// Gets or sets the daily download limit. + /// + /// The daily download limit. + public int? DailyDownloadLimit { get; set; } + /// + /// Gets or sets a value indicating whether [supports downloading]. + /// + /// true if [supports downloading]; otherwise, false. + public bool SupportsContentDownloading { get; set; } + + public InternalChannelFeatures() + { + MediaTypes = new List(); + ContentTypes = new List(); + + DefaultSortFields = new List(); + } + } +} diff --git a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs new file mode 100644 index 0000000000..c69a1f6c32 --- /dev/null +++ b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs @@ -0,0 +1,21 @@ +using MediaBrowser.Model.Channels; +using System; + + +namespace MediaBrowser.Controller.Channels +{ + public class InternalChannelItemQuery + { + public string FolderId { get; set; } + + public Guid UserId { get; set; } + + public int? StartIndex { get; set; } + + public int? Limit { get; set; } + + public ChannelItemSortField? SortBy { get; set; } + + public bool SortDescending { get; set; } + } +} diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs new file mode 100644 index 0000000000..2a20eb365d --- /dev/null +++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Chapters +{ + /// + /// Interface IChapterManager + /// + public interface IChapterManager + { + /// + /// Gets the chapters. + /// + /// The item identifier. + /// List{ChapterInfo}. + + /// + /// Saves the chapters. + /// + void SaveChapters(string itemId, List chapters); + } +} diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs new file mode 100644 index 0000000000..727b487a79 --- /dev/null +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -0,0 +1,27 @@ +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Collections +{ + public class CollectionCreationOptions : IHasProviderIds + { + public string Name { get; set; } + + public Guid? ParentId { get; set; } + + public bool IsLocked { get; set; } + + public Dictionary ProviderIds { get; set; } + + public string[] ItemIdList { get; set; } + public Guid[] UserIds { get; set; } + + public CollectionCreationOptions() + { + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + ItemIdList = new string[] {}; + UserIds = new Guid[] {}; + } + } +} diff --git a/MediaBrowser.Controller/Collections/CollectionEvents.cs b/MediaBrowser.Controller/Collections/CollectionEvents.cs new file mode 100644 index 0000000000..80f66a444a --- /dev/null +++ b/MediaBrowser.Controller/Collections/CollectionEvents.cs @@ -0,0 +1,37 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Collections +{ + public class CollectionCreatedEventArgs : EventArgs + { + /// + /// Gets or sets the collection. + /// + /// The collection. + public BoxSet Collection { get; set; } + + /// + /// Gets or sets the options. + /// + /// The options. + public CollectionCreationOptions Options { get; set; } + } + + public class CollectionModifiedEventArgs : EventArgs + { + /// + /// Gets or sets the collection. + /// + /// The collection. + public BoxSet Collection { get; set; } + + /// + /// Gets or sets the items changed. + /// + /// The items changed. + public List ItemsChanged { get; set; } + } +} diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs new file mode 100644 index 0000000000..05bc927ba0 --- /dev/null +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -0,0 +1,57 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Collections +{ + public interface ICollectionManager + { + /// + /// Occurs when [collection created]. + /// + event EventHandler CollectionCreated; + + /// + /// Occurs when [items added to collection]. + /// + event EventHandler ItemsAddedToCollection; + + /// + /// Occurs when [items removed from collection]. + /// + event EventHandler ItemsRemovedFromCollection; + + /// + /// Creates the collection. + /// + /// The options. + BoxSet CreateCollection(CollectionCreationOptions options); + + /// + /// Adds to collection. + /// + /// The collection identifier. + /// The item ids. + void AddToCollection(Guid collectionId, IEnumerable itemIds); + + /// + /// Removes from collection. + /// + /// The collection identifier. + /// The item ids. + void RemoveFromCollection(Guid collectionId, IEnumerable itemIds); + + void AddToCollection(Guid collectionId, IEnumerable itemIds); + void RemoveFromCollection(Guid collectionId, IEnumerable itemIds); + + /// + /// Collapses the items within box sets. + /// + /// The items. + /// The user. + /// IEnumerable{BaseItem}. + IEnumerable CollapseItemsWithinBoxSets(IEnumerable items, User user); + } +} diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs new file mode 100644 index 0000000000..af57149322 --- /dev/null +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -0,0 +1,25 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; + +namespace MediaBrowser.Controller.Configuration +{ + /// + /// Interface IServerConfigurationManager + /// + public interface IServerConfigurationManager : IConfigurationManager + { + /// + /// Gets the application paths. + /// + /// The application paths. + IServerApplicationPaths ApplicationPaths { get; } + + /// + /// Gets the configuration. + /// + /// The configuration. + ServerConfiguration Configuration { get; } + + bool SetOptimalValues(); + } +} diff --git a/MediaBrowser.Controller/Connect/IConnectManager.cs b/MediaBrowser.Controller/Connect/IConnectManager.cs new file mode 100644 index 0000000000..8ac61bf2b1 --- /dev/null +++ b/MediaBrowser.Controller/Connect/IConnectManager.cs @@ -0,0 +1,45 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Connect; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Connect +{ + public interface IConnectManager + { + /// + /// Gets the wan API address. + /// + /// The wan API address. + string WanApiAddress { get; } + + /// + /// Links the user. + /// + /// The user identifier. + /// The connect username. + /// Task. + Task LinkUser(string userId, string connectUsername); + + /// + /// Removes the link. + /// + /// The user identifier. + /// Task. + Task RemoveConnect(string userId); + + User GetUserFromExchangeToken(string token); + + /// + /// Authenticates the specified username. + /// + Task Authenticate(string username, string password, string passwordMd5); + + /// + /// Determines whether [is authorization token valid] [the specified token]. + /// + /// The token. + /// true if [is authorization token valid] [the specified token]; otherwise, false. + bool IsAuthorizationTokenValid(string token); + } +} diff --git a/MediaBrowser.Controller/Connect/UserLinkResult.cs b/MediaBrowser.Controller/Connect/UserLinkResult.cs new file mode 100644 index 0000000000..16ebfc70a3 --- /dev/null +++ b/MediaBrowser.Controller/Connect/UserLinkResult.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Controller.Connect +{ + public class UserLinkResult + { + public bool IsPending { get; set; } + public bool IsNewUserInvitation { get; set; } + public string GuestDisplayName { get; set; } + } +} diff --git a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs new file mode 100644 index 0000000000..b3f3bb9025 --- /dev/null +++ b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs @@ -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; } + } +} diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs new file mode 100644 index 0000000000..d29fb8ded0 --- /dev/null +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -0,0 +1,73 @@ +using MediaBrowser.Model.Devices; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Session; +using System; +using System.IO; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Devices +{ + public interface IDeviceManager + { + /// + /// Occurs when [camera image uploaded]. + /// + event EventHandler> CameraImageUploaded; + + /// + /// Saves the capabilities. + /// + /// The reported identifier. + /// The capabilities. + /// Task. + void SaveCapabilities(string reportedId, ClientCapabilities capabilities); + + /// + /// Gets the capabilities. + /// + /// The reported identifier. + /// ClientCapabilities. + ClientCapabilities GetCapabilities(string reportedId); + + /// + /// Gets the device information. + /// + /// The identifier. + /// DeviceInfo. + DeviceInfo GetDevice(string id); + + /// + /// Gets the devices. + /// + /// The query. + /// IEnumerable<DeviceInfo>. + QueryResult GetDevices(DeviceQuery query); + + /// + /// Gets the upload history. + /// + /// The device identifier. + /// ContentUploadHistory. + ContentUploadHistory GetCameraUploadHistory(string deviceId); + + /// + /// Accepts the upload. + /// + /// The device identifier. + /// The stream. + /// The file. + /// Task. + Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file); + + /// + /// Determines whether this instance [can access device] the specified user identifier. + /// + bool CanAccessDevice(User user, string deviceId); + + void UpdateDeviceOptions(string deviceId, DeviceOptions options); + DeviceOptions GetDeviceOptions(string deviceId); + event EventHandler>> DeviceOptionsUpdated; + } +} diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs new file mode 100644 index 0000000000..2f64cd1946 --- /dev/null +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -0,0 +1,76 @@ +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Dlna; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Dlna +{ + public interface IDlnaManager + { + /// + /// Gets the profile infos. + /// + /// IEnumerable{DeviceProfileInfo}. + IEnumerable GetProfileInfos(); + + /// + /// Gets the profile. + /// + /// The headers. + /// DeviceProfile. + DeviceProfile GetProfile(IDictionary headers); + + /// + /// Gets the default profile. + /// + /// DeviceProfile. + DeviceProfile GetDefaultProfile(); + + /// + /// Creates the profile. + /// + /// The profile. + void CreateProfile(DeviceProfile profile); + + /// + /// Updates the profile. + /// + /// The profile. + void UpdateProfile(DeviceProfile profile); + + /// + /// Deletes the profile. + /// + /// The identifier. + void DeleteProfile(string id); + + /// + /// Gets the profile. + /// + /// The identifier. + /// DeviceProfile. + DeviceProfile GetProfile(string id); + + /// + /// Gets the profile. + /// + /// The device information. + /// DeviceProfile. + DeviceProfile GetProfile(DeviceIdentification deviceInfo); + + /// + /// Gets the server description XML. + /// + /// The headers. + /// The server uu identifier. + /// The server address. + /// System.String. + string GetServerDescriptionXml(IDictionary headers, string serverUuId, string serverAddress); + + /// + /// Gets the icon. + /// + /// The filename. + /// DlnaIconResponse. + ImageStream GetIcon(string filename); + } +} diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs new file mode 100644 index 0000000000..757448eb26 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -0,0 +1,49 @@ +using System; +using MediaBrowser.Model.Drawing; + +namespace MediaBrowser.Controller.Drawing +{ + public interface IImageEncoder + { + /// + /// Gets the supported input formats. + /// + /// The supported input formats. + string[] SupportedInputFormats { get; } + /// + /// Gets the supported output formats. + /// + /// The supported output formats. + ImageFormat[] SupportedOutputFormats { get; } + + /// + /// Encodes the image. + /// + string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat); + + /// + /// Creates the image collage. + /// + /// The options. + void CreateImageCollage(ImageCollageOptions options); + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Gets a value indicating whether [supports image collage creation]. + /// + /// true if [supports image collage creation]; otherwise, false. + bool SupportsImageCollageCreation { get; } + + /// + /// Gets a value indicating whether [supports image encoding]. + /// + /// true if [supports image encoding]; otherwise, false. + bool SupportsImageEncoding { get; } + + ImageSize GetImageSize(string path); + } +} diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs new file mode 100644 index 0000000000..fdf10e2238 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -0,0 +1,118 @@ +using System; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Drawing +{ + /// + /// Interface IImageProcessor + /// + public interface IImageProcessor + { + /// + /// Gets the supported input formats. + /// + /// The supported input formats. + string[] SupportedInputFormats { get; } + + /// + /// Gets the image enhancers. + /// + /// The image enhancers. + IImageEnhancer[] ImageEnhancers { get; } + + ImageSize GetImageSize(string path); + + /// + /// Gets the size of the image. + /// + /// The information. + /// ImageSize. + ImageSize GetImageSize(BaseItem item, ItemImageInfo info); + + ImageSize GetImageSize(BaseItem item, ItemImageInfo info, bool allowSlowMethods, bool updateItem); + + /// + /// Adds the parts. + /// + /// The enhancers. + void AddParts(IEnumerable enhancers); + + /// + /// Gets the supported enhancers. + /// + /// The item. + /// Type of the image. + /// IEnumerable{IImageEnhancer}. + IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType); + + /// + /// Gets the image cache tag. + /// + /// The item. + /// The image. + /// Guid. + string GetImageCacheTag(BaseItem item, ItemImageInfo image); + string GetImageCacheTag(BaseItem item, ChapterInfo info); + + /// + /// Gets the image cache tag. + /// + /// The item. + /// The image. + /// The image enhancers. + /// Guid. + string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers); + + /// + /// Processes the image. + /// + /// The options. + /// To stream. + /// Task. + Task ProcessImage(ImageProcessingOptions options, Stream toStream); + + /// + /// Processes the image. + /// + /// The options. + /// Task. + Task> ProcessImage(ImageProcessingOptions options); + + /// + /// Gets the enhanced image. + /// + /// The item. + /// Type of the image. + /// Index of the image. + /// Task{System.String}. + Task GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex); + + /// + /// Gets the supported image output formats. + /// + /// ImageOutputFormat[]. + ImageFormat[] GetSupportedImageOutputFormats(); + + /// + /// Creates the image collage. + /// + /// The options. + void CreateImageCollage(ImageCollageOptions options); + + /// + /// Gets a value indicating whether [supports image collage creation]. + /// + /// true if [supports image collage creation]; otherwise, false. + bool SupportsImageCollageCreation { get; } + + IImageEncoder ImageEncoder { get; set; } + + bool SupportsTransparency(string path); + } +} diff --git a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs new file mode 100644 index 0000000000..92a7f5ac92 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs @@ -0,0 +1,27 @@ + +namespace MediaBrowser.Controller.Drawing +{ + public class ImageCollageOptions + { + /// + /// Gets or sets the input paths. + /// + /// The input paths. + public string[] InputPaths { get; set; } + /// + /// Gets or sets the output path. + /// + /// The output path. + public string OutputPath { get; set; } + /// + /// Gets or sets the width. + /// + /// The width. + public int Width { get; set; } + /// + /// Gets or sets the height. + /// + /// The height. + public int Height { get; set; } + } +} diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs new file mode 100644 index 0000000000..6fb9f256e3 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -0,0 +1,72 @@ +using System; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Drawing +{ + public static class ImageHelper + { + public static ImageSize GetNewImageSize(ImageProcessingOptions options, ImageSize? originalImageSize) + { + if (originalImageSize.HasValue) + { + // Determine the output size based on incoming parameters + var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width ?? 0, options.Height ?? 0, options.MaxWidth ?? 0, options.MaxHeight ?? 0); + + return newSize; + } + return GetSizeEstimate(options); + } + + public static IImageProcessor ImageProcessor { get; set; } + + private static ImageSize GetSizeEstimate(ImageProcessingOptions options) + { + if (options.Width.HasValue && options.Height.HasValue) + { + return new ImageSize(options.Width.Value, options.Height.Value); + } + + var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item); + + var width = options.Width ?? options.MaxWidth; + + if (width.HasValue) + { + var heightValue = width.Value / aspect; + return new ImageSize(width.Value, heightValue); + } + + var height = options.Height ?? options.MaxHeight ?? 200; + var widthValue = aspect * height; + return new ImageSize(widthValue, height); + } + + private static double GetEstimatedAspectRatio(ImageType type, BaseItem item) + { + switch (type) + { + case ImageType.Art: + case ImageType.Backdrop: + case ImageType.Chapter: + case ImageType.Screenshot: + case ImageType.Thumb: + return 1.78; + case ImageType.Banner: + return 5.4; + case ImageType.Box: + case ImageType.BoxRear: + case ImageType.Disc: + case ImageType.Menu: + return 1; + case ImageType.Logo: + return 2.58; + case ImageType.Primary: + return item.GetDefaultPrimaryImageAspectRatio(); + default: + return 1; + } + } + } +} diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs new file mode 100644 index 0000000000..ffc3e6cc02 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -0,0 +1,114 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Drawing; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Controller.Drawing +{ + public class ImageProcessingOptions + { + public ImageProcessingOptions() + { + RequiresAutoOrientation = true; + } + + public Guid ItemId { get; set; } + public BaseItem Item { get; set; } + + public ItemImageInfo Image { get; set; } + + public int ImageIndex { get; set; } + + public bool CropWhiteSpace { get; set; } + + public int? Width { get; set; } + + public int? Height { get; set; } + + public int? MaxWidth { get; set; } + + public int? MaxHeight { get; set; } + + public int Quality { get; set; } + + public IImageEnhancer[] Enhancers { get; set; } + + public ImageFormat[] SupportedOutputFormats { get; set; } + + public bool AddPlayedIndicator { get; set; } + + public int? UnplayedCount { get; set; } + public int? Blur { get; set; } + + public double PercentPlayed { get; set; } + + public string BackgroundColor { get; set; } + public string ForegroundLayer { get; set; } + public bool RequiresAutoOrientation { get; set; } + + private bool HasDefaultOptions(string originalImagePath) + { + return HasDefaultOptionsWithoutSize(originalImagePath) && + !Width.HasValue && + !Height.HasValue && + !MaxWidth.HasValue && + !MaxHeight.HasValue; + } + + public bool HasDefaultOptions(string originalImagePath, ImageSize? size) + { + if (!size.HasValue) + { + return HasDefaultOptions(originalImagePath); + } + + if (!HasDefaultOptionsWithoutSize(originalImagePath)) + { + return false; + } + + var sizeValue = size.Value; + + if (Width.HasValue && !sizeValue.Width.Equals(Width.Value)) + { + return false; + } + if (Height.HasValue && !sizeValue.Height.Equals(Height.Value)) + { + return false; + } + if (MaxWidth.HasValue && sizeValue.Width > MaxWidth.Value) + { + return false; + } + if (MaxHeight.HasValue && sizeValue.Height > MaxHeight.Value) + { + return false; + } + + return true; + } + + private bool HasDefaultOptionsWithoutSize(string originalImagePath) + { + return (Quality >= 90) && + IsFormatSupported(originalImagePath) && + !AddPlayedIndicator && + PercentPlayed.Equals(0) && + !UnplayedCount.HasValue && + !Blur.HasValue && + !CropWhiteSpace && + string.IsNullOrEmpty(BackgroundColor) && + string.IsNullOrEmpty(ForegroundLayer); + } + + private bool IsFormatSupported(string originalImagePath) + { + var ext = Path.GetExtension(originalImagePath); + return SupportedOutputFormats.Any(outputFormat => string.Equals(ext, "." + outputFormat, StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs new file mode 100644 index 0000000000..948219bf55 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs @@ -0,0 +1,25 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Drawing +{ + public static class ImageProcessorExtensions + { + public static string GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType) + { + return processor.GetImageCacheTag(item, imageType, 0); + } + + public static string GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType, int imageIndex) + { + var imageInfo = item.GetImageInfo(imageType, imageIndex); + + if (imageInfo == null) + { + return null; + } + + return processor.GetImageCacheTag(item, imageInfo); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Drawing/ImageStream.cs b/MediaBrowser.Controller/Drawing/ImageStream.cs new file mode 100644 index 0000000000..353abaca33 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageStream.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Model.Drawing; +using System; +using System.IO; + +namespace MediaBrowser.Controller.Drawing +{ + public class ImageStream : IDisposable + { + /// + /// Gets or sets the stream. + /// + /// The stream. + public Stream Stream { get; set; } + /// + /// Gets or sets the format. + /// + /// The format. + public ImageFormat Format { get; set; } + + public void Dispose() + { + if (Stream != null) + { + Stream.Dispose(); + } + } + } +} diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs new file mode 100644 index 0000000000..b5ce090288 --- /dev/null +++ b/MediaBrowser.Controller/Dto/DtoOptions.cs @@ -0,0 +1,72 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; +using System.Linq; + +namespace MediaBrowser.Controller.Dto +{ + public class DtoOptions + { + private static readonly ItemFields[] DefaultExcludedFields = new [] + { + ItemFields.SeasonUserData, + ItemFields.RefreshState + }; + + public ItemFields[] Fields { get; set; } + public ImageType[] ImageTypes { get; set; } + public int ImageTypeLimit { get; set; } + public bool EnableImages { get; set; } + public bool AddProgramRecordingInfo { get; set; } + public bool EnableUserData { get; set; } + public bool AddCurrentProgram { get; set; } + + public DtoOptions() + : this(true) + { + } + + private static readonly ImageType[] AllImageTypes = Enum.GetNames(typeof(ImageType)) + .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true)) + .ToArray(); + + private static readonly ItemFields[] AllItemFields = Enum.GetNames(typeof(ItemFields)) + .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) + .Except(DefaultExcludedFields) + .ToArray(); + + public bool ContainsField(ItemFields field) + { + return AllItemFields.Contains(field); + } + + public DtoOptions(bool allFields) + { + ImageTypeLimit = int.MaxValue; + EnableImages = true; + EnableUserData = true; + AddCurrentProgram = true; + + if (allFields) + { + Fields = AllItemFields; + } + else + { + Fields = new ItemFields[] { }; + } + + ImageTypes = AllImageTypes; + } + + public int GetImageLimit(ImageType type) + { + if (EnableImages && ImageTypes.Contains(type)) + { + return ImageTypeLimit; + } + + return 0; + } + } +} diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs new file mode 100644 index 0000000000..219b367891 --- /dev/null +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -0,0 +1,70 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Querying; +using System.Collections.Generic; +using MediaBrowser.Controller.Sync; + +namespace MediaBrowser.Controller.Dto +{ + /// + /// Interface IDtoService + /// + public interface IDtoService + { + /// + /// Gets the dto id. + /// + /// The item. + /// System.String. + string GetDtoId(BaseItem item); + + /// + /// Attaches the primary image aspect ratio. + /// + /// The dto. + /// The item. + void AttachPrimaryImageAspectRatio(IItemDto dto, BaseItem item); + + /// + /// Gets the primary image aspect ratio. + /// + /// The item. + /// System.Nullable<System.Double>. + double? GetPrimaryImageAspectRatio(BaseItem item); + + /// + /// Gets the base item dto. + /// + /// The item. + /// The fields. + /// The user. + /// The owner. + BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null); + + /// + /// Gets the base item dto. + /// + /// The item. + /// The options. + /// The user. + /// The owner. + /// BaseItemDto. + BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null); + + /// + /// Gets the base item dtos. + /// + /// The items. + /// The options. + /// The user. + /// The owner. + BaseItemDto[] GetBaseItemDtos(BaseItem[] items, DtoOptions options, User user = null, BaseItem owner = null); + + BaseItemDto[] GetBaseItemDtos(List items, DtoOptions options, User user = null, BaseItem owner = null); + + /// + /// Gets the item by name dto. + /// + BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List taggedItems, User user = null); + } +} diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs new file mode 100644 index 0000000000..4f4b3483cf --- /dev/null +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -0,0 +1,219 @@ +using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Library; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Controller.Entities +{ + /// + /// Specialized folder that can have items added to it's children by external entities. + /// Used for our RootFolder so plug-ins can add items. + /// + public class AggregateFolder : Folder + { + public AggregateFolder() + { + PhysicalLocationsList = new string[] { }; + } + + [IgnoreDataMember] + public override bool IsPhysicalRoot + { + get { return true; } + } + + public override bool CanDelete() + { + return false; + } + + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + + /// + /// The _virtual children + /// + private readonly ConcurrentBag _virtualChildren = new ConcurrentBag(); + + /// + /// Gets the virtual children. + /// + /// The virtual children. + public ConcurrentBag VirtualChildren + { + get { return _virtualChildren; } + } + + [IgnoreDataMember] + public override string[] PhysicalLocations + { + get + { + return PhysicalLocationsList; + } + } + + public string[] PhysicalLocationsList { get; set; } + + protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService) + { + return CreateResolveArgs(directoryService, true).FileSystemChildren; + } + + private Guid[] _childrenIds = null; + private readonly object _childIdsLock = new object(); + protected override List LoadChildren() + { + lock (_childIdsLock) + { + if (_childrenIds == null || _childrenIds.Length == 0) + { + var list = base.LoadChildren(); + _childrenIds = list.Select(i => i.Id).ToArray(); + return list; + } + + return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList(); + } + } + + private void ClearCache() + { + lock (_childIdsLock) + { + _childrenIds = null; + } + } + + private bool _requiresRefresh; + public override bool RequiresRefresh() + { + var changed = base.RequiresRefresh() || _requiresRefresh; + + if (!changed) + { + var locations = PhysicalLocations; + + var newLocations = CreateResolveArgs(new DirectoryService(Logger, FileSystem), false).PhysicalLocations; + + if (!locations.SequenceEqual(newLocations)) + { + changed = true; + } + } + + return changed; + } + + public override bool BeforeMetadataRefresh(bool replaceAllMetdata) + { + ClearCache(); + + var changed = base.BeforeMetadataRefresh(replaceAllMetdata) || _requiresRefresh; + _requiresRefresh = false; + return changed; + } + + private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations) + { + ClearCache(); + + var path = ContainingFolderPath; + + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) + { + FileInfo = FileSystem.GetDirectoryInfo(path), + Path = path + }; + + // Gather child folder and files + if (args.IsDirectory) + { + // When resolving the root, we need it's grandchildren (children of user views) + var flattenFolderDepth = 2; + + var files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, CollectionFolder.ApplicationHost, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: true); + + // Need to remove subpaths that may have been resolved from shortcuts + // Example: if \\server\movies exists, then strip out \\server\movies\action + files = LibraryManager.NormalizeRootPathList(files).ToArray(); + + args.FileSystemChildren = files; + } + + _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations); + if (setPhysicalLocations) + { + PhysicalLocationsList = args.PhysicalLocations; + } + + return args; + } + + protected override IEnumerable GetNonCachedChildren(IDirectoryService directoryService) + { + return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren); + } + + protected override async Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + { + ClearCache(); + + await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService) + .ConfigureAwait(false); + + ClearCache(); + } + + /// + /// Adds the virtual child. + /// + /// The child. + /// + public void AddVirtualChild(BaseItem child) + { + if (child == null) + { + throw new ArgumentNullException(); + } + + _virtualChildren.Add(child); + } + + /// + /// Finds the virtual child. + /// + /// The id. + /// BaseItem. + /// id + public BaseItem FindVirtualChild(Guid id) + { + if (id.Equals(Guid.Empty)) + { + throw new ArgumentNullException("id"); + } + + foreach (var child in _virtualChildren) + { + if (child.Id == id) + { + return child; + } + } + return null; + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs new file mode 100644 index 0000000000..d07e31d8a4 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -0,0 +1,216 @@ +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Controller.Entities.Audio +{ + /// + /// Class Audio + /// + public class Audio : BaseItem, + IHasAlbumArtist, + IHasArtist, + IHasMusicGenres, + IHasLookupInfo, + IHasMediaSources + { + /// + /// Gets or sets the artist. + /// + /// The artist. + [IgnoreDataMember] + public string[] Artists { get; set; } + + [IgnoreDataMember] + public string[] AlbumArtists { get; set; } + + public Audio() + { + Artists = new string[] {}; + AlbumArtists = new string[] {}; + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + return 1; + } + + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return false; } + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get { return true; } + } + + [IgnoreDataMember] + protected override bool SupportsOwnedItems + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override Folder LatestItemsIndexContainer + { + get + { + return AlbumEntity; + } + } + + public override bool CanDownload() + { + return IsFileProtocol; + } + + [IgnoreDataMember] + public string[] AllArtists + { + get + { + var list = new string[AlbumArtists.Length + Artists.Length]; + + var index = 0; + foreach (var artist in AlbumArtists) + { + list[index] = artist; + index++; + } + foreach (var artist in Artists) + { + list[index] = artist; + index++; + } + + return list; + + } + } + + [IgnoreDataMember] + public MusicAlbum AlbumEntity + { + get { return FindParent(); } + } + + /// + /// Gets the type of the media. + /// + /// The type of the media. + [IgnoreDataMember] + public override string MediaType + { + get + { + return Model.Entities.MediaType.Audio; + } + } + + /// + /// Creates the name of the sort. + /// + /// System.String. + protected override string CreateSortName() + { + return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "") + + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; + } + + public override List GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty; + + + if (ParentIndexNumber.HasValue) + { + songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; + } + songKey += Name; + + if (!string.IsNullOrEmpty(Album)) + { + songKey = Album + "-" + songKey; + } + + var albumArtist = AlbumArtists.Length == 0 ? null : AlbumArtists[0]; + if (!string.IsNullOrEmpty(albumArtist)) + { + songKey = albumArtist + "-" + songKey; + } + + list.Insert(0, songKey); + + return list; + } + + public override UnratedItem GetBlockUnratedType() + { + if (SourceType == SourceType.Library) + { + return UnratedItem.Music; + } + return base.GetBlockUnratedType(); + } + + public List GetMediaStreams(MediaStreamType type) + { + return MediaSourceManager.GetMediaStreams(new MediaStreamQuery + { + ItemId = Id, + Type = type + }); + } + + public SongInfo GetLookupInfo() + { + var info = GetItemLookupInfo(); + + info.AlbumArtists = AlbumArtists; + info.Album = Album; + info.Artists = Artists; + + return info; + } + + protected override List> GetAllItemsForMediaSources() + { + var list = new List>(); + list.Add(new Tuple(this, MediaSourceType.Default)); + return list; + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs new file mode 100644 index 0000000000..b2dedada47 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs @@ -0,0 +1,15 @@ + +namespace MediaBrowser.Controller.Entities.Audio +{ + public interface IHasAlbumArtist + { + string[] AlbumArtists { get; set; } + } + + public interface IHasArtist + { + string[] AllArtists { get; } + + string[] Artists { get; set; } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs new file mode 100644 index 0000000000..2200d4b759 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Entities.Audio +{ + public interface IHasMusicGenres + { + string[] Genres { get; } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs new file mode 100644 index 0000000000..48b5c64b21 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -0,0 +1,272 @@ +using System; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Serialization; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Controller.Entities.Audio +{ + /// + /// Class MusicAlbum + /// + public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo, IMetadataContainer + { + public string[] AlbumArtists { get; set; } + public string[] Artists { get; set; } + + public MusicAlbum() + { + Artists = new string[] {}; + AlbumArtists = new string[] {}; + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get { return true; } + } + + [IgnoreDataMember] + public MusicArtist MusicArtist + { + get { return GetMusicArtist(new DtoOptions(true)); } + } + + public MusicArtist GetMusicArtist(DtoOptions options) + { + var parents = GetParents(); + foreach (var parent in parents) + { + var artist = parent as MusicArtist; + if (artist != null) + { + return artist; + } + } + + var name = AlbumArtist; + if (!string.IsNullOrEmpty(name)) + { + return LibraryManager.GetArtist(name, options); + } + return null; + } + + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + + [IgnoreDataMember] + public string[] AllArtists + { + get + { + var list = new string[AlbumArtists.Length + Artists.Length]; + + var index = 0; + foreach (var artist in AlbumArtists) + { + list[index] = artist; + index++; + } + foreach (var artist in Artists) + { + list[index] = artist; + index++; + } + + return list; + } + } + + [IgnoreDataMember] + public string AlbumArtist + { + get { return AlbumArtists.Length == 0 ? null : AlbumArtists[0]; } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return false; } + } + + /// + /// Gets the tracks. + /// + /// The tracks. + [IgnoreDataMember] + public IEnumerable Tracks + { + get + { + return GetRecursiveChildren(i => i is Audio); + } + } + + protected override IEnumerable GetEligibleChildrenForRecursiveChildren(User user) + { + return Tracks; + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + return 1; + } + + public override List GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var albumArtist = AlbumArtist; + if (!string.IsNullOrEmpty(albumArtist)) + { + list.Insert(0, albumArtist + "-" + Name); + } + + var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum); + + if (!string.IsNullOrEmpty(id)) + { + list.Insert(0, "MusicAlbum-Musicbrainz-" + id); + } + + id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + if (!string.IsNullOrEmpty(id)) + { + list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id); + } + + return list; + } + + protected override bool GetBlockUnratedValue(UserPolicy config) + { + return config.BlockUnratedItems.Contains(UnratedItem.Music); + } + + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + + public AlbumInfo GetLookupInfo() + { + var id = GetItemLookupInfo(); + + id.AlbumArtists = AlbumArtists; + + var artist = GetMusicArtist(new DtoOptions(false)); + + if (artist != null) + { + id.ArtistProviderIds = artist.ProviderIds; + } + + id.SongInfos = GetRecursiveChildren(i => i is Audio) + .Cast