diff options
Diffstat (limited to 'MediaBrowser.Common')
24 files changed, 1869 insertions, 0 deletions
diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs new file mode 100644 index 000000000..310e2aa63 --- /dev/null +++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs @@ -0,0 +1,18 @@ +using System; + +namespace MediaBrowser.Common.Configuration +{ + public class ConfigurationUpdateEventArgs : EventArgs + { + /// <summary> + /// Gets or sets the key. + /// </summary> + /// <value>The key.</value> + public string Key { get; set; } + /// <summary> + /// Gets or sets the new configuration. + /// </summary> + /// <value>The new configuration.</value> + public object NewConfiguration { get; set; } + } +} diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs new file mode 100644 index 000000000..c6256bc5a --- /dev/null +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -0,0 +1,84 @@ + +namespace MediaBrowser.Common.Configuration +{ + /// <summary> + /// Interface IApplicationPaths + /// </summary> + public interface IApplicationPaths + { + /// <summary> + /// Gets the path to the program data folder + /// </summary> + /// <value>The program data path.</value> + string ProgramDataPath { get; } + + /// <summary> + /// Gets the path to the program system folder + /// </summary> + /// <value>The program data path.</value> + string ProgramSystemPath { get; } + + /// <summary> + /// Gets the folder path to the data directory + /// </summary> + /// <value>The data directory.</value> + string DataPath { get; } + + /// <summary> + /// Gets the image cache path. + /// </summary> + /// <value>The image cache path.</value> + string ImageCachePath { get; } + + /// <summary> + /// Gets the path to the plugin directory + /// </summary> + /// <value>The plugins path.</value> + string PluginsPath { get; } + + /// <summary> + /// Gets the path to the plugin configurations directory + /// </summary> + /// <value>The plugin configurations path.</value> + string PluginConfigurationsPath { get; } + + /// <summary> + /// Gets the path to where temporary update files will be stored + /// </summary> + /// <value>The plugin configurations path.</value> + string TempUpdatePath { get; } + + /// <summary> + /// Gets the path to the log directory + /// </summary> + /// <value>The log directory path.</value> + string LogDirectoryPath { get; } + + /// <summary> + /// Gets the path to the application configuration root directory + /// </summary> + /// <value>The configuration directory path.</value> + string ConfigurationDirectoryPath { get; } + + /// <summary> + /// Gets the path to the system configuration file + /// </summary> + /// <value>The system configuration file path.</value> + string SystemConfigurationFilePath { get; } + + /// <summary> + /// Gets the folder path to the cache directory + /// </summary> + /// <value>The cache directory.</value> + string CachePath { get; } + + /// <summary> + /// Gets the folder path to the temp directory within the cache folder + /// </summary> + /// <value>The temp directory.</value> + 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 000000000..6ed638536 --- /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<ConfigurationStore> 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 000000000..d826a3ee7 --- /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 + { + /// <summary> + /// Occurs when [configuration updating]. + /// </summary> + event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating; + + /// <summary> + /// Occurs when [configuration updated]. + /// </summary> + event EventHandler<EventArgs> ConfigurationUpdated; + + /// <summary> + /// Occurs when [named configuration updated]. + /// </summary> + event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated; + + /// <summary> + /// Gets or sets the application paths. + /// </summary> + /// <value>The application paths.</value> + IApplicationPaths CommonApplicationPaths { get; } + + /// <summary> + /// Gets the configuration. + /// </summary> + /// <value>The configuration.</value> + BaseApplicationConfiguration CommonConfiguration { get; } + + /// <summary> + /// Saves the configuration. + /// </summary> + void SaveConfiguration(); + + /// <summary> + /// Replaces the configuration. + /// </summary> + /// <param name="newConfiguration">The new configuration.</param> + void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration); + + /// <summary> + /// Gets the configuration. + /// </summary> + /// <param name="key">The key.</param> + /// <returns>System.Object.</returns> + object GetConfiguration(string key); + + /// <summary> + /// Gets the type of the configuration. + /// </summary> + /// <param name="key">The key.</param> + /// <returns>Type.</returns> + Type GetConfigurationType(string key); + + /// <summary> + /// Saves the configuration. + /// </summary> + /// <param name="key">The key.</param> + /// <param name="configuration">The configuration.</param> + void SaveConfiguration(string key, object configuration); + + /// <summary> + /// Adds the parts. + /// </summary> + /// <param name="factories">The factories.</param> + void AddParts(IEnumerable<IConfigurationFactory> factories); + } + + public static class ConfigurationManagerExtensions + { + public static T GetConfiguration<T>(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 000000000..2bb52f0ae --- /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 +{ + /// <summary> + /// Class EventHelper + /// </summary> + public static class EventHelper + { + /// <summary> + /// Fires the event. + /// </summary> + /// <param name="handler">The handler.</param> + /// <param name="sender">The sender.</param> + /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param> + /// <param name="logger">The logger.</param> + 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); + } + }); + } + } + + /// <summary> + /// Queues the event. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handler">The handler.</param> + /// <param name="sender">The sender.</param> + /// <param name="args">The args.</param> + /// <param name="logger">The logger.</param> + public static void QueueEventIfNotNull<T>(EventHandler<T> 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); + } + }); + } + } + + /// <summary> + /// Fires the event. + /// </summary> + /// <param name="handler">The handler.</param> + /// <param name="sender">The sender.</param> + /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param> + /// <param name="logger">The logger.</param> + 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); + } + } + } + + /// <summary> + /// Fires the event. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handler">The handler.</param> + /// <param name="sender">The sender.</param> + /// <param name="args">The args.</param> + /// <param name="logger">The logger.</param> + public static void FireEventIfNotNull<T>(EventHandler<T> 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 000000000..d7f4424fa --- /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 +{ + /// <summary> + /// Class BaseExtensions + /// </summary> + public static class BaseExtensions + { + public static ICryptoProvider CryptographyProvider { get; set; } + + /// <summary> + /// Strips the HTML. + /// </summary> + /// <param name="htmlString">The HTML string.</param> + /// <returns>System.String.</returns> + 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(); + } + + /// <summary> + /// Gets the M d5. + /// </summary> + /// <param name="str">The STR.</param> + /// <returns>Guid.</returns> + public static Guid GetMD5(this string str) + { + return CryptographyProvider.GetMD5(str); + } + + /// <summary> + /// Gets the MB id. + /// </summary> + /// <param name="str">The STR.</param> + /// <param name="type">The type.</param> + /// <returns>Guid.</returns> + /// <exception cref="System.ArgumentNullException">type</exception> + [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 000000000..89e20b1b4 --- /dev/null +++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs @@ -0,0 +1,63 @@ +using System; + +namespace MediaBrowser.Common.Extensions +{ + /// <summary> + /// Class ResourceNotFoundException + /// </summary> + public class ResourceNotFoundException : Exception + { + /// <summary> + /// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class. + /// </summary> + public ResourceNotFoundException() + { + + } + + /// <summary> + /// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class. + /// </summary> + /// <param name="message">The message.</param> + public ResourceNotFoundException(string message) + : base(message) + { + + } + } + + public class RemoteServiceUnavailableException : Exception + { + public RemoteServiceUnavailableException() + { + + } + + public RemoteServiceUnavailableException(string message) + : base(message) + { + + } + } + + public class RateLimitExceededException : Exception + { + /// <summary> + /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class. + /// </summary> + public RateLimitExceededException() + { + + } + + /// <summary> + /// Initializes a new instance of the <see cref="RateLimitExceededException" /> class. + /// </summary> + /// <param name="message">The message.</param> + public RateLimitExceededException(string message) + : base(message) + { + + } + } +} diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs new file mode 100644 index 000000000..32b942b60 --- /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 +{ + /// <summary> + /// An interface to be implemented by the applications hosting a kernel + /// </summary> + public interface IApplicationHost + { + /// <summary> + /// Gets the display name of the operating system. + /// </summary> + /// <value>The display name of the operating system.</value> + string OperatingSystemDisplayName { get; } + + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + + /// <summary> + /// Gets the device identifier. + /// </summary> + /// <value>The device identifier.</value> + string SystemId { get; } + + /// <summary> + /// Occurs when [application updated]. + /// </summary> + event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated; + + /// <summary> + /// Gets or sets a value indicating whether this instance has pending kernel reload. + /// </summary> + /// <value><c>true</c> if this instance has pending kernel reload; otherwise, <c>false</c>.</value> + bool HasPendingRestart { get; } + + bool IsShuttingDown { get; } + + /// <summary> + /// Gets a value indicating whether this instance can self restart. + /// </summary> + /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> + bool CanSelfRestart { get; } + + /// <summary> + /// Occurs when [has pending restart changed]. + /// </summary> + event EventHandler HasPendingRestartChanged; + + /// <summary> + /// Notifies the pending restart. + /// </summary> + void NotifyPendingRestart(); + + /// <summary> + /// Restarts this instance. + /// </summary> + void Restart(); + + /// <summary> + /// Gets the application version. + /// </summary> + /// <value>The application version.</value> + Version ApplicationVersion { get; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can self update. + /// </summary> + /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value> + bool CanSelfUpdate { get; } + + /// <summary> + /// Gets the exports. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="manageLiftime">if set to <c>true</c> [manage liftime].</param> + /// <returns>IEnumerable{``0}.</returns> + IEnumerable<T> GetExports<T>(bool manageLiftime = true); + + /// <summary> + /// Checks for update. + /// </summary> + /// <returns>Task{CheckForUpdateResult}.</returns> + Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress); + + /// <summary> + /// Updates the application. + /// </summary> + /// <returns>Task.</returns> + Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress); + + /// <summary> + /// Resolves this instance. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>``0.</returns> + T Resolve<T>(); + + /// <summary> + /// Resolves this instance. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns>``0.</returns> + T TryResolve<T>(); + + /// <summary> + /// Shuts down. + /// </summary> + Task Shutdown(); + + /// <summary> + /// Gets the plugins. + /// </summary> + /// <value>The plugins.</value> + IPlugin[] Plugins { get; } + + /// <summary> + /// Removes the plugin. + /// </summary> + /// <param name="plugin">The plugin.</param> + void RemovePlugin(IPlugin plugin); + + /// <summary> + /// Inits this instance. + /// </summary> + void Init(); + + /// <summary> + /// Creates the instance. + /// </summary> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + 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 000000000..43f3b0be7 --- /dev/null +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -0,0 +1,16 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> + </ItemGroup> + + <ItemGroup> + <Compile Include="..\SharedVersion.cs"/> + </ItemGroup> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + </PropertyGroup> + +</Project> diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs new file mode 100644 index 000000000..c61e88c87 --- /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 +{ + /// <summary> + /// Class HttpRequestOptions + /// </summary> + public class HttpRequestOptions + { + /// <summary> + /// Gets or sets the URL. + /// </summary> + /// <value>The URL.</value> + public string Url { get; set; } + + public CompressionMethod? DecompressionMethod { get; set; } + + /// <summary> + /// Gets or sets the accept header. + /// </summary> + /// <value>The accept header.</value> + public string AcceptHeader + { + get { return GetHeaderValue("Accept"); } + set + { + RequestHeaders["Accept"] = value; + } + } + /// <summary> + /// Gets or sets the cancellation token. + /// </summary> + /// <value>The cancellation token.</value> + public CancellationToken CancellationToken { get; set; } + + /// <summary> + /// Gets or sets the resource pool. + /// </summary> + /// <value>The resource pool.</value> + public SemaphoreSlim ResourcePool { get; set; } + + /// <summary> + /// Gets or sets the user agent. + /// </summary> + /// <value>The user agent.</value> + public string UserAgent + { + get { return GetHeaderValue("User-Agent"); } + set + { + RequestHeaders["User-Agent"] = value; + } + } + + /// <summary> + /// Gets or sets the referrer. + /// </summary> + /// <value>The referrer.</value> + public string Referer { get; set; } + + /// <summary> + /// Gets or sets the host. + /// </summary> + /// <value>The host.</value> + public string Host { get; set; } + + /// <summary> + /// Gets or sets the progress. + /// </summary> + /// <value>The progress.</value> + public IProgress<double> Progress { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable HTTP compression]. + /// </summary> + /// <value><c>true</c> if [enable HTTP compression]; otherwise, <c>false</c>.</value> + public bool EnableHttpCompression { get; set; } + + public Dictionary<string, string> 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; + } + + /// <summary> + /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class. + /// </summary> + public HttpRequestOptions() + { + EnableHttpCompression = true; + + RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + + LogRequest = true; + LogErrors = true; + CacheMode = CacheMode.None; + + TimeoutMs = 20000; + } + + public void SetPostData(IDictionary<string,string> 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 000000000..ed941a447 --- /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 +{ + /// <summary> + /// Class HttpResponseInfo + /// </summary> + public class HttpResponseInfo : IDisposable + { + /// <summary> + /// Gets or sets the type of the content. + /// </summary> + /// <value>The type of the content.</value> + public string ContentType { get; set; } + + /// <summary> + /// Gets or sets the response URL. + /// </summary> + /// <value>The response URL.</value> + public string ResponseUrl { get; set; } + + /// <summary> + /// Gets or sets the content. + /// </summary> + /// <value>The content.</value> + public Stream Content { get; set; } + + /// <summary> + /// Gets or sets the status code. + /// </summary> + /// <value>The status code.</value> + public HttpStatusCode StatusCode { get; set; } + + /// <summary> + /// Gets or sets the temp file path. + /// </summary> + /// <value>The temp file path.</value> + public string TempFilePath { get; set; } + + /// <summary> + /// Gets or sets the length of the content. + /// </summary> + /// <value>The length of the content.</value> + public long? ContentLength { get; set; } + + /// <summary> + /// Gets or sets the headers. + /// </summary> + /// <value>The headers.</value> + public Dictionary<string,string> Headers { get; set; } + + private readonly IDisposable _disposable; + + public HttpResponseInfo(IDisposable disposable) + { + _disposable = disposable; + Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } + public HttpResponseInfo() + { + Headers = new Dictionary<string, string>(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 000000000..cf5511965 --- /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 +{ + /// <summary> + /// Interface IHttpClient + /// </summary> + public interface IHttpClient + { + /// <summary> + /// Gets the response. + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task{HttpResponseInfo}.</returns> + Task<HttpResponseInfo> GetResponse(HttpRequestOptions options); + + /// <summary> + /// Gets the specified options. + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task{Stream}.</returns> + Task<Stream> Get(HttpRequestOptions options); + + /// <summary> + /// Sends the asynchronous. + /// </summary> + /// <param name="options">The options.</param> + /// <param name="httpMethod">The HTTP method.</param> + /// <returns>Task{HttpResponseInfo}.</returns> + Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod); + + /// <summary> + /// Posts the specified options. + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task{HttpResponseInfo}.</returns> + Task<HttpResponseInfo> Post(HttpRequestOptions options); + + /// <summary> + /// Downloads the contents of a given url into a temporary location + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task{System.String}.</returns> + /// <exception cref="System.ArgumentNullException">progress</exception> + /// <exception cref="MediaBrowser.Model.Net.HttpException"></exception> + Task<string> GetTempFile(HttpRequestOptions options); + + /// <summary> + /// Gets the temporary file response. + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task{HttpResponseInfo}.</returns> + Task<HttpResponseInfo> 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 000000000..b2ff797bc --- /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; + + /// <summary> + /// Gets a random port number that is currently available + /// </summary> + /// <returns>System.Int32.</returns> + int GetRandomUnusedTcpPort(); + + int GetRandomUnusedUdpPort(); + + Func<string[]> LocalSubnetsFn { get; set; } + + /// <summary> + /// Returns MAC Address from first Network Card in Computer + /// </summary> + /// <returns>[string] MAC Address</returns> + List<string> GetMacAddresses(); + + /// <summary> + /// Determines whether [is in private address space] [the specified endpoint]. + /// </summary> + /// <param name="endpoint">The endpoint.</param> + /// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns> + bool IsInPrivateAddressSpace(string endpoint); + + /// <summary> + /// Gets the network shares. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>IEnumerable{NetworkShare}.</returns> + IEnumerable<NetworkShare> GetNetworkShares(string path); + + /// <summary> + /// Gets available devices within the domain + /// </summary> + /// <returns>PC's in the Domain</returns> + IEnumerable<FileSystemEntryInfo> GetNetworkDevices(); + + /// <summary> + /// Determines whether [is in local network] [the specified endpoint]. + /// </summary> + /// <param name="endpoint">The endpoint.</param> + /// <returns><c>true</c> if [is in local network] [the specified endpoint]; otherwise, <c>false</c>.</returns> + bool IsInLocalNetwork(string endpoint); + + IpAddressInfo[] GetLocalIpAddresses(); + + IpAddressInfo ParseIpAddress(string ipAddress); + + bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo); + + Task<IpAddressInfo[]> 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 000000000..82eb6ba4b --- /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 + { + /// <summary> + /// Gets the name of the plugin + /// </summary> + /// <value>The name.</value> + public abstract string Name { get; } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + public virtual string Description + { + get { return string.Empty; } + } + + /// <summary> + /// Gets the unique id. + /// </summary> + /// <value>The unique id.</value> + public virtual Guid Id { get; private set; } + + /// <summary> + /// Gets the plugin version + /// </summary> + /// <value>The version.</value> + public Version Version { get; private set; } + + /// <summary> + /// Gets the path to the assembly file + /// </summary> + /// <value>The assembly file path.</value> + public string AssemblyFilePath { get; private set; } + + /// <summary> + /// Gets the plugin info. + /// </summary> + /// <returns>PluginInfo.</returns> + public virtual PluginInfo GetPluginInfo() + { + var info = new PluginInfo + { + Name = Name, + Version = Version.ToString(), + Description = Description, + Id = Id.ToString() + }; + + return info; + } + + /// <summary> + /// Called when just before the plugin is uninstalled from the server. + /// </summary> + 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; + } + + /// <summary> + /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed + /// </summary> + /// <value>The data folder path.</value> + public string DataFolderPath { get; private set; } + } + + /// <summary> + /// Provides a common base class for all plugins + /// </summary> + /// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam> + public abstract class BasePlugin<TConfigurationType> : BasePlugin, IHasPluginConfiguration + where TConfigurationType : BasePluginConfiguration + { + /// <summary> + /// Gets the application paths. + /// </summary> + /// <value>The application paths.</value> + protected IApplicationPaths ApplicationPaths { get; private set; } + + /// <summary> + /// Gets the XML serializer. + /// </summary> + /// <value>The XML serializer.</value> + protected IXmlSerializer XmlSerializer { get; private set; } + + /// <summary> + /// Gets the type of configuration this plugin uses + /// </summary> + /// <value>The type of the configuration.</value> + public Type ConfigurationType + { + get { return typeof(TConfigurationType); } + } + + private Action<string> _directoryCreateFn; + public void SetStartupInfo(Action<string> directoryCreateFn) + { + // hack alert, until the .net core transition is complete + _directoryCreateFn = directoryCreateFn; + } + + /// <summary> + /// Gets the name the assembly file + /// </summary> + /// <value>The name of the assembly file.</value> + protected string AssemblyFileName + { + get + { + return Path.GetFileName(AssemblyFilePath); + } + } + + /// <summary> + /// The _configuration sync lock + /// </summary> + private readonly object _configurationSyncLock = new object(); + /// <summary> + /// The _configuration + /// </summary> + private TConfigurationType _configuration; + /// <summary> + /// Gets the plugin's configuration + /// </summary> + /// <value>The configuration.</value> + 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)); + } + } + + /// <summary> + /// Gets the name of the configuration file. Subclasses should override + /// </summary> + /// <value>The name of the configuration file.</value> + public virtual string ConfigurationFileName + { + get { return Path.ChangeExtension(AssemblyFileName, ".xml"); } + } + + /// <summary> + /// Gets the full path to the configuration file + /// </summary> + /// <value>The configuration file path.</value> + public string ConfigurationFilePath + { + get + { + return Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class. + /// </summary> + /// <param name="applicationPaths">The application paths.</param> + /// <param name="xmlSerializer">The XML serializer.</param> + protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + { + ApplicationPaths = applicationPaths; + XmlSerializer = xmlSerializer; + } + + /// <summary> + /// The _save lock + /// </summary> + private readonly object _configurationSaveLock = new object(); + + /// <summary> + /// Saves the current configuration to the file system + /// </summary> + public virtual void SaveConfiguration() + { + lock (_configurationSaveLock) + { + _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath)); + + XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath); + } + } + + /// <summary> + /// Completely overwrites the current configuration with a new copy + /// Returns true or false indicating success or failure + /// </summary> + /// <param name="configuration">The configuration.</param> + /// <exception cref="System.ArgumentNullException">configuration</exception> + public virtual void UpdateConfiguration(BasePluginConfiguration configuration) + { + if (configuration == null) + { + throw new ArgumentNullException("configuration"); + } + + Configuration = (TConfigurationType)configuration; + + SaveConfiguration(); + } + + /// <summary> + /// Gets the plugin's configuration + /// </summary> + /// <value>The configuration.</value> + 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 000000000..bffd21143 --- /dev/null +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -0,0 +1,83 @@ +using MediaBrowser.Model.Plugins; +using System; + +namespace MediaBrowser.Common.Plugins +{ + /// <summary> + /// Interface IPlugin + /// </summary> + public interface IPlugin + { + /// <summary> + /// Gets the name of the plugin + /// </summary> + /// <value>The name.</value> + string Name { get; } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + string Description { get; } + + /// <summary> + /// Gets the unique id. + /// </summary> + /// <value>The unique id.</value> + Guid Id { get; } + + /// <summary> + /// Gets the plugin version + /// </summary> + /// <value>The version.</value> + Version Version { get; } + + /// <summary> + /// Gets the path to the assembly file + /// </summary> + /// <value>The assembly file path.</value> + string AssemblyFilePath { get; } + + /// <summary> + /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed + /// </summary> + /// <value>The data folder path.</value> + string DataFolderPath { get; } + + /// <summary> + /// Gets the plugin info. + /// </summary> + /// <returns>PluginInfo.</returns> + PluginInfo GetPluginInfo(); + + /// <summary> + /// Called when just before the plugin is uninstalled from the server. + /// </summary> + void OnUninstalling(); + } + + public interface IHasPluginConfiguration + { + /// <summary> + /// Gets the type of configuration this plugin uses + /// </summary> + /// <value>The type of the configuration.</value> + Type ConfigurationType { get; } + + /// <summary> + /// Completely overwrites the current configuration with a new copy + /// Returns true or false indicating success or failure + /// </summary> + /// <param name="configuration">The configuration.</param> + /// <exception cref="System.ArgumentNullException">configuration</exception> + void UpdateConfiguration(BasePluginConfiguration configuration); + + /// <summary> + /// Gets the plugin's configuration + /// </summary> + /// <value>The configuration.</value> + BasePluginConfiguration Configuration { get; } + + void SetStartupInfo(Action<string> 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 000000000..67347bc15 --- /dev/null +++ b/MediaBrowser.Common/Progress/ActionableProgress.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Common.Progress +{ + /// <summary> + /// Class ActionableProgress + /// </summary> + /// <typeparam name="T"></typeparam> + public class ActionableProgress<T> : IProgress<T> + { + /// <summary> + /// The _actions + /// </summary> + private Action<T> _action; + public event EventHandler<T> ProgressChanged; + + /// <summary> + /// Registers the action. + /// </summary> + /// <param name="action">The action.</param> + public void RegisterAction(Action<T> 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<T> : IProgress<T> + { + public event EventHandler<T> 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 000000000..09fd68f93 --- /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 000000000..7b1667c2e --- /dev/null +++ b/MediaBrowser.Common/Security/IRequiresRegistration.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Security +{ + public interface IRequiresRegistration + { + /// <summary> + /// 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. + /// </summary> + /// <returns></returns> + Task LoadRegistrationInfoAsync(); + } +} diff --git a/MediaBrowser.Common/Security/ISecurityManager.cs b/MediaBrowser.Common/Security/ISecurityManager.cs new file mode 100644 index 000000000..b63a9efd0 --- /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 + { + /// <summary> + /// Gets a value indicating whether this instance is MB supporter. + /// </summary> + /// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value> + Task<bool> IsSupporter(); + + /// <summary> + /// Gets or sets the supporter key. + /// </summary> + /// <value>The supporter key.</value> + string SupporterKey { get; } + + /// <summary> + /// Gets the registration status. Overload to support existing plug-ins. + /// </summary> + Task<MBRegistrationRecord> GetRegistrationStatus(string feature); + + /// <summary> + /// Register and app store sale with our back-end + /// </summary> + /// <param name="parameters">Json parameters to pass to admin server</param> + 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 000000000..27b3e6961 --- /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 000000000..4275799a9 --- /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> 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<RootObject[]>(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<List<RootObject>> GetLatestReleases(string organzation, string repository, string assetFilename, CancellationToken cancellationToken) + { + var list = new List<RootObject>(); + + 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<RootObject[]>(stream); + + obj = obj.Where(i => (i.assets ?? new List<Asset>()).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<Asset>()).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<Asset> 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 000000000..dab38b27c --- /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<InstallationEventArgs> PackageInstalling; + event EventHandler<InstallationEventArgs> PackageInstallationCompleted; + event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed; + event EventHandler<InstallationEventArgs> PackageInstallationCancelled; + + /// <summary> + /// The current installations + /// </summary> + List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; } + + /// <summary> + /// The completed installations + /// </summary> + IEnumerable<InstallationInfo> CompletedInstallations { get; } + + /// <summary> + /// Occurs when [plugin uninstalled]. + /// </summary> + event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled; + + /// <summary> + /// Occurs when [plugin updated]. + /// </summary> + event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated; + + /// <summary> + /// Occurs when [plugin updated]. + /// </summary> + event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; + + /// <summary> + /// Gets all available packages. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="withRegistration">if set to <c>true</c> [with registration].</param> + /// <param name="packageType">Type of the package.</param> + /// <param name="applicationVersion">The application version.</param> + /// <returns>Task{List{PackageInfo}}.</returns> + Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken, + bool withRegistration = true, + string packageType = null, + Version applicationVersion = null); + + /// <summary> + /// Gets all available packages from a static resource. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{List{PackageInfo}}.</returns> + Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken); + + /// <summary> + /// Gets the package. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="guid">The assembly guid</param> + /// <param name="classification">The classification.</param> + /// <param name="version">The version.</param> + /// <returns>Task{PackageVersionInfo}.</returns> + Task<PackageVersionInfo> GetPackage(string name, string guid, PackageVersionClass classification, Version version); + + /// <summary> + /// Gets the latest compatible version. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="guid">The assembly guid</param> + /// <param name="currentServerVersion">The current server version.</param> + /// <param name="classification">The classification.</param> + /// <returns>Task{PackageVersionInfo}.</returns> + Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release); + + /// <summary> + /// Gets the latest compatible version. + /// </summary> + /// <param name="availablePackages">The available packages.</param> + /// <param name="name">The name.</param> + /// <param name="guid">The assembly guid</param> + /// <param name="currentServerVersion">The current server version.</param> + /// <param name="classification">The classification.</param> + /// <returns>PackageVersionInfo.</returns> + PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release); + + /// <summary> + /// Gets the available plugin updates. + /// </summary> + /// <param name="applicationVersion">The current server version.</param> + /// <param name="withAutoUpdateEnabled">if set to <c>true</c> [with auto update enabled].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns> + Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken); + + /// <summary> + /// Installs the package. + /// </summary> + /// <param name="package">The package.</param> + /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">package</exception> + Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken); + + /// <summary> + /// Uninstalls a plugin + /// </summary> + /// <param name="plugin">The plugin.</param> + /// <exception cref="System.ArgumentException"></exception> + 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 000000000..9dc8ead83 --- /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 000000000..69dc1ee98 --- /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 |
