mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
continue with .net core targeting
This commit is contained in:
parent
e5d71c1014
commit
da20e8dcd2
@ -6,7 +6,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore": "5.0.0",
|
||||
"Microsoft.NETCore.Portable.Compatibility": "1.0.0"
|
||||
"Microsoft.NETCore.Portable.Compatibility": "1.0.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"dotnet": {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,12 +28,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DvdLib", "DvdLib\DvdLib.csp
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.xproj", "{5A27010A-09C6-4E86-93EA-437484C10917}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{0A82260B-4C22-4FD2-869A-E510044E3502}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -95,12 +97,6 @@ Global
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
|
||||
@ -113,6 +109,18 @@ Global
|
||||
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
|
||||
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -127,8 +135,9 @@ Global
|
||||
{5624B7B5-B5A7-41D8-9F10-CC5611109619} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{713F42B5-878E-499D-A878-E4C652B1D5E8} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{442B5058-DCAF-4263-BB6A-F21E31120A1B} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{4A4402D4-E910-443B-B8FC-2C18286A2CA0} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -1,773 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Implementations.Devices;
|
||||
using MediaBrowser.Common.Implementations.IO;
|
||||
using MediaBrowser.Common.Implementations.ScheduledTasks;
|
||||
using MediaBrowser.Common.Implementations.Serialization;
|
||||
using MediaBrowser.Common.Implementations.Updates;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Updates;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Implementations.Cryptography;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseApplicationHost
|
||||
/// </summary>
|
||||
/// <typeparam name="TApplicationPathsType">The type of the T application paths type.</typeparam>
|
||||
public abstract class BaseApplicationHost<TApplicationPathsType> : IApplicationHost
|
||||
where TApplicationPathsType : class, IApplicationPaths
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when [has pending restart changed].
|
||||
/// </summary>
|
||||
public event EventHandler HasPendingRestartChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [application updated].
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance has changes that require the entire application to restart.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance has pending application restart; otherwise, <c>false</c>.</value>
|
||||
public bool HasPendingRestart { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger.
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
protected ILogger Logger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the plugins.
|
||||
/// </summary>
|
||||
/// <value>The plugins.</value>
|
||||
public IPlugin[] Plugins { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log manager.
|
||||
/// </summary>
|
||||
/// <value>The log manager.</value>
|
||||
public ILogManager LogManager { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
protected TApplicationPathsType ApplicationPaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The json serializer
|
||||
/// </summary>
|
||||
public IJsonSerializer JsonSerializer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _XML serializer
|
||||
/// </summary>
|
||||
protected readonly IXmlSerializer XmlSerializer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets assemblies that failed to load
|
||||
/// </summary>
|
||||
/// <value>The failed assemblies.</value>
|
||||
public List<string> FailedAssemblies { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all concrete types.
|
||||
/// </summary>
|
||||
/// <value>All concrete types.</value>
|
||||
public Type[] AllConcreteTypes { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The disposable parts
|
||||
/// </summary>
|
||||
protected readonly List<IDisposable> DisposableParts = new List<IDisposable>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is first run.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value>
|
||||
public bool IsFirstRun { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kernel.
|
||||
/// </summary>
|
||||
/// <value>The kernel.</value>
|
||||
protected ITaskManager TaskManager { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the HTTP client.
|
||||
/// </summary>
|
||||
/// <value>The HTTP client.</value>
|
||||
public IHttpClient HttpClient { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the network manager.
|
||||
/// </summary>
|
||||
/// <value>The network manager.</value>
|
||||
protected INetworkManager NetworkManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration manager.
|
||||
/// </summary>
|
||||
/// <value>The configuration manager.</value>
|
||||
protected IConfigurationManager ConfigurationManager { get; private set; }
|
||||
|
||||
protected IFileSystem FileSystemManager { get; private set; }
|
||||
|
||||
protected IIsoManager IsoManager { get; private set; }
|
||||
|
||||
protected ISystemEvents SystemEvents { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is running as service.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value>
|
||||
public abstract bool IsRunningAsService { get; }
|
||||
|
||||
protected ICryptographyProvider CryptographyProvider = new CryptographyProvider();
|
||||
|
||||
private DeviceId _deviceId;
|
||||
public string SystemId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_deviceId == null)
|
||||
{
|
||||
_deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager);
|
||||
}
|
||||
|
||||
return _deviceId.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string OperatingSystemDisplayName
|
||||
{
|
||||
get { return Environment.OSVersion.VersionString; }
|
||||
}
|
||||
|
||||
public IMemoryStreamProvider MemoryStreamProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
|
||||
/// </summary>
|
||||
protected BaseApplicationHost(TApplicationPathsType applicationPaths,
|
||||
ILogManager logManager,
|
||||
IFileSystem fileSystem)
|
||||
{
|
||||
// hack alert, until common can target .net core
|
||||
BaseExtensions.CryptographyProvider = CryptographyProvider;
|
||||
|
||||
XmlSerializer = new XmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer"));
|
||||
FailedAssemblies = new List<string>();
|
||||
|
||||
ApplicationPaths = applicationPaths;
|
||||
LogManager = logManager;
|
||||
FileSystemManager = fileSystem;
|
||||
|
||||
ConfigurationManager = GetConfigurationManager();
|
||||
|
||||
// Initialize this early in case the -v command line option is used
|
||||
Logger = LogManager.GetLogger("App");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inits this instance.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
public virtual async Task Init(IProgress<double> progress)
|
||||
{
|
||||
progress.Report(1);
|
||||
|
||||
JsonSerializer = CreateJsonSerializer();
|
||||
|
||||
MemoryStreamProvider = CreateMemoryStreamProvider();
|
||||
SystemEvents = CreateSystemEvents();
|
||||
|
||||
OnLoggerLoaded(true);
|
||||
LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
|
||||
|
||||
IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted;
|
||||
progress.Report(2);
|
||||
|
||||
LogManager.LogSeverity = ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
|
||||
? LogSeverity.Debug
|
||||
: LogSeverity.Info;
|
||||
|
||||
progress.Report(3);
|
||||
|
||||
DiscoverTypes();
|
||||
progress.Report(14);
|
||||
|
||||
SetHttpLimit();
|
||||
progress.Report(15);
|
||||
|
||||
var innerProgress = new ActionableProgress<double>();
|
||||
innerProgress.RegisterAction(p => progress.Report(.8 * p + 15));
|
||||
|
||||
await RegisterResources(innerProgress).ConfigureAwait(false);
|
||||
|
||||
FindParts();
|
||||
progress.Report(95);
|
||||
|
||||
await InstallIsoMounters(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
protected abstract IMemoryStreamProvider CreateMemoryStreamProvider();
|
||||
protected abstract ISystemEvents CreateSystemEvents();
|
||||
|
||||
protected virtual void OnLoggerLoaded(bool isFirstLoad)
|
||||
{
|
||||
Logger.Info("Application version: {0}", ApplicationVersion);
|
||||
|
||||
if (!isFirstLoad)
|
||||
{
|
||||
LogEnvironmentInfo(Logger, ApplicationPaths, false);
|
||||
}
|
||||
|
||||
// Put the app config in the log for troubleshooting purposes
|
||||
Logger.LogMultiline("Application configuration:", LogSeverity.Info, new StringBuilder(JsonSerializer.SerializeToString(ConfigurationManager.CommonConfiguration)));
|
||||
|
||||
if (Plugins != null)
|
||||
{
|
||||
var pluginBuilder = new StringBuilder();
|
||||
|
||||
foreach (var plugin in Plugins)
|
||||
{
|
||||
pluginBuilder.AppendLine(string.Format("{0} {1}", plugin.Name, plugin.Version));
|
||||
}
|
||||
|
||||
Logger.LogMultiline("Plugins:", LogSeverity.Info, pluginBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, bool isStartup)
|
||||
{
|
||||
logger.LogMultiline("Emby", LogSeverity.Info, GetBaseExceptionMessage(appPaths));
|
||||
}
|
||||
|
||||
protected static StringBuilder GetBaseExceptionMessage(IApplicationPaths appPaths)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs())));
|
||||
|
||||
builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion));
|
||||
builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount));
|
||||
builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem));
|
||||
builder.AppendLine(string.Format("64-Bit Process: {0}", Environment.Is64BitProcess));
|
||||
builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath));
|
||||
|
||||
Type type = Type.GetType("Mono.Runtime");
|
||||
if (type != null)
|
||||
{
|
||||
MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (displayName != null)
|
||||
{
|
||||
builder.AppendLine("Mono: " + displayName.Invoke(null, null));
|
||||
}
|
||||
}
|
||||
|
||||
builder.AppendLine(string.Format("Application Path: {0}", appPaths.ApplicationPath));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected abstract IJsonSerializer CreateJsonSerializer();
|
||||
|
||||
private void SetHttpLimit()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Increase the max http request limit
|
||||
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error setting http limit", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs the iso mounters.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private async Task InstallIsoMounters(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = new List<IIsoMounter>();
|
||||
|
||||
foreach (var isoMounter in GetExports<IIsoMounter>())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isoMounter.RequiresInstallation && !isoMounter.IsInstalled)
|
||||
{
|
||||
Logger.Info("Installing {0}", isoMounter.Name);
|
||||
|
||||
await isoMounter.Install(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
list.Add(isoMounter);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("{0} failed to load.", ex, isoMounter.Name);
|
||||
}
|
||||
}
|
||||
|
||||
IsoManager.AddParts(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the startup tasks.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
public virtual Task RunStartupTasks()
|
||||
{
|
||||
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
|
||||
|
||||
ConfigureAutorun();
|
||||
|
||||
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the autorun.
|
||||
/// </summary>
|
||||
private void ConfigureAutorun()
|
||||
{
|
||||
try
|
||||
{
|
||||
ConfigureAutoRunAtStartup(ConfigurationManager.CommonConfiguration.RunAtStartup);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error configuring autorun", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the composable part assemblies.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{Assembly}.</returns>
|
||||
protected abstract IEnumerable<Assembly> GetComposablePartAssemblies();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration manager.
|
||||
/// </summary>
|
||||
/// <returns>IConfigurationManager.</returns>
|
||||
protected abstract IConfigurationManager GetConfigurationManager();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the parts.
|
||||
/// </summary>
|
||||
protected virtual void FindParts()
|
||||
{
|
||||
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
||||
Plugins = GetExports<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray();
|
||||
}
|
||||
|
||||
private IPlugin LoadPlugin(IPlugin plugin)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assemblyPlugin = plugin as IPluginAssembly;
|
||||
|
||||
if (assemblyPlugin != null)
|
||||
{
|
||||
var assembly = plugin.GetType().Assembly;
|
||||
var assemblyName = assembly.GetName();
|
||||
|
||||
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
|
||||
var assemblyId = new Guid(attribute.Value);
|
||||
|
||||
var assemblyFileName = assemblyName.Name + ".dll";
|
||||
var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName);
|
||||
|
||||
assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId);
|
||||
}
|
||||
|
||||
var isFirstRun = !File.Exists(plugin.ConfigurationFilePath);
|
||||
|
||||
plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error loading plugin {0}", ex, plugin.GetType().FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discovers the types.
|
||||
/// </summary>
|
||||
protected void DiscoverTypes()
|
||||
{
|
||||
FailedAssemblies.Clear();
|
||||
|
||||
var assemblies = GetComposablePartAssemblies().ToList();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
Logger.Info("Loading {0}", assembly.FullName);
|
||||
}
|
||||
|
||||
AllConcreteTypes = assemblies
|
||||
.SelectMany(GetTypes)
|
||||
.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers resources that classes will depend on
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
protected virtual Task RegisterResources(IProgress<double> progress)
|
||||
{
|
||||
RegisterSingleInstance(ConfigurationManager);
|
||||
RegisterSingleInstance<IApplicationHost>(this);
|
||||
|
||||
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
|
||||
|
||||
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager, SystemEvents);
|
||||
|
||||
RegisterSingleInstance(JsonSerializer);
|
||||
RegisterSingleInstance(XmlSerializer);
|
||||
RegisterSingleInstance(MemoryStreamProvider);
|
||||
RegisterSingleInstance(SystemEvents);
|
||||
|
||||
RegisterSingleInstance(LogManager);
|
||||
RegisterSingleInstance(Logger);
|
||||
|
||||
RegisterSingleInstance(TaskManager);
|
||||
|
||||
RegisterSingleInstance(FileSystemManager);
|
||||
|
||||
HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamProvider);
|
||||
RegisterSingleInstance(HttpClient);
|
||||
|
||||
NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));
|
||||
RegisterSingleInstance(NetworkManager);
|
||||
|
||||
IsoManager = new IsoManager();
|
||||
RegisterSingleInstance(IsoManager);
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of types within an assembly
|
||||
/// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly.</param>
|
||||
/// <returns>IEnumerable{Type}.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">assembly</exception>
|
||||
protected IEnumerable<Type> GetTypes(Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException("assembly");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
if (ex.LoaderExceptions != null)
|
||||
{
|
||||
foreach (var loaderException in ex.LoaderExceptions)
|
||||
{
|
||||
Logger.Error("LoaderException: " + loaderException.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// If it fails we can still get a list of the Types it was able to resolve
|
||||
return ex.Types.Where(t => t != null);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract INetworkManager CreateNetworkManager(ILogger logger);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of type and resolves all constructor dependancies
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public abstract object CreateInstance(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the instance safe.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
protected abstract object CreateInstanceSafe(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified obj.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param>
|
||||
protected abstract void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
|
||||
where T : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers the single instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="func">The func.</param>
|
||||
protected abstract void RegisterSingleInstance<T>(Func<T> func)
|
||||
where T : class;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves this instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
public abstract T Resolve<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Resolves this instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
public abstract T TryResolve<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the assembly.
|
||||
/// </summary>
|
||||
/// <param name="file">The file.</param>
|
||||
/// <returns>Assembly.</returns>
|
||||
protected Assembly LoadAssembly(string file)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Assembly.Load(File.ReadAllBytes(file));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FailedAssemblies.Add(file);
|
||||
Logger.ErrorException("Error loading assembly {0}", ex, file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the export types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>IEnumerable{Type}.</returns>
|
||||
public IEnumerable<Type> GetExportTypes<T>()
|
||||
{
|
||||
var currentType = typeof(T);
|
||||
|
||||
return AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public IEnumerable<T> GetExports<T>(bool manageLiftime = true)
|
||||
{
|
||||
var parts = GetExportTypes<T>()
|
||||
.Select(CreateInstanceSafe)
|
||||
.Where(i => i != null)
|
||||
.Cast<T>()
|
||||
.ToList();
|
||||
|
||||
if (manageLiftime)
|
||||
{
|
||||
lock (DisposableParts)
|
||||
{
|
||||
DisposableParts.AddRange(parts.OfType<IDisposable>());
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
public abstract Version ApplicationVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Handles the ConfigurationUpdated event of the ConfigurationManager control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
protected virtual void OnConfigurationUpdated(object sender, EventArgs e)
|
||||
{
|
||||
ConfigureAutorun();
|
||||
}
|
||||
|
||||
protected abstract void ConfigureAutoRunAtStartup(bool autorun);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the plugin.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin.</param>
|
||||
public void RemovePlugin(IPlugin plugin)
|
||||
{
|
||||
var list = Plugins.ToList();
|
||||
list.Remove(plugin);
|
||||
Plugins = list.ToArray();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public abstract bool CanSelfRestart { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that the kernel that a change has been made that requires a restart
|
||||
/// </summary>
|
||||
public void NotifyPendingRestart()
|
||||
{
|
||||
var changed = !HasPendingRestart;
|
||||
|
||||
HasPendingRestart = true;
|
||||
|
||||
if (changed)
|
||||
{
|
||||
EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
var type = GetType();
|
||||
|
||||
Logger.Info("Disposing " + type.Name);
|
||||
|
||||
var parts = DisposableParts.Distinct().Where(i => i.GetType() != type).ToList();
|
||||
DisposableParts.Clear();
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
Logger.Info("Disposing " + part.GetType().Name);
|
||||
|
||||
try
|
||||
{
|
||||
part.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error disposing {0}", ex, part.GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restarts this instance.
|
||||
/// </summary>
|
||||
public abstract Task Restart();
|
||||
|
||||
/// <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>
|
||||
public abstract bool CanSelfUpdate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks for update.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <returns>Task{CheckForUpdateResult}.</returns>
|
||||
public abstract Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken,
|
||||
IProgress<double> progress);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the application.
|
||||
/// </summary>
|
||||
/// <param name="package">The package that contains the update</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public abstract Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken,
|
||||
IProgress<double> progress);
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down.
|
||||
/// </summary>
|
||||
public abstract Task Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Called when [application updated].
|
||||
/// </summary>
|
||||
/// <param name="package">The package.</param>
|
||||
protected void OnApplicationUpdated(PackageVersionInfo package)
|
||||
{
|
||||
Logger.Info("Application has been updated to version {0}", package.versionStr);
|
||||
|
||||
EventHelper.FireEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs<PackageVersionInfo>
|
||||
{
|
||||
Argument = package
|
||||
|
||||
}, Logger);
|
||||
|
||||
NotifyPendingRestart();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using System.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class to hold common application paths used by both the Ui and Server.
|
||||
/// This can be subclassed to add application-specific paths.
|
||||
/// </summary>
|
||||
public abstract class BaseApplicationPaths : IApplicationPaths
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
|
||||
/// </summary>
|
||||
protected BaseApplicationPaths(string programDataPath, string applicationPath)
|
||||
{
|
||||
ProgramDataPath = programDataPath;
|
||||
ApplicationPath = applicationPath;
|
||||
}
|
||||
|
||||
public string ApplicationPath { get; private set; }
|
||||
public string ProgramDataPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the system folder
|
||||
/// </summary>
|
||||
public string ProgramSystemPath
|
||||
{
|
||||
get { return Path.GetDirectoryName(ApplicationPath); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _data directory
|
||||
/// </summary>
|
||||
private string _dataDirectory;
|
||||
/// <summary>
|
||||
/// Gets the folder path to the data directory
|
||||
/// </summary>
|
||||
/// <value>The data directory.</value>
|
||||
public string DataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dataDirectory == null)
|
||||
{
|
||||
_dataDirectory = Path.Combine(ProgramDataPath, "data");
|
||||
|
||||
Directory.CreateDirectory(_dataDirectory);
|
||||
}
|
||||
|
||||
return _dataDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image cache path.
|
||||
/// </summary>
|
||||
/// <value>The image cache path.</value>
|
||||
public string ImageCachePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(CachePath, "images");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the plugin directory
|
||||
/// </summary>
|
||||
/// <value>The plugins path.</value>
|
||||
public string PluginsPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(ProgramDataPath, "plugins");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the plugin configurations directory
|
||||
/// </summary>
|
||||
/// <value>The plugin configurations path.</value>
|
||||
public string PluginConfigurationsPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(PluginsPath, "configurations");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to where temporary update files will be stored
|
||||
/// </summary>
|
||||
/// <value>The plugin configurations path.</value>
|
||||
public string TempUpdatePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(ProgramDataPath, "updates");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the log directory
|
||||
/// </summary>
|
||||
/// <value>The log directory path.</value>
|
||||
public string LogDirectoryPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(ProgramDataPath, "logs");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the application configuration root directory
|
||||
/// </summary>
|
||||
/// <value>The configuration directory path.</value>
|
||||
public string ConfigurationDirectoryPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(ProgramDataPath, "config");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the system configuration file
|
||||
/// </summary>
|
||||
/// <value>The system configuration file path.</value>
|
||||
public string SystemConfigurationFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(ConfigurationDirectoryPath, "system.xml");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _cache directory
|
||||
/// </summary>
|
||||
private string _cachePath;
|
||||
/// <summary>
|
||||
/// Gets the folder path to the cache directory
|
||||
/// </summary>
|
||||
/// <value>The cache directory.</value>
|
||||
public string CachePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_cachePath))
|
||||
{
|
||||
_cachePath = Path.Combine(ProgramDataPath, "cache");
|
||||
|
||||
Directory.CreateDirectory(_cachePath);
|
||||
}
|
||||
|
||||
return _cachePath;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cachePath = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder path to the temp directory within the cache folder
|
||||
/// </summary>
|
||||
/// <value>The temp directory.</value>
|
||||
public string TempDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(CachePath, "temp");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,328 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseConfigurationManager
|
||||
/// </summary>
|
||||
public abstract class BaseConfigurationManager : IConfigurationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of the configuration.
|
||||
/// </summary>
|
||||
/// <value>The type of the configuration.</value>
|
||||
protected abstract Type ConfigurationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [configuration updated].
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> ConfigurationUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [configuration updating].
|
||||
/// </summary>
|
||||
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [named configuration updated].
|
||||
/// </summary>
|
||||
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
protected ILogger Logger { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the XML serializer.
|
||||
/// </summary>
|
||||
/// <value>The XML serializer.</value>
|
||||
protected IXmlSerializer XmlSerializer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
public IApplicationPaths CommonApplicationPaths { get; private set; }
|
||||
public readonly IFileSystem FileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// The _configuration loaded
|
||||
/// </summary>
|
||||
private bool _configurationLoaded;
|
||||
/// <summary>
|
||||
/// The _configuration sync lock
|
||||
/// </summary>
|
||||
private object _configurationSyncLock = new object();
|
||||
/// <summary>
|
||||
/// The _configuration
|
||||
/// </summary>
|
||||
private BaseApplicationConfiguration _configuration;
|
||||
/// <summary>
|
||||
/// Gets the system configuration
|
||||
/// </summary>
|
||||
/// <value>The configuration.</value>
|
||||
public BaseApplicationConfiguration CommonConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
// Lazy load
|
||||
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
|
||||
return _configuration;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
_configuration = value;
|
||||
|
||||
_configurationLoaded = value != null;
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigurationStore[] _configurationStores = { };
|
||||
private IConfigurationFactory[] _configurationFactories = { };
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationPaths">The application paths.</param>
|
||||
/// <param name="logManager">The log manager.</param>
|
||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||
protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
||||
{
|
||||
CommonApplicationPaths = applicationPaths;
|
||||
XmlSerializer = xmlSerializer;
|
||||
FileSystem = fileSystem;
|
||||
Logger = logManager.GetLogger(GetType().Name);
|
||||
|
||||
UpdateCachePath();
|
||||
}
|
||||
|
||||
public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
|
||||
{
|
||||
_configurationFactories = factories.ToArray();
|
||||
|
||||
_configurationStores = _configurationFactories
|
||||
.SelectMany(i => i.GetConfigurations())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration.
|
||||
/// </summary>
|
||||
public void SaveConfiguration()
|
||||
{
|
||||
Logger.Info("Saving system configuration");
|
||||
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
||||
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
XmlSerializer.SerializeToFile(CommonConfiguration, path);
|
||||
}
|
||||
|
||||
OnConfigurationUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [configuration updated].
|
||||
/// </summary>
|
||||
protected virtual void OnConfigurationUpdated()
|
||||
{
|
||||
UpdateCachePath();
|
||||
|
||||
EventHelper.QueueEventIfNotNull(ConfigurationUpdated, this, EventArgs.Empty, Logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the configuration.
|
||||
/// </summary>
|
||||
/// <param name="newConfiguration">The new configuration.</param>
|
||||
/// <exception cref="System.ArgumentNullException">newConfiguration</exception>
|
||||
public virtual void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration)
|
||||
{
|
||||
if (newConfiguration == null)
|
||||
{
|
||||
throw new ArgumentNullException("newConfiguration");
|
||||
}
|
||||
|
||||
ValidateCachePath(newConfiguration);
|
||||
|
||||
CommonConfiguration = newConfiguration;
|
||||
SaveConfiguration();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the items by name path.
|
||||
/// </summary>
|
||||
private void UpdateCachePath()
|
||||
{
|
||||
string cachePath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
|
||||
{
|
||||
cachePath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
cachePath = Path.Combine(CommonConfiguration.CachePath, "cache");
|
||||
}
|
||||
|
||||
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the cache path.
|
||||
/// </summary>
|
||||
/// <param name="newConfig">The new configuration.</param>
|
||||
/// <exception cref="System.IO.DirectoryNotFoundException"></exception>
|
||||
private void ValidateCachePath(BaseApplicationConfiguration newConfig)
|
||||
{
|
||||
var newPath = newConfig.CachePath;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(newPath)
|
||||
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
|
||||
{
|
||||
// Validate
|
||||
if (!FileSystem.DirectoryExists(newPath))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||
}
|
||||
|
||||
EnsureWriteAccess(newPath);
|
||||
}
|
||||
}
|
||||
|
||||
protected void EnsureWriteAccess(string path)
|
||||
{
|
||||
var file = Path.Combine(path, Guid.NewGuid().ToString());
|
||||
|
||||
FileSystem.WriteAllText(file, string.Empty);
|
||||
FileSystem.DeleteFile(file);
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
|
||||
|
||||
private string GetConfigurationFile(string key)
|
||||
{
|
||||
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLower() + ".xml");
|
||||
}
|
||||
|
||||
public object GetConfiguration(string key)
|
||||
{
|
||||
return _configurations.GetOrAdd(key, k =>
|
||||
{
|
||||
var file = GetConfigurationFile(key);
|
||||
|
||||
var configurationInfo = _configurationStores
|
||||
.FirstOrDefault(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (configurationInfo == null)
|
||||
{
|
||||
throw new ResourceNotFoundException("Configuration with key " + key + " not found.");
|
||||
}
|
||||
|
||||
var configurationType = configurationInfo.ConfigurationType;
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
return LoadConfiguration(file, configurationType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private object LoadConfiguration(string path, Type configurationType)
|
||||
{
|
||||
try
|
||||
{
|
||||
return XmlSerializer.DeserializeFromFile(configurationType, path);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return Activator.CreateInstance(configurationType);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return Activator.CreateInstance(configurationType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error loading configuration file: {0}", ex, path);
|
||||
|
||||
return Activator.CreateInstance(configurationType);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveConfiguration(string key, object configuration)
|
||||
{
|
||||
var configurationStore = GetConfigurationStore(key);
|
||||
var configurationType = configurationStore.ConfigurationType;
|
||||
|
||||
if (configuration.GetType() != configurationType)
|
||||
{
|
||||
throw new ArgumentException("Expected configuration type is " + configurationType.Name);
|
||||
}
|
||||
|
||||
var validatingStore = configurationStore as IValidatingConfiguration;
|
||||
if (validatingStore != null)
|
||||
{
|
||||
var currentConfiguration = GetConfiguration(key);
|
||||
|
||||
validatingStore.Validate(currentConfiguration, configuration);
|
||||
}
|
||||
|
||||
EventHelper.FireEventIfNotNull(NamedConfigurationUpdating, this, new ConfigurationUpdateEventArgs
|
||||
{
|
||||
Key = key,
|
||||
NewConfiguration = configuration
|
||||
|
||||
}, Logger);
|
||||
|
||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||
|
||||
var path = GetConfigurationFile(key);
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
XmlSerializer.SerializeToFile(configuration, path);
|
||||
}
|
||||
|
||||
OnNamedConfigurationUpdated(key, configuration);
|
||||
}
|
||||
|
||||
protected virtual void OnNamedConfigurationUpdated(string key, object configuration)
|
||||
{
|
||||
EventHelper.FireEventIfNotNull(NamedConfigurationUpdated, this, new ConfigurationUpdateEventArgs
|
||||
{
|
||||
Key = key,
|
||||
NewConfiguration = configuration
|
||||
|
||||
}, Logger);
|
||||
}
|
||||
|
||||
public Type GetConfigurationType(string key)
|
||||
{
|
||||
return GetConfigurationStore(key)
|
||||
.ConfigurationType;
|
||||
}
|
||||
|
||||
private ConfigurationStore GetConfigurationStore(string key)
|
||||
{
|
||||
return _configurationStores
|
||||
.First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ConfigurationHelper
|
||||
/// </summary>
|
||||
public static class ConfigurationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads an xml configuration file from the file system
|
||||
/// It will immediately re-serialize and save if new serialization data is available due to property changes
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
||||
{
|
||||
object configuration;
|
||||
|
||||
byte[] buffer = null;
|
||||
|
||||
// Use try/catch to avoid the extra file system lookup using File.Exists
|
||||
try
|
||||
{
|
||||
buffer = fileSystem.ReadAllBytes(path);
|
||||
|
||||
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
configuration = Activator.CreateInstance(type);
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
xmlSerializer.SerializeToStream(configuration, stream);
|
||||
|
||||
// Take the object we just got and serialize it back to bytes
|
||||
var newBytes = stream.ToArray();
|
||||
|
||||
// If the file didn't exist before, or if something has changed, re-save
|
||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
||||
{
|
||||
fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
// Save it after load in case we got new items
|
||||
fileSystem.WriteAllBytes(path, newBytes);
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Cryptography
|
||||
{
|
||||
public class CryptographyProvider : ICryptographyProvider
|
||||
{
|
||||
public Guid GetMD5(string str)
|
||||
{
|
||||
return new Guid(GetMD5Bytes(str));
|
||||
}
|
||||
public byte[] GetMD5Bytes(string str)
|
||||
{
|
||||
using (var provider = MD5.Create())
|
||||
{
|
||||
return provider.ComputeHash(Encoding.Unicode.GetBytes(str));
|
||||
}
|
||||
}
|
||||
public byte[] GetMD5Bytes(Stream str)
|
||||
{
|
||||
using (var provider = MD5.Create())
|
||||
{
|
||||
return provider.ComputeHash(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Devices
|
||||
{
|
||||
public class DeviceId
|
||||
{
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
private string CachePath
|
||||
{
|
||||
get { return Path.Combine(_appPaths.DataPath, "device.txt"); }
|
||||
}
|
||||
|
||||
private string GetCachedId()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
var value = File.ReadAllText(CachePath, Encoding.UTF8);
|
||||
|
||||
Guid guid;
|
||||
if (Guid.TryParse(value, out guid))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
_logger.Error("Invalid value found in device id file");
|
||||
}
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error reading file", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SaveId(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = CachePath;
|
||||
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
_fileSystem.WriteAllText(path, id, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error writing to file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetNewId()
|
||||
{
|
||||
return Guid.NewGuid().ToString("N");
|
||||
}
|
||||
|
||||
private string GetDeviceId()
|
||||
{
|
||||
var id = GetCachedId();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
id = GetNewId();
|
||||
SaveId(id);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private string _id;
|
||||
|
||||
public DeviceId(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
if (fileSystem == null) {
|
||||
throw new ArgumentNullException ("fileSystem");
|
||||
}
|
||||
|
||||
_appPaths = appPaths;
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return _id ?? (_id = GetDeviceId()); }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpClientInfo
|
||||
/// </summary>
|
||||
public class HttpClientInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the last timeout.
|
||||
/// </summary>
|
||||
/// <value>The last timeout.</value>
|
||||
public DateTime LastTimeout { get; set; }
|
||||
}
|
||||
}
|
@ -1,936 +0,0 @@
|
||||
using System.Net.Sockets;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Net;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpClientManager
|
||||
/// </summary>
|
||||
public class HttpClientManager : IHttpClient
|
||||
{
|
||||
/// <summary>
|
||||
/// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling
|
||||
/// </summary>
|
||||
private const int TimeoutSeconds = 30;
|
||||
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// The _app paths
|
||||
/// </summary>
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IMemoryStreamProvider _memoryStreamProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpClientManager" /> class.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <exception cref="System.ArgumentNullException">appPaths
|
||||
/// or
|
||||
/// logger</exception>
|
||||
public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
|
||||
{
|
||||
if (appPaths == null)
|
||||
{
|
||||
throw new ArgumentNullException("appPaths");
|
||||
}
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException("logger");
|
||||
}
|
||||
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_memoryStreamProvider = memoryStreamProvider;
|
||||
_appPaths = appPaths;
|
||||
|
||||
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
|
||||
// Trakt requests sometimes fail without this
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests.
|
||||
/// DON'T dispose it after use.
|
||||
/// </summary>
|
||||
/// <value>The HTTP clients.</value>
|
||||
private readonly ConcurrentDictionary<string, HttpClientInfo> _httpClients = new ConcurrentDictionary<string, HttpClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets
|
||||
/// </summary>
|
||||
/// <param name="host">The host.</param>
|
||||
/// <param name="enableHttpCompression">if set to <c>true</c> [enable HTTP compression].</param>
|
||||
/// <returns>HttpClient.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">host</exception>
|
||||
private HttpClientInfo GetHttpClient(string host, bool enableHttpCompression)
|
||||
{
|
||||
if (string.IsNullOrEmpty(host))
|
||||
{
|
||||
throw new ArgumentNullException("host");
|
||||
}
|
||||
|
||||
HttpClientInfo client;
|
||||
|
||||
var key = host + enableHttpCompression;
|
||||
|
||||
if (!_httpClients.TryGetValue(key, out client))
|
||||
{
|
||||
client = new HttpClientInfo();
|
||||
|
||||
_httpClients.TryAdd(key, client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private WebRequest CreateWebRequest(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
return WebRequest.Create(url);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
//Webrequest creation does fail on MONO randomly when using WebRequest.Create
|
||||
//the issue occurs in the GetCreator method here: http://www.oschina.net/code/explore/mono-2.8.1/mcs/class/System/System.Net/WebRequest.cs
|
||||
|
||||
var type = Type.GetType("System.Net.HttpRequestCreator, System, Version=4.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089");
|
||||
var creator = Activator.CreateInstance(type, nonPublic: true) as IWebRequestCreate;
|
||||
return creator.Create(new Uri(url)) as HttpWebRequest;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddIpv4Option(HttpWebRequest request, HttpRequestOptions options)
|
||||
{
|
||||
request.ServicePoint.BindIPEndPointDelegate = (servicePount, remoteEndPoint, retryCount) =>
|
||||
{
|
||||
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
return new IPEndPoint(IPAddress.Any, 0);
|
||||
}
|
||||
throw new InvalidOperationException("no IPv4 address");
|
||||
};
|
||||
}
|
||||
|
||||
private WebRequest GetRequest(HttpRequestOptions options, string method)
|
||||
{
|
||||
var url = options.Url;
|
||||
|
||||
var uriAddress = new Uri(url);
|
||||
var userInfo = uriAddress.UserInfo;
|
||||
if (!string.IsNullOrWhiteSpace(userInfo))
|
||||
{
|
||||
_logger.Info("Found userInfo in url: {0} ... url: {1}", userInfo, url);
|
||||
url = url.Replace(userInfo + "@", string.Empty);
|
||||
}
|
||||
|
||||
var request = CreateWebRequest(url);
|
||||
var httpWebRequest = request as HttpWebRequest;
|
||||
|
||||
if (httpWebRequest != null)
|
||||
{
|
||||
if (options.PreferIpv4)
|
||||
{
|
||||
AddIpv4Option(httpWebRequest, options);
|
||||
}
|
||||
|
||||
AddRequestHeaders(httpWebRequest, options);
|
||||
|
||||
httpWebRequest.AutomaticDecompression = options.EnableHttpCompression ?
|
||||
(options.DecompressionMethod ?? DecompressionMethods.Deflate) :
|
||||
DecompressionMethods.None;
|
||||
}
|
||||
|
||||
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
|
||||
|
||||
if (httpWebRequest != null)
|
||||
{
|
||||
if (options.EnableKeepAlive)
|
||||
{
|
||||
httpWebRequest.KeepAlive = true;
|
||||
}
|
||||
}
|
||||
|
||||
request.Method = method;
|
||||
request.Timeout = options.TimeoutMs;
|
||||
|
||||
if (httpWebRequest != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(options.Host))
|
||||
{
|
||||
httpWebRequest.Host = options.Host;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(options.Referer))
|
||||
{
|
||||
httpWebRequest.Referer = options.Referer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userInfo))
|
||||
{
|
||||
var parts = userInfo.Split(':');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
request.Credentials = GetCredential(url, parts[0], parts[1]);
|
||||
request.PreAuthenticate = true;
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private CredentialCache GetCredential(string url, string username, string password)
|
||||
{
|
||||
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
|
||||
CredentialCache credentialCache = new CredentialCache();
|
||||
credentialCache.Add(new Uri(url), "Basic", new NetworkCredential(username, password));
|
||||
return credentialCache;
|
||||
}
|
||||
|
||||
private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
|
||||
{
|
||||
foreach (var header in options.RequestHeaders.ToList())
|
||||
{
|
||||
if (string.Equals(header.Key, "Accept", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
request.Accept = header.Value;
|
||||
}
|
||||
else if (string.Equals(header.Key, "User-Agent", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
request.UserAgent = header.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Headers.Set(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response internal.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task{HttpResponseInfo}.</returns>
|
||||
public Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
|
||||
{
|
||||
return SendAsync(options, "GET");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a GET request and returns the resulting stream
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
public async Task<Stream> Get(HttpRequestOptions options)
|
||||
{
|
||||
var response = await GetResponse(options).ConfigureAwait(false);
|
||||
|
||||
return response.Content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a GET request and returns the resulting stream
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="resourcePool">The resource pool.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
public Task<Stream> Get(string url, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
|
||||
{
|
||||
return Get(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = resourcePool,
|
||||
CancellationToken = cancellationToken,
|
||||
BufferContent = resourcePool != null
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified URL.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
public Task<Stream> Get(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
return Get(url, null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// send as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="httpMethod">The HTTP method.</param>
|
||||
/// <returns>Task{HttpResponseInfo}.</returns>
|
||||
/// <exception cref="HttpException">
|
||||
/// </exception>
|
||||
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
|
||||
{
|
||||
if (options.CacheMode == CacheMode.None)
|
||||
{
|
||||
return await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var url = options.Url;
|
||||
var urlHash = url.ToLower().GetMD5().ToString("N");
|
||||
|
||||
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
||||
|
||||
var response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
|
||||
if (response != null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
response = await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
await CacheResponse(response, responseCachePath).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseInfo> GetCachedResponse(string responseCachePath, TimeSpan cacheLength, string url)
|
||||
{
|
||||
_logger.Info("Checking for cache file {0}", responseCachePath);
|
||||
|
||||
try
|
||||
{
|
||||
if (_fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
|
||||
{
|
||||
using (var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
|
||||
{
|
||||
var memoryStream = _memoryStreamProvider.CreateNew();
|
||||
|
||||
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
return new HttpResponseInfo
|
||||
{
|
||||
ResponseUrl = url,
|
||||
Content = memoryStream,
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
ContentLength = memoryStream.Length
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task CacheResponse(HttpResponseInfo response, string responseCachePath)
|
||||
{
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(responseCachePath));
|
||||
|
||||
using (var responseStream = response.Content)
|
||||
{
|
||||
var memoryStream = _memoryStreamProvider.CreateNew();
|
||||
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
|
||||
{
|
||||
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
response.Content = memoryStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, string httpMethod)
|
||||
{
|
||||
ValidateParams(options);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
||||
|
||||
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
|
||||
{
|
||||
throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url))
|
||||
{
|
||||
IsTimedOut = true
|
||||
};
|
||||
}
|
||||
|
||||
var httpWebRequest = GetRequest(options, httpMethod);
|
||||
|
||||
if (options.RequestContentBytes != null ||
|
||||
!string.IsNullOrEmpty(options.RequestContent) ||
|
||||
string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var bytes = options.RequestContentBytes ??
|
||||
Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty);
|
||||
|
||||
httpWebRequest.ContentType = options.RequestContentType ?? "application/x-www-form-urlencoded";
|
||||
|
||||
httpWebRequest.ContentLength = bytes.Length;
|
||||
httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
|
||||
{
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
options.ResourcePool.Release();
|
||||
}
|
||||
|
||||
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true };
|
||||
}
|
||||
|
||||
if (options.LogRequest)
|
||||
{
|
||||
_logger.Info("HttpClientManager {0}: {1}", httpMethod.ToUpper(), options.Url);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (!options.BufferContent)
|
||||
{
|
||||
var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false);
|
||||
|
||||
var httpResponse = (HttpWebResponse)response;
|
||||
|
||||
EnsureSuccessStatusCode(client, httpResponse, options);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
return GetResponseInfo(httpResponse, httpResponse.GetResponseStream(), GetContentLength(httpResponse), httpResponse);
|
||||
}
|
||||
|
||||
using (var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false))
|
||||
{
|
||||
var httpResponse = (HttpWebResponse)response;
|
||||
|
||||
EnsureSuccessStatusCode(client, httpResponse, options);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
using (var stream = httpResponse.GetResponseStream())
|
||||
{
|
||||
var memoryStream = _memoryStreamProvider.CreateNew();
|
||||
|
||||
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
return GetResponseInfo(httpResponse, memoryStream, memoryStream.Length, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
throw GetCancellationException(options, client, options.CancellationToken, ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw GetException(ex, options, client);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
options.ResourcePool.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
|
||||
{
|
||||
var responseInfo = new HttpResponseInfo(disposable)
|
||||
{
|
||||
Content = content,
|
||||
|
||||
StatusCode = httpResponse.StatusCode,
|
||||
|
||||
ContentType = httpResponse.ContentType,
|
||||
|
||||
ContentLength = contentLength,
|
||||
|
||||
ResponseUrl = httpResponse.ResponseUri.ToString()
|
||||
};
|
||||
|
||||
if (httpResponse.Headers != null)
|
||||
{
|
||||
SetHeaders(httpResponse.Headers, responseInfo);
|
||||
}
|
||||
|
||||
return responseInfo;
|
||||
}
|
||||
|
||||
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength)
|
||||
{
|
||||
var responseInfo = new HttpResponseInfo
|
||||
{
|
||||
TempFilePath = tempFile,
|
||||
|
||||
StatusCode = httpResponse.StatusCode,
|
||||
|
||||
ContentType = httpResponse.ContentType,
|
||||
|
||||
ContentLength = contentLength
|
||||
};
|
||||
|
||||
if (httpResponse.Headers != null)
|
||||
{
|
||||
SetHeaders(httpResponse.Headers, responseInfo);
|
||||
}
|
||||
|
||||
return responseInfo;
|
||||
}
|
||||
|
||||
private void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo)
|
||||
{
|
||||
foreach (var key in headers.AllKeys)
|
||||
{
|
||||
responseInfo.Headers[key] = headers[key];
|
||||
}
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
|
||||
{
|
||||
return SendAsync(options, "POST");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a POST request
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="postData">Params to add to the POST data.</param>
|
||||
/// <returns>stream on success, null on failure</returns>
|
||||
public async Task<Stream> Post(HttpRequestOptions options, Dictionary<string, string> postData)
|
||||
{
|
||||
options.SetPostData(postData);
|
||||
|
||||
var response = await Post(options).ConfigureAwait(false);
|
||||
|
||||
return response.Content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a POST request
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="postData">Params to add to the POST data.</param>
|
||||
/// <param name="resourcePool">The resource pool.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>stream on success, null on failure</returns>
|
||||
public Task<Stream> Post(string url, Dictionary<string, string> postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
|
||||
{
|
||||
return Post(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = resourcePool,
|
||||
CancellationToken = cancellationToken,
|
||||
BufferContent = resourcePool != null
|
||||
|
||||
}, postData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the contents of a given url into a temporary location
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task{System.String}.</returns>
|
||||
public async Task<string> GetTempFile(HttpRequestOptions options)
|
||||
{
|
||||
var response = await GetTempFileResponse(options).ConfigureAwait(false);
|
||||
|
||||
return response.TempFilePath;
|
||||
}
|
||||
|
||||
public async Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options)
|
||||
{
|
||||
ValidateParams(options);
|
||||
|
||||
_fileSystem.CreateDirectory(_appPaths.TempDirectory);
|
||||
|
||||
var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
|
||||
|
||||
if (options.Progress == null)
|
||||
{
|
||||
throw new ArgumentNullException("progress");
|
||||
}
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var httpWebRequest = GetRequest(options, "GET");
|
||||
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
options.Progress.Report(0);
|
||||
|
||||
if (options.LogRequest)
|
||||
{
|
||||
_logger.Info("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
|
||||
}
|
||||
|
||||
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
||||
|
||||
try
|
||||
{
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
|
||||
{
|
||||
var httpResponse = (HttpWebResponse)response;
|
||||
|
||||
EnsureSuccessStatusCode(client, httpResponse, options);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var contentLength = GetContentLength(httpResponse);
|
||||
|
||||
if (!contentLength.HasValue)
|
||||
{
|
||||
// We're not able to track progress
|
||||
using (var stream = httpResponse.GetResponseStream())
|
||||
{
|
||||
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
|
||||
{
|
||||
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value))
|
||||
{
|
||||
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
|
||||
{
|
||||
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options.Progress.Report(100);
|
||||
|
||||
return GetResponseInfo(httpResponse, tempFile, contentLength);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DeleteTempFile(tempFile);
|
||||
throw GetException(ex, options, client);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
options.ResourcePool.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long? GetContentLength(HttpWebResponse response)
|
||||
{
|
||||
var length = response.ContentLength;
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
private Exception GetException(Exception ex, HttpRequestOptions options, HttpClientInfo client)
|
||||
{
|
||||
if (ex is HttpException)
|
||||
{
|
||||
return ex;
|
||||
}
|
||||
|
||||
var webException = ex as WebException
|
||||
?? ex.InnerException as WebException;
|
||||
|
||||
if (webException != null)
|
||||
{
|
||||
if (options.LogErrors)
|
||||
{
|
||||
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
||||
}
|
||||
|
||||
var exception = new HttpException(ex.Message, ex);
|
||||
|
||||
var response = webException.Response as HttpWebResponse;
|
||||
if (response != null)
|
||||
{
|
||||
exception.StatusCode = response.StatusCode;
|
||||
|
||||
if ((int)response.StatusCode == 429)
|
||||
{
|
||||
client.LastTimeout = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
return exception;
|
||||
}
|
||||
|
||||
var operationCanceledException = ex as OperationCanceledException
|
||||
?? ex.InnerException as OperationCanceledException;
|
||||
|
||||
if (operationCanceledException != null)
|
||||
{
|
||||
return GetCancellationException(options, client, options.CancellationToken, operationCanceledException);
|
||||
}
|
||||
|
||||
if (options.LogErrors)
|
||||
{
|
||||
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
private void DeleteTempFile(string file)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(file);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Might not have been created at all. No need to worry.
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateParams(HttpRequestOptions options)
|
||||
{
|
||||
if (string.IsNullOrEmpty(options.Url))
|
||||
{
|
||||
throw new ArgumentNullException("options");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the host from URL.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetHostFromUrl(string url)
|
||||
{
|
||||
var index = url.IndexOf("://", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
url = url.Substring(index + 3);
|
||||
var host = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(host))
|
||||
{
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
_httpClients.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws the cancellation exception.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="client">The client.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <returns>Exception.</returns>
|
||||
private Exception GetCancellationException(HttpRequestOptions options, HttpClientInfo client, CancellationToken cancellationToken, OperationCanceledException exception)
|
||||
{
|
||||
// If the HttpClient's timeout is reached, it will cancel the Task internally
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var msg = string.Format("Connection to {0} timed out", options.Url);
|
||||
|
||||
if (options.LogErrors)
|
||||
{
|
||||
_logger.Error(msg);
|
||||
}
|
||||
|
||||
client.LastTimeout = DateTime.UtcNow;
|
||||
|
||||
// Throw an HttpException so that the caller doesn't think it was cancelled by user code
|
||||
return new HttpException(msg, exception)
|
||||
{
|
||||
IsTimedOut = true
|
||||
};
|
||||
}
|
||||
|
||||
return exception;
|
||||
}
|
||||
|
||||
private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options)
|
||||
{
|
||||
var statusCode = response.StatusCode;
|
||||
|
||||
var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299;
|
||||
|
||||
if (!isSuccessful)
|
||||
{
|
||||
if (options.LogErrorResponseBody)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = response.GetResponseStream())
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var msg = reader.ReadToEnd();
|
||||
|
||||
_logger.Error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
throw new HttpException(response.StatusDescription)
|
||||
{
|
||||
StatusCode = response.StatusCode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified URL.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="postData">The post data.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
public Task<Stream> Post(string url, Dictionary<string, string> postData, CancellationToken cancellationToken)
|
||||
{
|
||||
return Post(url, postData, null, cancellationToken);
|
||||
}
|
||||
|
||||
private Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
|
||||
{
|
||||
var taskCompletion = new TaskCompletionSource<WebResponse>();
|
||||
|
||||
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
|
||||
|
||||
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true);
|
||||
var callback = new TaskCallback { taskCompletion = taskCompletion };
|
||||
asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted);
|
||||
|
||||
// Handle errors
|
||||
asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
return taskCompletion.Task;
|
||||
}
|
||||
|
||||
private static void TimeoutCallback(object state, bool timedOut)
|
||||
{
|
||||
if (timedOut)
|
||||
{
|
||||
WebRequest request = (WebRequest)state;
|
||||
if (state != null)
|
||||
{
|
||||
request.Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskCallback
|
||||
{
|
||||
public TaskCompletionSource<WebResponse> taskCompletion;
|
||||
|
||||
public void OnSuccess(Task<WebResponse> task)
|
||||
{
|
||||
taskCompletion.TrySetResult(task.Result);
|
||||
}
|
||||
|
||||
public void OnError(Task<WebResponse> task)
|
||||
{
|
||||
if (task.Exception != null)
|
||||
{
|
||||
taskCompletion.TrySetException(task.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletion.TrySetException(new List<Exception>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
using MediaBrowser.Model.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Class IsoManager
|
||||
/// </summary>
|
||||
public class IsoManager : IIsoManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The _mounters
|
||||
/// </summary>
|
||||
private readonly List<IIsoMounter> _mounters = new List<IIsoMounter>();
|
||||
|
||||
/// <summary>
|
||||
/// Mounts the specified iso path.
|
||||
/// </summary>
|
||||
/// <param name="isoPath">The iso path.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>IsoMount.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">isoPath</exception>
|
||||
/// <exception cref="System.ArgumentException"></exception>
|
||||
public Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(isoPath))
|
||||
{
|
||||
throw new ArgumentNullException("isoPath");
|
||||
}
|
||||
|
||||
var mounter = _mounters.FirstOrDefault(i => i.CanMount(isoPath));
|
||||
|
||||
if (mounter == null)
|
||||
{
|
||||
throw new ArgumentException(string.Format("No mounters are able to mount {0}", isoPath));
|
||||
}
|
||||
|
||||
return mounter.Mount(isoPath, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can mount the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if this instance can mount the specified path; otherwise, <c>false</c>.</returns>
|
||||
public bool CanMount(string path)
|
||||
{
|
||||
return _mounters.Any(i => i.CanMount(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the parts.
|
||||
/// </summary>
|
||||
/// <param name="mounters">The mounters.</param>
|
||||
public void AddParts(IEnumerable<IIsoMounter> mounters)
|
||||
{
|
||||
_mounters.AddRange(mounters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var mounter in _mounters)
|
||||
{
|
||||
mounter.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,705 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ManagedFileSystem
|
||||
/// </summary>
|
||||
public class ManagedFileSystem : IFileSystem
|
||||
{
|
||||
protected ILogger Logger;
|
||||
|
||||
private readonly bool _supportsAsyncFileStreams;
|
||||
private char[] _invalidFileNameChars;
|
||||
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
|
||||
protected bool EnableFileSystemRequestConcat = true;
|
||||
|
||||
public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars)
|
||||
{
|
||||
Logger = logger;
|
||||
_supportsAsyncFileStreams = supportsAsyncFileStreams;
|
||||
SetInvalidFileNameChars(enableManagedInvalidFileNameChars);
|
||||
}
|
||||
|
||||
public void AddShortcutHandler(IShortcutHandler handler)
|
||||
{
|
||||
_shortcutHandlers.Add(handler);
|
||||
}
|
||||
|
||||
protected void SetInvalidFileNameChars(bool enableManagedInvalidFileNameChars)
|
||||
{
|
||||
if (enableManagedInvalidFileNameChars)
|
||||
{
|
||||
_invalidFileNameChars = Path.GetInvalidFileNameChars();
|
||||
}
|
||||
else
|
||||
{
|
||||
// GetInvalidFileNameChars is less restrictive in Linux/Mac than Windows, this mimic Windows behavior for mono under Linux/Mac.
|
||||
_invalidFileNameChars = new char[41] { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
||||
'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12',
|
||||
'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D',
|
||||
'\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' };
|
||||
}
|
||||
}
|
||||
|
||||
public char DirectorySeparatorChar
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFullPath(string path)
|
||||
{
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified filename is shortcut.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
||||
public virtual bool IsShortcut(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(filename);
|
||||
return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the shortcut.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
||||
public virtual string ResolveShortcut(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(filename);
|
||||
var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
return handler.Resolve(filename);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the shortcut.
|
||||
/// </summary>
|
||||
/// <param name="shortcutPath">The shortcut path.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// shortcutPath
|
||||
/// or
|
||||
/// target
|
||||
/// </exception>
|
||||
public void CreateShortcut(string shortcutPath, string target)
|
||||
{
|
||||
if (string.IsNullOrEmpty(shortcutPath))
|
||||
{
|
||||
throw new ArgumentNullException("shortcutPath");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(target))
|
||||
{
|
||||
throw new ArgumentNullException("target");
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(shortcutPath);
|
||||
var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
handler.Create(shortcutPath, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path.
|
||||
/// </summary>
|
||||
/// <param name="path">A path to a file or directory.</param>
|
||||
/// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
|
||||
/// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
|
||||
/// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks>
|
||||
public FileSystemMetadata GetFileSystemInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
|
||||
if (Path.HasExtension(path))
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
return GetFileSystemMetadata(new DirectoryInfo(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileInfo = new DirectoryInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
return GetFileSystemMetadata(new FileInfo(path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="FileSystemMetadata"/> object for the specified file path.
|
||||
/// </summary>
|
||||
/// <param name="path">A path to a file.</param>
|
||||
/// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
|
||||
/// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
|
||||
/// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> property will both be set to false.</para>
|
||||
/// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
|
||||
public FileSystemMetadata GetFileInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path.
|
||||
/// </summary>
|
||||
/// <param name="path">A path to a directory.</param>
|
||||
/// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
|
||||
/// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object's
|
||||
/// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetadata.Exists"/> property will be set to false.</para>
|
||||
/// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks>
|
||||
public FileSystemMetadata GetDirectoryInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var fileInfo = new DirectoryInfo(path);
|
||||
|
||||
return GetFileSystemMetadata(fileInfo);
|
||||
}
|
||||
|
||||
private FileSystemMetadata GetFileSystemMetadata(FileSystemInfo info)
|
||||
{
|
||||
var result = new FileSystemMetadata();
|
||||
|
||||
result.Exists = info.Exists;
|
||||
result.FullName = info.FullName;
|
||||
result.Extension = info.Extension;
|
||||
result.Name = info.Name;
|
||||
|
||||
if (result.Exists)
|
||||
{
|
||||
var attributes = info.Attributes;
|
||||
result.IsDirectory = info is DirectoryInfo || (attributes & FileAttributes.Directory) == FileAttributes.Directory;
|
||||
result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
||||
result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
|
||||
|
||||
var fileInfo = info as FileInfo;
|
||||
if (fileInfo != null)
|
||||
{
|
||||
result.Length = fileInfo.Length;
|
||||
result.DirectoryName = fileInfo.DirectoryName;
|
||||
}
|
||||
|
||||
result.CreationTimeUtc = GetCreationTimeUtc(info);
|
||||
result.LastWriteTimeUtc = GetLastWriteTimeUtc(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.IsDirectory = info is DirectoryInfo;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The space char
|
||||
/// </summary>
|
||||
private const char SpaceChar = ' ';
|
||||
|
||||
/// <summary>
|
||||
/// Takes a filename and removes invalid characters
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
||||
public string GetValidFilename(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
var builder = new StringBuilder(filename);
|
||||
|
||||
foreach (var c in _invalidFileNameChars)
|
||||
{
|
||||
builder = builder.Replace(c, SpaceChar);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetCreationTimeUtc(FileSystemInfo info)
|
||||
{
|
||||
// This could throw an error on some file systems that have dates out of range
|
||||
try
|
||||
{
|
||||
return info.CreationTimeUtc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName);
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetCreationTimeUtc(string path)
|
||||
{
|
||||
return GetCreationTimeUtc(GetFileSystemInfo(path));
|
||||
}
|
||||
|
||||
public DateTime GetCreationTimeUtc(FileSystemMetadata info)
|
||||
{
|
||||
return info.CreationTimeUtc;
|
||||
}
|
||||
|
||||
public DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
|
||||
{
|
||||
return info.LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation time UTC.
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
|
||||
{
|
||||
// This could throw an error on some file systems that have dates out of range
|
||||
try
|
||||
{
|
||||
return info.LastWriteTimeUtc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName);
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last write time UTC.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
public DateTime GetLastWriteTimeUtc(string path)
|
||||
{
|
||||
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file stream.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="mode">The mode.</param>
|
||||
/// <param name="access">The access.</param>
|
||||
/// <param name="share">The share.</param>
|
||||
/// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
|
||||
/// <returns>FileStream.</returns>
|
||||
public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false)
|
||||
{
|
||||
if (_supportsAsyncFileStreams && isAsync)
|
||||
{
|
||||
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144, true);
|
||||
}
|
||||
|
||||
return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144);
|
||||
}
|
||||
|
||||
private FileMode GetFileMode(FileOpenMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FileOpenMode.Append:
|
||||
return FileMode.Append;
|
||||
case FileOpenMode.Create:
|
||||
return FileMode.Create;
|
||||
case FileOpenMode.CreateNew:
|
||||
return FileMode.CreateNew;
|
||||
case FileOpenMode.Open:
|
||||
return FileMode.Open;
|
||||
case FileOpenMode.OpenOrCreate:
|
||||
return FileMode.OpenOrCreate;
|
||||
case FileOpenMode.Truncate:
|
||||
return FileMode.Truncate;
|
||||
default:
|
||||
throw new Exception("Unrecognized FileOpenMode");
|
||||
}
|
||||
}
|
||||
|
||||
private FileAccess GetFileAccess(FileAccessMode mode)
|
||||
{
|
||||
var val = (int)mode;
|
||||
|
||||
return (FileAccess)val;
|
||||
}
|
||||
|
||||
private FileShare GetFileShare(FileShareMode mode)
|
||||
{
|
||||
var val = (int)mode;
|
||||
|
||||
return (FileShare)val;
|
||||
}
|
||||
|
||||
public void SetHidden(string path, bool isHidden)
|
||||
{
|
||||
var info = GetFileInfo(path);
|
||||
|
||||
if (info.Exists && info.IsHidden != isHidden)
|
||||
{
|
||||
if (isHidden)
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
|
||||
File.SetAttributes(path, attributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
|
||||
{
|
||||
return attributes & ~attributesToRemove;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps the files.
|
||||
/// </summary>
|
||||
/// <param name="file1">The file1.</param>
|
||||
/// <param name="file2">The file2.</param>
|
||||
public void SwapFiles(string file1, string file2)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file1))
|
||||
{
|
||||
throw new ArgumentNullException("file1");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(file2))
|
||||
{
|
||||
throw new ArgumentNullException("file2");
|
||||
}
|
||||
|
||||
var temp1 = Path.GetTempFileName();
|
||||
var temp2 = Path.GetTempFileName();
|
||||
|
||||
// Copying over will fail against hidden files
|
||||
RemoveHiddenAttribute(file1);
|
||||
RemoveHiddenAttribute(file2);
|
||||
|
||||
CopyFile(file1, temp1, true);
|
||||
CopyFile(file2, temp2, true);
|
||||
|
||||
CopyFile(temp1, file2, true);
|
||||
CopyFile(temp2, file1, true);
|
||||
|
||||
DeleteFile(temp1);
|
||||
DeleteFile(temp2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the hidden attribute.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
private void RemoveHiddenAttribute(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var currentFile = new FileInfo(path);
|
||||
|
||||
// This will fail if the file is hidden
|
||||
if (currentFile.Exists)
|
||||
{
|
||||
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||
{
|
||||
currentFile.Attributes &= ~FileAttributes.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsSubPath(string parentPath, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(parentPath))
|
||||
{
|
||||
throw new ArgumentNullException("parentPath");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
return path.IndexOf(parentPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
public bool IsRootPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var parent = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string NormalizePath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
public string GetFileNameWithoutExtension(FileSystemMetadata info)
|
||||
{
|
||||
if (info.IsDirectory)
|
||||
{
|
||||
return info.Name;
|
||||
}
|
||||
|
||||
return Path.GetFileNameWithoutExtension(info.FullName);
|
||||
}
|
||||
|
||||
public string GetFileNameWithoutExtension(string path)
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
public bool IsPathFile(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
|
||||
|
||||
if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
|
||||
!path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
//return Path.IsPathRooted(path);
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path, bool recursive)
|
||||
{
|
||||
Directory.Delete(path, recursive);
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
public IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
|
||||
return ToMetadata(path, new DirectoryInfo(path).EnumerateDirectories("*", searchOption));
|
||||
}
|
||||
|
||||
public IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
|
||||
return ToMetadata(path, new DirectoryInfo(path).EnumerateFiles("*", searchOption));
|
||||
}
|
||||
|
||||
public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(path);
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
|
||||
if (EnableFileSystemRequestConcat)
|
||||
{
|
||||
return ToMetadata(path, directoryInfo.EnumerateDirectories("*", searchOption))
|
||||
.Concat(ToMetadata(path, directoryInfo.EnumerateFiles("*", searchOption)));
|
||||
}
|
||||
|
||||
return ToMetadata(path, directoryInfo.EnumerateFileSystemInfos("*", searchOption));
|
||||
}
|
||||
|
||||
private IEnumerable<FileSystemMetadata> ToMetadata(string parentPath, IEnumerable<FileSystemInfo> infos)
|
||||
{
|
||||
return infos.Select(i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetFileSystemMetadata(i);
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
// Can't log using the FullName because it will throw the PathTooLongExceptiona again
|
||||
//Logger.Warn("Path too long: {0}", i.FullName);
|
||||
Logger.Warn("File or directory path too long. Parent folder: {0}", parentPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).Where(i => i != null);
|
||||
}
|
||||
|
||||
public Stream OpenRead(string path)
|
||||
{
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
|
||||
public void CopyFile(string source, string target, bool overwrite)
|
||||
{
|
||||
File.Copy(source, target, overwrite);
|
||||
}
|
||||
|
||||
public void MoveFile(string source, string target)
|
||||
{
|
||||
File.Move(source, target);
|
||||
}
|
||||
|
||||
public void MoveDirectory(string source, string target)
|
||||
{
|
||||
Directory.Move(source, target);
|
||||
}
|
||||
|
||||
public bool DirectoryExists(string path)
|
||||
{
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
public bool FileExists(string path)
|
||||
{
|
||||
return File.Exists(path);
|
||||
}
|
||||
|
||||
public string ReadAllText(string path)
|
||||
{
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
public byte[] ReadAllBytes(string path)
|
||||
{
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public void WriteAllText(string path, string text, Encoding encoding)
|
||||
{
|
||||
File.WriteAllText(path, text, encoding);
|
||||
}
|
||||
|
||||
public void WriteAllText(string path, string text)
|
||||
{
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
public void WriteAllBytes(string path, byte[] bytes)
|
||||
{
|
||||
File.WriteAllBytes(path, bytes);
|
||||
}
|
||||
|
||||
public string ReadAllText(string path, Encoding encoding)
|
||||
{
|
||||
return File.ReadAllText(path, encoding);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
return Directory.EnumerateDirectories(path, "*", searchOption);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilePaths(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
return Directory.EnumerateFiles(path, "*", searchOption);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
|
||||
{
|
||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
return Directory.EnumerateFileSystemEntries(path, "*", searchOption);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.IO
|
||||
{
|
||||
public class WindowsFileSystem : ManagedFileSystem
|
||||
{
|
||||
public WindowsFileSystem(ILogger logger)
|
||||
: base(logger, true, true)
|
||||
{
|
||||
EnableFileSystemRequestConcat = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.Common.Implementations</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.Common.Implementations</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release Mono\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs">
|
||||
<Link>Properties\SharedVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="BaseApplicationHost.cs" />
|
||||
<Compile Include="BaseApplicationPaths.cs" />
|
||||
<Compile Include="Configuration\BaseConfigurationManager.cs" />
|
||||
<Compile Include="Configuration\ConfigurationHelper.cs" />
|
||||
<Compile Include="Cryptography\CryptographyProvider.cs" />
|
||||
<Compile Include="Devices\DeviceId.cs" />
|
||||
<Compile Include="HttpClientManager\HttpClientInfo.cs" />
|
||||
<Compile Include="HttpClientManager\HttpClientManager.cs" />
|
||||
<Compile Include="IO\IsoManager.cs" />
|
||||
<Compile Include="IO\ManagedFileSystem.cs" />
|
||||
<Compile Include="IO\WindowsFileSystem.cs" />
|
||||
<Compile Include="Networking\BaseNetworkManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ScheduledTasks\DailyTrigger.cs" />
|
||||
<Compile Include="ScheduledTasks\IntervalTrigger.cs" />
|
||||
<Compile Include="ScheduledTasks\ScheduledTaskWorker.cs" />
|
||||
<Compile Include="ScheduledTasks\StartupTrigger.cs" />
|
||||
<Compile Include="ScheduledTasks\SystemEventTrigger.cs" />
|
||||
<Compile Include="ScheduledTasks\TaskManager.cs" />
|
||||
<Compile Include="ScheduledTasks\Tasks\DeleteCacheFileTask.cs" />
|
||||
<Compile Include="ScheduledTasks\Tasks\DeleteLogFileTask.cs" />
|
||||
<Compile Include="ScheduledTasks\Tasks\ReloadLoggerFileTask.cs" />
|
||||
<Compile Include="ScheduledTasks\WeeklyTrigger.cs" />
|
||||
<Compile Include="Serialization\XmlSerializer.cs" />
|
||||
<Compile Include="Updates\GithubUpdater.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent />
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
@ -1,385 +0,0 @@
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Networking
|
||||
{
|
||||
public abstract class BaseNetworkManager
|
||||
{
|
||||
protected ILogger Logger { get; private set; }
|
||||
private DateTime _lastRefresh;
|
||||
|
||||
protected BaseNetworkManager(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private List<IPAddress> _localIpAddresses;
|
||||
private readonly object _localIpAddressSyncLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the machine's local ip address
|
||||
/// </summary>
|
||||
/// <returns>IPAddress.</returns>
|
||||
public IEnumerable<IPAddress> GetLocalIpAddresses()
|
||||
{
|
||||
const int cacheMinutes = 5;
|
||||
|
||||
lock (_localIpAddressSyncLock)
|
||||
{
|
||||
var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes;
|
||||
|
||||
if (_localIpAddresses == null || forceRefresh)
|
||||
{
|
||||
var addresses = GetLocalIpAddressesInternal().ToList();
|
||||
|
||||
_localIpAddresses = addresses;
|
||||
_lastRefresh = DateTime.UtcNow;
|
||||
|
||||
return addresses;
|
||||
}
|
||||
}
|
||||
|
||||
return _localIpAddresses;
|
||||
}
|
||||
|
||||
private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
|
||||
{
|
||||
var list = GetIPsDefault()
|
||||
.ToList();
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
list.AddRange(GetLocalIpAddressesFallback());
|
||||
}
|
||||
|
||||
return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
|
||||
}
|
||||
|
||||
private bool FilterIpAddress(IPAddress address)
|
||||
{
|
||||
var addressString = address.ToString ();
|
||||
|
||||
if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsInPrivateAddressSpace(string endpoint)
|
||||
{
|
||||
if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle ipv4 mapped to ipv6
|
||||
endpoint = endpoint.Replace("::ffff:", string.Empty);
|
||||
|
||||
// Private address space:
|
||||
// http://en.wikipedia.org/wiki/Private_network
|
||||
|
||||
if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Is172AddressPrivate(endpoint);
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
|
||||
endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
|
||||
endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) ||
|
||||
endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool Is172AddressPrivate(string endpoint)
|
||||
{
|
||||
for (var i = 16; i <= 31; i++)
|
||||
{
|
||||
if (endpoint.StartsWith("172." + i.ToString(CultureInfo.InvariantCulture) + ".", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsInLocalNetwork(string endpoint)
|
||||
{
|
||||
return IsInLocalNetworkInternal(endpoint, true);
|
||||
}
|
||||
|
||||
public bool IsInLocalNetworkInternal(string endpoint, bool resolveHost)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(endpoint))
|
||||
{
|
||||
throw new ArgumentNullException("endpoint");
|
||||
}
|
||||
|
||||
IPAddress address;
|
||||
if (IPAddress.TryParse(endpoint, out address))
|
||||
{
|
||||
var addressString = address.ToString();
|
||||
|
||||
int lengthMatch = 100;
|
||||
if (address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
lengthMatch = 4;
|
||||
if (IsInPrivateAddressSpace(addressString))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
lengthMatch = 10;
|
||||
if (IsInPrivateAddressSpace(endpoint))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Should be even be doing this with ipv6?
|
||||
if (addressString.Length >= lengthMatch)
|
||||
{
|
||||
var prefix = addressString.Substring(0, lengthMatch);
|
||||
|
||||
if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (resolveHost)
|
||||
{
|
||||
Uri uri;
|
||||
if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri))
|
||||
{
|
||||
try
|
||||
{
|
||||
var host = uri.DnsSafeHost;
|
||||
Logger.Debug("Resolving host {0}", host);
|
||||
|
||||
address = GetIpAddresses(host).FirstOrDefault();
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
Logger.Debug("{0} resolved to {1}", host, address);
|
||||
|
||||
return IsInLocalNetworkInternal(address.ToString(), false);
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Can happen with reverse proxy or IIS url rewriting
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error resovling hostname", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<IPAddress> GetIpAddresses(string hostName)
|
||||
{
|
||||
return Dns.GetHostAddresses(hostName);
|
||||
}
|
||||
|
||||
private List<IPAddress> GetIPsDefault()
|
||||
{
|
||||
NetworkInterface[] interfaces;
|
||||
|
||||
try
|
||||
{
|
||||
interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
|
||||
return new List<IPAddress>();
|
||||
}
|
||||
|
||||
return interfaces.SelectMany(network => {
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
|
||||
|
||||
var properties = network.GetIPProperties();
|
||||
|
||||
return properties.UnicastAddresses
|
||||
.Where(i => i.IsDnsEligible)
|
||||
.Select(i => i.Address)
|
||||
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error querying network interface", ex);
|
||||
return new List<IPAddress>();
|
||||
}
|
||||
|
||||
}).DistinctBy(i => i.ToString())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<IPAddress> GetLocalIpAddressesFallback()
|
||||
{
|
||||
var host = Dns.GetHostEntry(Dns.GetHostName());
|
||||
|
||||
// Reverse them because the last one is usually the correct one
|
||||
// It's not fool-proof so ultimately the consumer will have to examine them and decide
|
||||
return host.AddressList
|
||||
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
|
||||
.Reverse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random port number that is currently available
|
||||
/// </summary>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int GetRandomUnusedPort()
|
||||
{
|
||||
var listener = new TcpListener(IPAddress.Any, 0);
|
||||
listener.Start();
|
||||
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns MAC Address from first Network Card in Computer
|
||||
/// </summary>
|
||||
/// <returns>[string] MAC Address</returns>
|
||||
public string GetMacAddress()
|
||||
{
|
||||
return NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
||||
.Select(i => BitConverter.ToString(i.GetPhysicalAddress().GetAddressBytes()))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified endpointstring.
|
||||
/// </summary>
|
||||
/// <param name="endpointstring">The endpointstring.</param>
|
||||
/// <returns>IPEndPoint.</returns>
|
||||
public IPEndPoint Parse(string endpointstring)
|
||||
{
|
||||
return Parse(endpointstring, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified endpointstring.
|
||||
/// </summary>
|
||||
/// <param name="endpointstring">The endpointstring.</param>
|
||||
/// <param name="defaultport">The defaultport.</param>
|
||||
/// <returns>IPEndPoint.</returns>
|
||||
/// <exception cref="System.ArgumentException">Endpoint descriptor may not be empty.</exception>
|
||||
/// <exception cref="System.FormatException"></exception>
|
||||
private static IPEndPoint Parse(string endpointstring, int defaultport)
|
||||
{
|
||||
if (String.IsNullOrEmpty(endpointstring)
|
||||
|| endpointstring.Trim().Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Endpoint descriptor may not be empty.");
|
||||
}
|
||||
|
||||
if (defaultport != -1 &&
|
||||
(defaultport < IPEndPoint.MinPort
|
||||
|| defaultport > IPEndPoint.MaxPort))
|
||||
{
|
||||
throw new ArgumentException(String.Format("Invalid default port '{0}'", defaultport));
|
||||
}
|
||||
|
||||
string[] values = endpointstring.Split(new char[] { ':' });
|
||||
IPAddress ipaddy;
|
||||
int port = -1;
|
||||
|
||||
//check if we have an IPv6 or ports
|
||||
if (values.Length <= 2) // ipv4 or hostname
|
||||
{
|
||||
port = values.Length == 1 ? defaultport : GetPort(values[1]);
|
||||
|
||||
//try to use the address as IPv4, otherwise get hostname
|
||||
if (!IPAddress.TryParse(values[0], out ipaddy))
|
||||
ipaddy = GetIPfromHost(values[0]);
|
||||
}
|
||||
else if (values.Length > 2) //ipv6
|
||||
{
|
||||
//could [a:b:c]:d
|
||||
if (values[0].StartsWith("[") && values[values.Length - 2].EndsWith("]"))
|
||||
{
|
||||
string ipaddressstring = String.Join(":", values.Take(values.Length - 1).ToArray());
|
||||
ipaddy = IPAddress.Parse(ipaddressstring);
|
||||
port = GetPort(values[values.Length - 1]);
|
||||
}
|
||||
else //[a:b:c] or a:b:c
|
||||
{
|
||||
ipaddy = IPAddress.Parse(endpointstring);
|
||||
port = defaultport;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException(String.Format("Invalid endpoint ipaddress '{0}'", endpointstring));
|
||||
}
|
||||
|
||||
if (port == -1)
|
||||
throw new ArgumentException(String.Format("No port specified: '{0}'", endpointstring));
|
||||
|
||||
return new IPEndPoint(ipaddy, port);
|
||||
}
|
||||
|
||||
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port.
|
||||
/// </summary>
|
||||
/// <param name="p">The p.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
/// <exception cref="System.FormatException"></exception>
|
||||
private static int GetPort(string p)
|
||||
{
|
||||
int port;
|
||||
|
||||
if (!Int32.TryParse(p, out port)
|
||||
|| port < IPEndPoint.MinPort
|
||||
|| port > IPEndPoint.MaxPort)
|
||||
{
|
||||
throw new FormatException(String.Format("Invalid end point port '{0}'", p));
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the I pfrom host.
|
||||
/// </summary>
|
||||
/// <param name="p">The p.</param>
|
||||
/// <returns>IPAddress.</returns>
|
||||
/// <exception cref="System.ArgumentException"></exception>
|
||||
private static IPAddress GetIPfromHost(string p)
|
||||
{
|
||||
var hosts = Dns.GetHostAddresses(p);
|
||||
|
||||
if (hosts == null || hosts.Length == 0)
|
||||
throw new ArgumentException(String.Format("Host not found: {0}", p));
|
||||
|
||||
return hosts[0];
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
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.Implementations")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.Common.Implementations")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[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)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("fc7d85c6-0fe7-4db6-8158-54f7b18f17cd")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a task trigger that fires everyday
|
||||
/// </summary>
|
||||
public class DailyTrigger : ITaskTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the time of day to trigger the task to run
|
||||
/// </summary>
|
||||
/// <value>The time of day.</value>
|
||||
public TimeSpan TimeOfDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timer.
|
||||
/// </summary>
|
||||
/// <value>The timer.</value>
|
||||
private Timer Timer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution properties of this task.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The execution properties of this task.
|
||||
/// </value>
|
||||
public TaskExecutionOptions TaskOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stars waiting for the trigger action
|
||||
/// </summary>
|
||||
/// <param name="lastResult">The last result.</param>
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
||||
{
|
||||
DisposeTimer();
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
||||
var triggerDate = now.TimeOfDay > TimeOfDay ? now.Date.AddDays(1) : now.Date;
|
||||
triggerDate = triggerDate.Add(TimeOfDay);
|
||||
|
||||
var dueTime = triggerDate - now;
|
||||
|
||||
logger.Info("Daily trigger for {0} set to fire at {1}, which is {2} minutes from now.", taskName, triggerDate.ToString(), dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
Timer = new Timer(state => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops waiting for the trigger action
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
DisposeTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the timer.
|
||||
/// </summary>
|
||||
private void DisposeTimer()
|
||||
{
|
||||
if (Timer != null)
|
||||
{
|
||||
Timer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [triggered].
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Called when [triggered].
|
||||
/// </summary>
|
||||
private void OnTriggered()
|
||||
{
|
||||
if (Triggered != null)
|
||||
{
|
||||
Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a task trigger that runs repeatedly on an interval
|
||||
/// </summary>
|
||||
public class IntervalTrigger : ITaskTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the interval.
|
||||
/// </summary>
|
||||
/// <value>The interval.</value>
|
||||
public TimeSpan Interval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timer.
|
||||
/// </summary>
|
||||
/// <value>The timer.</value>
|
||||
private Timer Timer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution properties of this task.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The execution properties of this task.
|
||||
/// </value>
|
||||
public TaskExecutionOptions TaskOptions { get; set; }
|
||||
|
||||
private DateTime _lastStartDate;
|
||||
|
||||
/// <summary>
|
||||
/// Stars waiting for the trigger action
|
||||
/// </summary>
|
||||
/// <param name="lastResult">The last result.</param>
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
||||
{
|
||||
DisposeTimer();
|
||||
|
||||
DateTime triggerDate;
|
||||
|
||||
if (lastResult == null)
|
||||
{
|
||||
// Task has never been completed before
|
||||
triggerDate = DateTime.UtcNow.AddHours(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerDate = new[] { lastResult.EndTimeUtc, _lastStartDate }.Max().Add(Interval);
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow > triggerDate)
|
||||
{
|
||||
triggerDate = DateTime.UtcNow.AddMinutes(1);
|
||||
}
|
||||
|
||||
var dueTime = triggerDate - DateTime.UtcNow;
|
||||
var maxDueTime = TimeSpan.FromDays(7);
|
||||
|
||||
if (dueTime > maxDueTime)
|
||||
{
|
||||
dueTime = maxDueTime;
|
||||
}
|
||||
|
||||
Timer = new Timer(state => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops waiting for the trigger action
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
DisposeTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the timer.
|
||||
/// </summary>
|
||||
private void DisposeTimer()
|
||||
{
|
||||
if (Timer != null)
|
||||
{
|
||||
Timer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [triggered].
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Called when [triggered].
|
||||
/// </summary>
|
||||
private void OnTriggered()
|
||||
{
|
||||
DisposeTimer();
|
||||
|
||||
if (Triggered != null)
|
||||
{
|
||||
_lastStartDate = DateTime.UtcNow;
|
||||
Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,781 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ScheduledTaskWorker
|
||||
/// </summary>
|
||||
public class ScheduledTaskWorker : IScheduledTaskWorker
|
||||
{
|
||||
public event EventHandler<GenericEventArgs<double>> TaskProgress;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scheduled task.
|
||||
/// </summary>
|
||||
/// <value>The scheduled task.</value>
|
||||
public IScheduledTask ScheduledTask { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the json serializer.
|
||||
/// </summary>
|
||||
/// <value>The json serializer.</value>
|
||||
private IJsonSerializer JsonSerializer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
private IApplicationPaths ApplicationPaths { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
private ILogger Logger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the task manager.
|
||||
/// </summary>
|
||||
/// <value>The task manager.</value>
|
||||
private ITaskManager TaskManager { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
|
||||
/// </summary>
|
||||
/// <param name="scheduledTask">The scheduled task.</param>
|
||||
/// <param name="applicationPaths">The application paths.</param>
|
||||
/// <param name="taskManager">The task manager.</param>
|
||||
/// <param name="jsonSerializer">The json serializer.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// scheduledTask
|
||||
/// or
|
||||
/// applicationPaths
|
||||
/// or
|
||||
/// taskManager
|
||||
/// or
|
||||
/// jsonSerializer
|
||||
/// or
|
||||
/// logger
|
||||
/// </exception>
|
||||
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
throw new ArgumentNullException("scheduledTask");
|
||||
}
|
||||
if (applicationPaths == null)
|
||||
{
|
||||
throw new ArgumentNullException("applicationPaths");
|
||||
}
|
||||
if (taskManager == null)
|
||||
{
|
||||
throw new ArgumentNullException("taskManager");
|
||||
}
|
||||
if (jsonSerializer == null)
|
||||
{
|
||||
throw new ArgumentNullException("jsonSerializer");
|
||||
}
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException("logger");
|
||||
}
|
||||
|
||||
ScheduledTask = scheduledTask;
|
||||
ApplicationPaths = applicationPaths;
|
||||
TaskManager = taskManager;
|
||||
JsonSerializer = jsonSerializer;
|
||||
Logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
InitTriggerEvents();
|
||||
}
|
||||
|
||||
private bool _readFromFile = false;
|
||||
/// <summary>
|
||||
/// The _last execution result
|
||||
/// </summary>
|
||||
private TaskResult _lastExecutionResult;
|
||||
/// <summary>
|
||||
/// The _last execution result sync lock
|
||||
/// </summary>
|
||||
private readonly object _lastExecutionResultSyncLock = new object();
|
||||
/// <summary>
|
||||
/// Gets the last execution result.
|
||||
/// </summary>
|
||||
/// <value>The last execution result.</value>
|
||||
public TaskResult LastExecutionResult
|
||||
{
|
||||
get
|
||||
{
|
||||
var path = GetHistoryFilePath();
|
||||
|
||||
lock (_lastExecutionResultSyncLock)
|
||||
{
|
||||
if (_lastExecutionResult == null && !_readFromFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
_lastExecutionResult = JsonSerializer.DeserializeFromFile<TaskResult>(path);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
// File doesn't exist. No biggie
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// File doesn't exist. No biggie
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error deserializing {0}", ex, path);
|
||||
}
|
||||
_readFromFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
return _lastExecutionResult;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_lastExecutionResult = value;
|
||||
|
||||
var path = GetHistoryFilePath();
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_lastExecutionResultSyncLock)
|
||||
{
|
||||
JsonSerializer.SerializeToFile(value, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name
|
||||
{
|
||||
get { return ScheduledTask.Name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public string Description
|
||||
{
|
||||
get { return ScheduledTask.Description; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category.
|
||||
/// </summary>
|
||||
/// <value>The category.</value>
|
||||
public string Category
|
||||
{
|
||||
get { return ScheduledTask.Category; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current cancellation token
|
||||
/// </summary>
|
||||
/// <value>The current cancellation token source.</value>
|
||||
private CancellationTokenSource CurrentCancellationTokenSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current execution start time.
|
||||
/// </summary>
|
||||
/// <value>The current execution start time.</value>
|
||||
private DateTime CurrentExecutionStartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state.
|
||||
/// </summary>
|
||||
/// <value>The state.</value>
|
||||
public TaskState State
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CurrentCancellationTokenSource != null)
|
||||
{
|
||||
return CurrentCancellationTokenSource.IsCancellationRequested
|
||||
? TaskState.Cancelling
|
||||
: TaskState.Running;
|
||||
}
|
||||
|
||||
return TaskState.Idle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current progress.
|
||||
/// </summary>
|
||||
/// <value>The current progress.</value>
|
||||
public double? CurrentProgress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _triggers
|
||||
/// </summary>
|
||||
private Tuple<TaskTriggerInfo,ITaskTrigger>[] _triggers;
|
||||
/// <summary>
|
||||
/// Gets the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <value>The triggers.</value>
|
||||
private Tuple<TaskTriggerInfo, ITaskTrigger>[] InternalTriggers
|
||||
{
|
||||
get
|
||||
{
|
||||
return _triggers;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
// Cleanup current triggers
|
||||
if (_triggers != null)
|
||||
{
|
||||
DisposeTriggers();
|
||||
}
|
||||
|
||||
_triggers = value.ToArray();
|
||||
|
||||
ReloadTriggerEvents(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <value>The triggers.</value>
|
||||
/// <exception cref="System.ArgumentNullException">value</exception>
|
||||
public TaskTriggerInfo[] Triggers
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalTriggers.Select(i => i.Item1).ToArray();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
SaveTriggers(value);
|
||||
|
||||
InternalTriggers = value.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _id
|
||||
/// </summary>
|
||||
private string _id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id.
|
||||
/// </summary>
|
||||
/// <value>The unique id.</value>
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_id == null)
|
||||
{
|
||||
_id = ScheduledTask.GetType().FullName.GetMD5().ToString("N");
|
||||
}
|
||||
|
||||
return _id;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitTriggerEvents()
|
||||
{
|
||||
_triggers = LoadTriggers();
|
||||
ReloadTriggerEvents(true);
|
||||
}
|
||||
|
||||
public void ReloadTriggerEvents()
|
||||
{
|
||||
ReloadTriggerEvents(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the trigger events.
|
||||
/// </summary>
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
private void ReloadTriggerEvents(bool isApplicationStartup)
|
||||
{
|
||||
foreach (var triggerInfo in InternalTriggers)
|
||||
{
|
||||
var trigger = triggerInfo.Item2;
|
||||
|
||||
trigger.Stop();
|
||||
|
||||
trigger.Triggered -= trigger_Triggered;
|
||||
trigger.Triggered += trigger_Triggered;
|
||||
trigger.Start(LastExecutionResult, Logger, Name, isApplicationStartup);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the Triggered event of the trigger control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
||||
async void trigger_Triggered(object sender, GenericEventArgs<TaskExecutionOptions> e)
|
||||
{
|
||||
var trigger = (ITaskTrigger)sender;
|
||||
|
||||
var configurableTask = ScheduledTask as IConfigurableScheduledTask;
|
||||
|
||||
if (configurableTask != null && !configurableTask.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info("{0} fired for task: {1}", trigger.GetType().Name, Name);
|
||||
|
||||
trigger.Stop();
|
||||
|
||||
TaskManager.QueueScheduledTask(ScheduledTask, e.Argument);
|
||||
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
trigger.Start(LastExecutionResult, Logger, Name, false);
|
||||
}
|
||||
|
||||
private Task _currentTask;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the task
|
||||
/// </summary>
|
||||
/// <param name="options">Task options.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">Cannot execute a Task that is already running</exception>
|
||||
public async Task Execute(TaskExecutionOptions options)
|
||||
{
|
||||
var task = ExecuteInternal(options);
|
||||
|
||||
_currentTask = task;
|
||||
|
||||
try
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_currentTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteInternal(TaskExecutionOptions options)
|
||||
{
|
||||
// Cancel the current execution, if any
|
||||
if (CurrentCancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot execute a Task that is already running");
|
||||
}
|
||||
|
||||
var progress = new Progress<double>();
|
||||
|
||||
CurrentCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
Logger.Info("Executing {0}", Name);
|
||||
|
||||
((TaskManager)TaskManager).OnTaskExecuting(this);
|
||||
|
||||
progress.ProgressChanged += progress_ProgressChanged;
|
||||
|
||||
TaskCompletionStatus status;
|
||||
CurrentExecutionStartTime = DateTime.UtcNow;
|
||||
|
||||
Exception failureException = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (options != null && options.MaxRuntimeMs.HasValue)
|
||||
{
|
||||
CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value);
|
||||
}
|
||||
|
||||
var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
|
||||
|
||||
await localTask.ConfigureAwait(false);
|
||||
|
||||
status = TaskCompletionStatus.Completed;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
status = TaskCompletionStatus.Cancelled;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error", ex);
|
||||
|
||||
failureException = ex;
|
||||
|
||||
status = TaskCompletionStatus.Failed;
|
||||
}
|
||||
|
||||
var startTime = CurrentExecutionStartTime;
|
||||
var endTime = DateTime.UtcNow;
|
||||
|
||||
progress.ProgressChanged -= progress_ProgressChanged;
|
||||
CurrentCancellationTokenSource.Dispose();
|
||||
CurrentCancellationTokenSource = null;
|
||||
CurrentProgress = null;
|
||||
|
||||
OnTaskCompleted(startTime, endTime, status, failureException);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Progress_s the progress changed.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void progress_ProgressChanged(object sender, double e)
|
||||
{
|
||||
CurrentProgress = e;
|
||||
|
||||
EventHelper.FireEventIfNotNull(TaskProgress, this, new GenericEventArgs<double>
|
||||
{
|
||||
Argument = e
|
||||
|
||||
}, Logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the task if it is currently executing
|
||||
/// </summary>
|
||||
/// <exception cref="System.InvalidOperationException">Cannot cancel a Task unless it is in the Running state.</exception>
|
||||
public void Cancel()
|
||||
{
|
||||
if (State != TaskState.Running)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot cancel a Task unless it is in the Running state.");
|
||||
}
|
||||
|
||||
CancelIfRunning();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels if running.
|
||||
/// </summary>
|
||||
public void CancelIfRunning()
|
||||
{
|
||||
if (State == TaskState.Running)
|
||||
{
|
||||
Logger.Info("Attempting to cancel Scheduled Task {0}", Name);
|
||||
CurrentCancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scheduled tasks configuration directory.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetScheduledTasksConfigurationDirectory()
|
||||
{
|
||||
return Path.Combine(ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scheduled tasks data directory.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetScheduledTasksDataDirectory()
|
||||
{
|
||||
return Path.Combine(ApplicationPaths.DataPath, "ScheduledTasks");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the history file path.
|
||||
/// </summary>
|
||||
/// <value>The history file path.</value>
|
||||
private string GetHistoryFilePath()
|
||||
{
|
||||
return Path.Combine(GetScheduledTasksDataDirectory(), new Guid(Id) + ".js");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration file path.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetConfigurationFilePath()
|
||||
{
|
||||
return Path.Combine(GetScheduledTasksConfigurationDirectory(), new Guid(Id) + ".js");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the triggers.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
private Tuple<TaskTriggerInfo, ITaskTrigger>[] LoadTriggers()
|
||||
{
|
||||
var settings = LoadTriggerSettings();
|
||||
|
||||
return settings.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
|
||||
}
|
||||
|
||||
private TaskTriggerInfo[] LoadTriggerSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath())
|
||||
.ToArray();
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// File doesn't exist. No biggie. Return defaults.
|
||||
return ScheduledTask.GetDefaultTriggers().ToArray();
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
// File doesn't exist. No biggie. Return defaults.
|
||||
return ScheduledTask.GetDefaultTriggers().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the triggers.
|
||||
/// </summary>
|
||||
/// <param name="triggers">The triggers.</param>
|
||||
private void SaveTriggers(TaskTriggerInfo[] triggers)
|
||||
{
|
||||
var path = GetConfigurationFilePath();
|
||||
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
JsonSerializer.SerializeToFile(triggers, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [task completed].
|
||||
/// </summary>
|
||||
/// <param name="startTime">The start time.</param>
|
||||
/// <param name="endTime">The end time.</param>
|
||||
/// <param name="status">The status.</param>
|
||||
private void OnTaskCompleted(DateTime startTime, DateTime endTime, TaskCompletionStatus status, Exception ex)
|
||||
{
|
||||
var elapsedTime = endTime - startTime;
|
||||
|
||||
Logger.Info("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds);
|
||||
|
||||
var result = new TaskResult
|
||||
{
|
||||
StartTimeUtc = startTime,
|
||||
EndTimeUtc = endTime,
|
||||
Status = status,
|
||||
Name = Name,
|
||||
Id = Id
|
||||
};
|
||||
|
||||
result.Key = ScheduledTask.Key;
|
||||
|
||||
if (ex != null)
|
||||
{
|
||||
result.ErrorMessage = ex.Message;
|
||||
result.LongErrorMessage = ex.StackTrace;
|
||||
}
|
||||
|
||||
LastExecutionResult = result;
|
||||
|
||||
((TaskManager)TaskManager).OnTaskCompleted(this, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
DisposeTriggers();
|
||||
|
||||
var wassRunning = State == TaskState.Running;
|
||||
var startTime = CurrentExecutionStartTime;
|
||||
|
||||
var token = CurrentCancellationTokenSource;
|
||||
if (token != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Info(Name + ": Cancelling");
|
||||
token.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error calling CancellationToken.Cancel();", ex);
|
||||
}
|
||||
}
|
||||
var task = _currentTask;
|
||||
if (task != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Info(Name + ": Waiting on Task");
|
||||
var exited = Task.WaitAll(new[] { task }, 2000);
|
||||
|
||||
if (exited)
|
||||
{
|
||||
Logger.Info(Name + ": Task exited");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info(Name + ": Timed out waiting for task to stop");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error calling Task.WaitAll();", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Debug(Name + ": Disposing CancellationToken");
|
||||
token.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error calling CancellationToken.Dispose();", ex);
|
||||
}
|
||||
}
|
||||
if (wassRunning)
|
||||
{
|
||||
OnTaskCompleted(startTime, DateTime.UtcNow, TaskCompletionStatus.Aborted, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger
|
||||
/// </summary>
|
||||
/// <param name="info">The info.</param>
|
||||
/// <returns>BaseTaskTrigger.</returns>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
/// <exception cref="System.ArgumentException">Invalid trigger type: + info.Type</exception>
|
||||
public static ITaskTrigger GetTrigger(TaskTriggerInfo info)
|
||||
{
|
||||
var options = new TaskExecutionOptions
|
||||
{
|
||||
MaxRuntimeMs = info.MaxRuntimeMs
|
||||
};
|
||||
|
||||
if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.TimeOfDayTicks.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new DailyTrigger
|
||||
{
|
||||
TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(WeeklyTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.TimeOfDayTicks.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
if (!info.DayOfWeek.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new WeeklyTrigger
|
||||
{
|
||||
TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
|
||||
DayOfWeek = info.DayOfWeek.Value,
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(IntervalTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.IntervalTicks.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new IntervalTrigger
|
||||
{
|
||||
Interval = TimeSpan.FromTicks(info.IntervalTicks.Value),
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(SystemEventTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!info.SystemEvent.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return new SystemEventTrigger
|
||||
{
|
||||
SystemEvent = info.SystemEvent.Value,
|
||||
TaskOptions = options
|
||||
};
|
||||
}
|
||||
|
||||
if (info.Type.Equals(typeof(StartupTrigger).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new StartupTrigger();
|
||||
}
|
||||
|
||||
throw new ArgumentException("Unrecognized trigger type: " + info.Type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes each trigger
|
||||
/// </summary>
|
||||
private void DisposeTriggers()
|
||||
{
|
||||
foreach (var triggerInfo in InternalTriggers)
|
||||
{
|
||||
var trigger = triggerInfo.Item2;
|
||||
trigger.Triggered -= trigger_Triggered;
|
||||
trigger.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class StartupTaskTrigger
|
||||
/// </summary>
|
||||
public class StartupTrigger : ITaskTrigger
|
||||
{
|
||||
public int DelayMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution properties of this task.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The execution properties of this task.
|
||||
/// </value>
|
||||
public TaskExecutionOptions TaskOptions { get; set; }
|
||||
|
||||
public StartupTrigger()
|
||||
{
|
||||
DelayMs = 3000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stars waiting for the trigger action
|
||||
/// </summary>
|
||||
/// <param name="lastResult">The last result.</param>
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
public async void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
||||
{
|
||||
if (isApplicationStartup)
|
||||
{
|
||||
await Task.Delay(DelayMs).ConfigureAwait(false);
|
||||
|
||||
OnTriggered();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops waiting for the trigger action
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [triggered].
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Called when [triggered].
|
||||
/// </summary>
|
||||
private void OnTriggered()
|
||||
{
|
||||
if (Triggered != null)
|
||||
{
|
||||
Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class SystemEventTrigger
|
||||
/// </summary>
|
||||
public class SystemEventTrigger : ITaskTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the system event.
|
||||
/// </summary>
|
||||
/// <value>The system event.</value>
|
||||
public SystemEvent SystemEvent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution properties of this task.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The execution properties of this task.
|
||||
/// </value>
|
||||
public TaskExecutionOptions TaskOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stars waiting for the trigger action
|
||||
/// </summary>
|
||||
/// <param name="lastResult">The last result.</param>
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
||||
{
|
||||
switch (SystemEvent)
|
||||
{
|
||||
case SystemEvent.WakeFromSleep:
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops waiting for the trigger action
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the PowerModeChanged event of the SystemEvents control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="PowerModeChangedEventArgs" /> instance containing the event data.</param>
|
||||
async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
if (e.Mode == PowerModes.Resume && SystemEvent == SystemEvent.WakeFromSleep)
|
||||
{
|
||||
// This value is a bit arbitrary, but add a delay to help ensure network connections have been restored before running the task
|
||||
await Task.Delay(10000).ConfigureAwait(false);
|
||||
|
||||
OnTriggered();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [triggered].
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Called when [triggered].
|
||||
/// </summary>
|
||||
private void OnTriggered()
|
||||
{
|
||||
if (Triggered != null)
|
||||
{
|
||||
Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,361 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class TaskManager
|
||||
/// </summary>
|
||||
public class TaskManager : ITaskManager
|
||||
{
|
||||
public event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
|
||||
public event EventHandler<TaskCompletionEventArgs> TaskCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Scheduled Tasks
|
||||
/// </summary>
|
||||
/// <value>The scheduled tasks.</value>
|
||||
public IScheduledTaskWorker[] ScheduledTasks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _task queue
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<Tuple<Type, TaskExecutionOptions>> _taskQueue =
|
||||
new ConcurrentQueue<Tuple<Type, TaskExecutionOptions>>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the json serializer.
|
||||
/// </summary>
|
||||
/// <value>The json serializer.</value>
|
||||
private IJsonSerializer JsonSerializer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
private IApplicationPaths ApplicationPaths { get; set; }
|
||||
|
||||
private readonly ISystemEvents _systemEvents;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
private ILogger Logger { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private bool _suspendTriggers;
|
||||
|
||||
public bool SuspendTriggers
|
||||
{
|
||||
get { return _suspendTriggers; }
|
||||
set
|
||||
{
|
||||
Logger.Info("Setting SuspendTriggers to {0}", value);
|
||||
var executeQueued = _suspendTriggers && !value;
|
||||
|
||||
_suspendTriggers = value;
|
||||
|
||||
if (executeQueued)
|
||||
{
|
||||
ExecuteQueuedTasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TaskManager" /> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationPaths">The application paths.</param>
|
||||
/// <param name="jsonSerializer">The json serializer.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="System.ArgumentException">kernel</exception>
|
||||
public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem, ISystemEvents systemEvents)
|
||||
{
|
||||
ApplicationPaths = applicationPaths;
|
||||
JsonSerializer = jsonSerializer;
|
||||
Logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_systemEvents = systemEvents;
|
||||
|
||||
ScheduledTasks = new IScheduledTaskWorker[] { };
|
||||
}
|
||||
|
||||
private void BindToSystemEvent()
|
||||
{
|
||||
_systemEvents.Resume += _systemEvents_Resume;
|
||||
}
|
||||
|
||||
private void _systemEvents_Resume(object sender, EventArgs e)
|
||||
{
|
||||
foreach (var task in ScheduledTasks)
|
||||
{
|
||||
task.ReloadTriggerEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels if running and queue.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="options">Task options.</param>
|
||||
public void CancelIfRunningAndQueue<T>(TaskExecutionOptions options)
|
||||
where T : IScheduledTask
|
||||
{
|
||||
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
((ScheduledTaskWorker)task).CancelIfRunning();
|
||||
|
||||
QueueScheduledTask<T>(options);
|
||||
}
|
||||
|
||||
public void CancelIfRunningAndQueue<T>()
|
||||
where T : IScheduledTask
|
||||
{
|
||||
CancelIfRunningAndQueue<T>(new TaskExecutionOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels if running
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public void CancelIfRunning<T>()
|
||||
where T : IScheduledTask
|
||||
{
|
||||
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
((ScheduledTaskWorker)task).CancelIfRunning();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the scheduled task.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="options">Task options</param>
|
||||
public void QueueScheduledTask<T>(TaskExecutionOptions options)
|
||||
where T : IScheduledTask
|
||||
{
|
||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", typeof(T).Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueScheduledTask(scheduledTask, options);
|
||||
}
|
||||
}
|
||||
|
||||
public void QueueScheduledTask<T>()
|
||||
where T : IScheduledTask
|
||||
{
|
||||
QueueScheduledTask<T>(new TaskExecutionOptions());
|
||||
}
|
||||
|
||||
public void QueueIfNotRunning<T>()
|
||||
where T : IScheduledTask
|
||||
{
|
||||
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
|
||||
if (task.State != TaskState.Running)
|
||||
{
|
||||
QueueScheduledTask<T>(new TaskExecutionOptions());
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute<T>()
|
||||
where T : IScheduledTask
|
||||
{
|
||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
Logger.Error("Unable to find scheduled task of type {0} in Execute.", typeof(T).Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = scheduledTask.ScheduledTask.GetType();
|
||||
|
||||
Logger.Info("Queueing task {0}", type.Name);
|
||||
|
||||
lock (_taskQueue)
|
||||
{
|
||||
if (scheduledTask.State == TaskState.Idle)
|
||||
{
|
||||
Execute(scheduledTask, new TaskExecutionOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the scheduled task.
|
||||
/// </summary>
|
||||
/// <param name="task">The task.</param>
|
||||
/// <param name="options">The task options.</param>
|
||||
public void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options)
|
||||
{
|
||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
|
||||
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", task.GetType().Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueScheduledTask(scheduledTask, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the scheduled task.
|
||||
/// </summary>
|
||||
/// <param name="task">The task.</param>
|
||||
/// <param name="options">The task options.</param>
|
||||
private void QueueScheduledTask(IScheduledTaskWorker task, TaskExecutionOptions options)
|
||||
{
|
||||
var type = task.ScheduledTask.GetType();
|
||||
|
||||
Logger.Info("Queueing task {0}", type.Name);
|
||||
|
||||
lock (_taskQueue)
|
||||
{
|
||||
if (task.State == TaskState.Idle && !SuspendTriggers)
|
||||
{
|
||||
Execute(task, options);
|
||||
return;
|
||||
}
|
||||
|
||||
_taskQueue.Enqueue(new Tuple<Type, TaskExecutionOptions>(type, options));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the tasks.
|
||||
/// </summary>
|
||||
/// <param name="tasks">The tasks.</param>
|
||||
public void AddTasks(IEnumerable<IScheduledTask> tasks)
|
||||
{
|
||||
var myTasks = ScheduledTasks.ToList();
|
||||
|
||||
var list = tasks.ToList();
|
||||
myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem)));
|
||||
|
||||
ScheduledTasks = myTasks.ToArray();
|
||||
|
||||
BindToSystemEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool dispose)
|
||||
{
|
||||
foreach (var task in ScheduledTasks)
|
||||
{
|
||||
task.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Cancel(IScheduledTaskWorker task)
|
||||
{
|
||||
((ScheduledTaskWorker)task).Cancel();
|
||||
}
|
||||
|
||||
public Task Execute(IScheduledTaskWorker task, TaskExecutionOptions options)
|
||||
{
|
||||
return ((ScheduledTaskWorker)task).Execute(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [task executing].
|
||||
/// </summary>
|
||||
/// <param name="task">The task.</param>
|
||||
internal void OnTaskExecuting(IScheduledTaskWorker task)
|
||||
{
|
||||
EventHelper.FireEventIfNotNull(TaskExecuting, this, new GenericEventArgs<IScheduledTaskWorker>
|
||||
{
|
||||
Argument = task
|
||||
|
||||
}, Logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [task completed].
|
||||
/// </summary>
|
||||
/// <param name="task">The task.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result)
|
||||
{
|
||||
EventHelper.FireEventIfNotNull(TaskCompleted, task, new TaskCompletionEventArgs
|
||||
{
|
||||
Result = result,
|
||||
Task = task
|
||||
|
||||
}, Logger);
|
||||
|
||||
ExecuteQueuedTasks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the queued tasks.
|
||||
/// </summary>
|
||||
private void ExecuteQueuedTasks()
|
||||
{
|
||||
if (SuspendTriggers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info("ExecuteQueuedTasks");
|
||||
|
||||
// Execute queued tasks
|
||||
lock (_taskQueue)
|
||||
{
|
||||
var list = new List<Tuple<Type, TaskExecutionOptions>>();
|
||||
|
||||
Tuple<Type, TaskExecutionOptions> item;
|
||||
while (_taskQueue.TryDequeue(out item))
|
||||
{
|
||||
if (list.All(i => i.Item1 != item.Item1))
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var enqueuedType in list)
|
||||
{
|
||||
var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == enqueuedType.Item1);
|
||||
|
||||
if (scheduledTask.State == TaskState.Idle)
|
||||
{
|
||||
Execute(scheduledTask, enqueuedType.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes old cache files
|
||||
/// </summary>
|
||||
public class DeleteCacheFileTask : IScheduledTask, IConfigurableScheduledTask
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
private IApplicationPaths ApplicationPaths { get; set; }
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
|
||||
/// </summary>
|
||||
public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
ApplicationPaths = appPaths;
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return new[] {
|
||||
|
||||
// Every so often
|
||||
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the task to be executed
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
var minDateModified = DateTime.UtcNow.AddDays(-30);
|
||||
|
||||
try
|
||||
{
|
||||
DeleteCacheFilesFromDirectory(cancellationToken, ApplicationPaths.CachePath, minDateModified, progress);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
// No biggie here. Nothing to delete
|
||||
}
|
||||
|
||||
progress.Report(90);
|
||||
|
||||
minDateModified = DateTime.UtcNow.AddDays(-1);
|
||||
|
||||
try
|
||||
{
|
||||
DeleteCacheFilesFromDirectory(cancellationToken, ApplicationPaths.TempDirectory, minDateModified, progress);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
// No biggie here. Nothing to delete
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the cache files from directory with a last write time less than a given date
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The task cancellation token.</param>
|
||||
/// <param name="directory">The directory.</param>
|
||||
/// <param name="minDateModified">The min date modified.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
|
||||
{
|
||||
var filesToDelete = _fileSystem.GetFiles(directory, true)
|
||||
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||
.ToList();
|
||||
|
||||
var index = 0;
|
||||
|
||||
foreach (var file in filesToDelete)
|
||||
{
|
||||
double percent = index;
|
||||
percent /= filesToDelete.Count;
|
||||
|
||||
progress.Report(100 * percent);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
DeleteFile(file.FullName);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
DeleteEmptyFolders(directory);
|
||||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
private void DeleteEmptyFolders(string parent)
|
||||
{
|
||||
foreach (var directory in _fileSystem.GetDirectoryPaths(parent))
|
||||
{
|
||||
DeleteEmptyFolders(directory);
|
||||
if (!_fileSystem.GetFileSystemEntryPaths(directory).Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteDirectory(directory, false);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger.ErrorException("Error deleting directory {0}", ex, directory);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_logger.ErrorException("Error deleting directory {0}", ex, directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(path);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger.ErrorException("Error deleting file {0}", ex, path);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_logger.ErrorException("Error deleting file {0}", ex, path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the task
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name
|
||||
{
|
||||
get { return "Cache file cleanup"; }
|
||||
}
|
||||
|
||||
public string Key
|
||||
{
|
||||
get { return "DeleteCacheFiles"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public string Description
|
||||
{
|
||||
get { return "Deletes cache files no longer needed by the system"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category.
|
||||
/// </summary>
|
||||
/// <value>The category.</value>
|
||||
public string Category
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Maintenance";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is hidden.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
|
||||
public bool IsHidden
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsLogged
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes old log files
|
||||
/// </summary>
|
||||
public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration manager.
|
||||
/// </summary>
|
||||
/// <value>The configuration manager.</value>
|
||||
private IConfigurationManager ConfigurationManager { get; set; }
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem)
|
||||
{
|
||||
ConfigurationManager = configurationManager;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the triggers that define when the task will run
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return new[] {
|
||||
|
||||
// Every so often
|
||||
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the task to be executed
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
// Delete log files more than n days old
|
||||
var minDateModified = DateTime.UtcNow.AddDays(-ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
|
||||
|
||||
var filesToDelete = _fileSystem.GetFiles(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, true)
|
||||
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||
.ToList();
|
||||
|
||||
var index = 0;
|
||||
|
||||
foreach (var file in filesToDelete)
|
||||
{
|
||||
double percent = index;
|
||||
percent /= filesToDelete.Count;
|
||||
|
||||
progress.Report(100 * percent);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
_fileSystem.DeleteFile(file.FullName);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
progress.Report(100);
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public string Key
|
||||
{
|
||||
get { return "CleanLogFiles"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the task
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name
|
||||
{
|
||||
get { return "Log file cleanup"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public string Description
|
||||
{
|
||||
get { return string.Format("Deletes log files that are more than {0} days old.", ConfigurationManager.CommonConfiguration.LogFileRetentionDays); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category.
|
||||
/// </summary>
|
||||
/// <value>The category.</value>
|
||||
public string Category
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Maintenance";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is hidden.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
|
||||
public bool IsHidden
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsLogged
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ReloadLoggerFileTask
|
||||
/// </summary>
|
||||
public class ReloadLoggerFileTask : IScheduledTask, IConfigurableScheduledTask
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the log manager.
|
||||
/// </summary>
|
||||
/// <value>The log manager.</value>
|
||||
private ILogManager LogManager { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration manager.
|
||||
/// </summary>
|
||||
/// <value>The configuration manager.</value>
|
||||
private IConfigurationManager ConfigurationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReloadLoggerFileTask" /> class.
|
||||
/// </summary>
|
||||
/// <param name="logManager">The logManager.</param>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
public ReloadLoggerFileTask(ILogManager logManager, IConfigurationManager configurationManager)
|
||||
{
|
||||
LogManager = logManager;
|
||||
ConfigurationManager = configurationManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default triggers.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
var trigger = new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(0).Ticks }; //12am
|
||||
|
||||
return new[] { trigger };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the internal.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
progress.Report(0);
|
||||
|
||||
LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
|
||||
? LogSeverity.Debug
|
||||
: LogSeverity.Info);
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name
|
||||
{
|
||||
get { return "Start new log file"; }
|
||||
}
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public string Description
|
||||
{
|
||||
get { return "Moves logging to a new file to help reduce log file sizes."; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category.
|
||||
/// </summary>
|
||||
/// <value>The category.</value>
|
||||
public string Category
|
||||
{
|
||||
get { return "Application"; }
|
||||
}
|
||||
|
||||
public bool IsHidden
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsLogged
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a task trigger that fires on a weekly basis
|
||||
/// </summary>
|
||||
public class WeeklyTrigger : ITaskTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the time of day to trigger the task to run
|
||||
/// </summary>
|
||||
/// <value>The time of day.</value>
|
||||
public TimeSpan TimeOfDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the day of week.
|
||||
/// </summary>
|
||||
/// <value>The day of week.</value>
|
||||
public DayOfWeek DayOfWeek { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution properties of this task.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The execution properties of this task.
|
||||
/// </value>
|
||||
public TaskExecutionOptions TaskOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timer.
|
||||
/// </summary>
|
||||
/// <value>The timer.</value>
|
||||
private Timer Timer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stars waiting for the trigger action
|
||||
/// </summary>
|
||||
/// <param name="lastResult">The last result.</param>
|
||||
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
|
||||
public void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup)
|
||||
{
|
||||
DisposeTimer();
|
||||
|
||||
var triggerDate = GetNextTriggerDateTime();
|
||||
|
||||
Timer = new Timer(state => OnTriggered(), null, triggerDate - DateTime.Now, TimeSpan.FromMilliseconds(-1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next trigger date time.
|
||||
/// </summary>
|
||||
/// <returns>DateTime.</returns>
|
||||
private DateTime GetNextTriggerDateTime()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
// If it's on the same day
|
||||
if (now.DayOfWeek == DayOfWeek)
|
||||
{
|
||||
// It's either later today, or a week from now
|
||||
return now.TimeOfDay < TimeOfDay ? now.Date.Add(TimeOfDay) : now.Date.AddDays(7).Add(TimeOfDay);
|
||||
}
|
||||
|
||||
var triggerDate = now.Date;
|
||||
|
||||
// Walk the date forward until we get to the trigger day
|
||||
while (triggerDate.DayOfWeek != DayOfWeek)
|
||||
{
|
||||
triggerDate = triggerDate.AddDays(1);
|
||||
}
|
||||
|
||||
// Return the trigger date plus the time offset
|
||||
return triggerDate.Add(TimeOfDay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops waiting for the trigger action
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
DisposeTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the timer.
|
||||
/// </summary>
|
||||
private void DisposeTimer()
|
||||
{
|
||||
if (Timer != null)
|
||||
{
|
||||
Timer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [triggered].
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Called when [triggered].
|
||||
/// </summary>
|
||||
private void OnTriggered()
|
||||
{
|
||||
if (Triggered != null)
|
||||
{
|
||||
Triggered(this, new GenericEventArgs<TaskExecutionOptions>(TaskOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a wrapper around third party xml serialization.
|
||||
/// </summary>
|
||||
public class XmlSerializer : IXmlSerializer
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public XmlSerializer(IFileSystem fileSystem, ILogger logger)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// Need to cache these
|
||||
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
|
||||
private readonly Dictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
|
||||
new Dictionary<string, System.Xml.Serialization.XmlSerializer>();
|
||||
|
||||
private System.Xml.Serialization.XmlSerializer GetSerializer(Type type)
|
||||
{
|
||||
var key = type.FullName;
|
||||
lock (_serializers)
|
||||
{
|
||||
System.Xml.Serialization.XmlSerializer serializer;
|
||||
if (!_serializers.TryGetValue(key, out serializer))
|
||||
{
|
||||
serializer = new System.Xml.Serialization.XmlSerializer(type);
|
||||
_serializers[key] = serializer;
|
||||
}
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes to writer.
|
||||
/// </summary>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <param name="writer">The writer.</param>
|
||||
private void SerializeToWriter(object obj, XmlTextWriter writer)
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
var netSerializer = GetSerializer(obj.GetType());
|
||||
netSerializer.Serialize(writer, obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes from stream.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object DeserializeFromStream(Type type, Stream stream)
|
||||
{
|
||||
using (var reader = new XmlTextReader(stream))
|
||||
{
|
||||
var netSerializer = GetSerializer(type);
|
||||
return netSerializer.Deserialize(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes to stream.
|
||||
/// </summary>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <param name="stream">The stream.</param>
|
||||
public void SerializeToStream(object obj, Stream stream)
|
||||
{
|
||||
using (var writer = new XmlTextWriter(stream, null))
|
||||
{
|
||||
SerializeToWriter(obj, writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes to file.
|
||||
/// </summary>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <param name="file">The file.</param>
|
||||
public void SerializeToFile(object obj, string file)
|
||||
{
|
||||
_logger.Debug("Serializing to file {0}", file);
|
||||
using (var stream = new FileStream(file, FileMode.Create))
|
||||
{
|
||||
SerializeToStream(obj, stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes from file.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="file">The file.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object DeserializeFromFile(Type type, string file)
|
||||
{
|
||||
_logger.Debug("Deserializing file {0}", file);
|
||||
using (var stream = _fileSystem.OpenRead(file))
|
||||
{
|
||||
return DeserializeFromStream(type, stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes from bytes.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object DeserializeFromBytes(Type type, byte[] buffer)
|
||||
{
|
||||
using (var stream = new MemoryStream(buffer))
|
||||
{
|
||||
return DeserializeFromStream(type, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
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.Implementations.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 stream = await _httpClient.Get(options).ConfigureAwait(false))
|
||||
{
|
||||
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 stream = await _httpClient.Get(options).ConfigureAwait(false))
|
||||
{
|
||||
var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
|
||||
|
||||
obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename))).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;
|
||||
if (!Version.TryParse(obj.tag_name, out version))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (version < minVersion)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename));
|
||||
|
||||
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)
|
||||
{
|
||||
var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ namespace MediaBrowser.Controller.Sync
|
||||
/// </summary>
|
||||
Task<QueryResult<FileSystemMetadata>> GetFiles(string id, SyncTarget target, CancellationToken cancellationToken);
|
||||
Task<QueryResult<FileSystemMetadata>> GetFiles(string[] pathParts, SyncTarget target, CancellationToken cancellationToken);
|
||||
Task<QueryResult<FileSystemMetadata>> GetFiles(SyncTarget target, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface ISupportsDirectCopy
|
||||
|
@ -4,46 +4,21 @@ using MediaBrowser.Controller.Library;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using System.Xml;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Xml;
|
||||
|
||||
namespace MediaBrowser.LocalMetadata.Savers
|
||||
{
|
||||
/// <summary>
|
||||
/// Saves game.xml for games
|
||||
/// </summary>
|
||||
public class GameXmlSaver : IMetadataFileSaver
|
||||
public class GameXmlSaver : BaseXmlSaver
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return XmlProviderUtils.Name;
|
||||
}
|
||||
}
|
||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public GameXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
|
||||
{
|
||||
_config = config;
|
||||
_libraryManager = libraryManager;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is enabled for] [the specified item].
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="updateType">Type of the update.</param>
|
||||
/// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
|
||||
public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
|
||||
public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
|
||||
{
|
||||
if (!item.SupportsLocalMetadata)
|
||||
{
|
||||
@ -53,52 +28,41 @@ namespace MediaBrowser.LocalMetadata.Savers
|
||||
return item is Game && updateType >= ItemUpdateType.MetadataDownload;
|
||||
}
|
||||
|
||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public void Save(IHasMetadata item, CancellationToken cancellationToken)
|
||||
protected override List<string> GetTagsUsed()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.Append("<Item>");
|
||||
|
||||
var game = (Game)item;
|
||||
|
||||
if (game.PlayersSupported.HasValue)
|
||||
var list = new List<string>
|
||||
{
|
||||
builder.Append("<Players>" + SecurityElement.Escape(game.PlayersSupported.Value.ToString(UsCulture)) + "</Players>");
|
||||
}
|
||||
"GameSystem",
|
||||
"Players"
|
||||
};
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
|
||||
{
|
||||
var game = (Game)item;
|
||||
|
||||
if (!string.IsNullOrEmpty(game.GameSystem))
|
||||
{
|
||||
builder.Append("<GameSystem>" + SecurityElement.Escape(game.GameSystem) + "</GameSystem>");
|
||||
writer.WriteElementString("GameSystem", game.GameSystem);
|
||||
}
|
||||
if (game.PlayersSupported.HasValue)
|
||||
{
|
||||
writer.WriteElementString("Players", game.PlayersSupported.Value.ToString(UsCulture));
|
||||
}
|
||||
|
||||
XmlSaverHelpers.AddCommonNodes(game, _libraryManager, builder);
|
||||
|
||||
builder.Append("</Item>");
|
||||
|
||||
var xmlFilePath = GetSavePath(item);
|
||||
|
||||
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
|
||||
{
|
||||
"Players",
|
||||
"GameSystem",
|
||||
"NesBox",
|
||||
"NesBoxRom"
|
||||
}, _config, _fileSystem);
|
||||
}
|
||||
|
||||
public string GetSavePath(IHasMetadata item)
|
||||
protected override string GetLocalSavePath(IHasMetadata item)
|
||||
{
|
||||
return GetGameSavePath((Game)item);
|
||||
}
|
||||
|
||||
protected override string GetRootElementName(IHasMetadata item)
|
||||
{
|
||||
return "Item";
|
||||
}
|
||||
|
||||
public static string GetGameSavePath(Game item)
|
||||
{
|
||||
if (item.DetectIsInMixedFolder())
|
||||
@ -108,5 +72,9 @@ namespace MediaBrowser.LocalMetadata.Savers
|
||||
|
||||
return Path.Combine(item.ContainingFolderPath, "game.xml");
|
||||
}
|
||||
|
||||
public GameXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@
|
||||
<Compile Include="Subtitles\VttWriter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\BdInfo\BDInfo\BDInfo.csproj">
|
||||
<ProjectReference Include="..\BDInfo\BDInfo.csproj">
|
||||
<Project>{88ae38df-19d7-406f-a6a9-09527719a21e}</Project>
|
||||
<Name>BDInfo</Name>
|
||||
</ProjectReference>
|
||||
|
@ -18,6 +18,8 @@ using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
@ -554,9 +556,7 @@ namespace MediaBrowser.Providers.Manager
|
||||
switch (type)
|
||||
{
|
||||
case ImageType.Primary:
|
||||
return false;
|
||||
case ImageType.Thumb:
|
||||
return false;
|
||||
return !(item is Movie || item is Series || item is Season || item is Game);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
@ -2461,12 +2461,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
|
||||
|
||||
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata
|
||||
{
|
||||
Id = i.FullName,
|
||||
IsFolder = i.IsDirectory
|
||||
|
||||
}).ToList());
|
||||
var videos = videoListResolver.Resolve(fileSystemChildren);
|
||||
|
||||
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
@ -2510,12 +2505,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
|
||||
|
||||
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata
|
||||
{
|
||||
Id = i.FullName,
|
||||
IsFolder = i.IsDirectory
|
||||
|
||||
}).ToList());
|
||||
var videos = videoListResolver.Resolve(fileSystemChildren);
|
||||
|
||||
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
|
@ -135,12 +135,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
|
||||
|
||||
var resolver = new VideoListResolver(namingOptions, new PatternsLogger());
|
||||
var resolverResult = resolver.Resolve(files.Select(i => new FileMetadata
|
||||
{
|
||||
Id = i.FullName,
|
||||
IsFolder = i.IsDirectory
|
||||
|
||||
}).ToList(), suppportMultiEditions).ToList();
|
||||
var resolverResult = resolver.Resolve(files, suppportMultiEditions).ToList();
|
||||
|
||||
var result = new MultiItemResolverResult
|
||||
{
|
||||
|
@ -42,6 +42,9 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Emby.Common.Implementations">
|
||||
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Emby.XmlTv.1.0.0.58\lib\portable-net46+win10\Emby.XmlTv.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -53,14 +56,17 @@
|
||||
<Reference Include="Interfaces.IO">
|
||||
<HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MediaBrowser.Naming, Version=1.0.6059.24054, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MediaBrowser.Naming.1.0.0.55\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
|
||||
<Reference Include="MediaBrowser.Naming, Version=1.0.6146.28476, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MediaBrowser.Naming.1.0.0.56\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Nat">
|
||||
<HintPath>..\ThirdParty\emby\Mono.Nat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.10\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -75,10 +81,6 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SocketHttpListener, Version=1.0.6109.26162, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SocketHttpListener.1.0.0.40\lib\net45\SocketHttpListener.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -389,10 +391,6 @@
|
||||
<Compile Include="Xml\XmlReaderSettingsFactory.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
|
||||
<Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
|
||||
<Name>MediaBrowser.Common.Implementations</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
@ -409,10 +407,6 @@
|
||||
<Project>{442b5058-dcaf-4263-bb6a-f21e31120a1b}</Project>
|
||||
<Name>MediaBrowser.Providers</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj">
|
||||
<Project>{d7453b88-2266-4805-b39b-2b5a2a33e1ba}</Project>
|
||||
<Name>Mono.Nat</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\Ratings\us.txt" />
|
||||
|
@ -78,8 +78,8 @@ namespace MediaBrowser.Server.Implementations.Sync
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false);
|
||||
var remoteFiles = await provider.GetFiles(new FileQuery(), target, cancellationToken).ConfigureAwait(false);
|
||||
var remoteIds = remoteFiles.Items.Select(i => i.Id).ToList();
|
||||
var remoteFiles = await provider.GetFiles(target, cancellationToken).ConfigureAwait(false);
|
||||
var remoteIds = remoteFiles.Items.Select(i => i.FullName).ToList();
|
||||
|
||||
var jobItemIds = new List<string>();
|
||||
|
||||
|
@ -68,15 +68,11 @@ namespace MediaBrowser.Server.Implementations.Sync
|
||||
{
|
||||
_logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name);
|
||||
|
||||
var fileResult = await _provider.GetFiles(new FileQuery
|
||||
{
|
||||
FullPath = GetRemotePath().ToArray()
|
||||
|
||||
}, _target, cancellationToken).ConfigureAwait(false);
|
||||
var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (fileResult.Items.Length > 0)
|
||||
{
|
||||
using (var stream = await _provider.GetFile(fileResult.Items[0].Id, _target, new Progress<double>(), cancellationToken))
|
||||
using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken))
|
||||
{
|
||||
return _json.DeserializeFromStream<List<LocalItem>>(stream);
|
||||
}
|
||||
|
@ -3,10 +3,9 @@
|
||||
<package id="Emby.XmlTv" version="1.0.0.58" targetFramework="net46" />
|
||||
<package id="ini-parser" version="2.3.0" targetFramework="net45" />
|
||||
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
|
||||
<package id="MediaBrowser.Naming" version="1.0.0.55" targetFramework="net45" />
|
||||
<package id="MediaBrowser.Naming" version="1.0.0.56" targetFramework="net46" />
|
||||
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.1.0.0" targetFramework="net46" />
|
||||
<package id="NLog" version="4.3.10" targetFramework="net46" />
|
||||
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
|
||||
<package id="SimpleInjector" version="3.2.4" targetFramework="net46" />
|
||||
<package id="SocketHttpListener" version="1.0.0.40" targetFramework="net45" />
|
||||
</packages>
|
@ -58,6 +58,9 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Emby.Common.Implementations">
|
||||
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
|
||||
@ -139,10 +142,6 @@
|
||||
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
|
||||
<Name>MediaBrowser.Controller</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
|
||||
<Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
|
||||
<Name>MediaBrowser.Common.Implementations</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
|
@ -1,9 +1,9 @@
|
||||
using MediaBrowser.Common.Implementations.Networking;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Net;
|
||||
using System.Collections.Generic;
|
||||
using Emby.Common.Implementations.Networking;
|
||||
|
||||
namespace MediaBrowser.Server.Mono.Networking
|
||||
{
|
||||
|
@ -13,9 +13,7 @@ using System.Net.Security;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Implementations.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Server.Implementations.Logging;
|
||||
using Emby.Common.Implementations.IO;
|
||||
|
||||
namespace MediaBrowser.Server.Mono
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Implementations;
|
||||
using MediaBrowser.Common.Implementations.ScheduledTasks;
|
||||
using Emby.Common.Implementations.ScheduledTasks;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Controller;
|
||||
@ -96,13 +96,12 @@ using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Common.Implementations;
|
||||
using Emby.Common.Implementations.Networking;
|
||||
using Emby.Common.Implementations.Updates;
|
||||
using Emby.Photos;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Api.Playback;
|
||||
using MediaBrowser.Common.Implementations.Networking;
|
||||
using MediaBrowser.Common.Implementations.Serialization;
|
||||
using MediaBrowser.Common.Implementations.Updates;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Common.Updates;
|
||||
@ -111,6 +110,7 @@ using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Model.Activity;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.News;
|
||||
@ -251,11 +251,6 @@ namespace MediaBrowser.Server.Startup.Common
|
||||
|
||||
internal INativeApp NativeApp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The container
|
||||
/// </summary>
|
||||
protected readonly SimpleInjector.Container Container = new SimpleInjector.Container();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationHost" /> class.
|
||||
/// </summary>
|
||||
@ -1750,115 +1745,20 @@ namespace MediaBrowser.Server.Startup.Common
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of type and resolves all constructor dependancies
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public override object CreateInstance(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Container.GetInstance(type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error creating {0}", ex, type.FullName);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the instance safe.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
protected override object CreateInstanceSafe(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Container.GetInstance(type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error creating {0}", ex, type.FullName);
|
||||
// Don't blow up in release mode
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void IDependencyContainer.RegisterSingleInstance<T>(T obj, bool manageLifetime)
|
||||
{
|
||||
RegisterSingleInstance(obj, manageLifetime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified obj.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param>
|
||||
protected override void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
|
||||
{
|
||||
Container.RegisterSingleton(obj);
|
||||
|
||||
if (manageLifetime)
|
||||
{
|
||||
var disposable = obj as IDisposable;
|
||||
|
||||
if (disposable != null)
|
||||
{
|
||||
DisposableParts.Add(disposable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IDependencyContainer.RegisterSingleInstance<T>(Func<T> func)
|
||||
{
|
||||
RegisterSingleInstance(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the single instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="func">The func.</param>
|
||||
protected override void RegisterSingleInstance<T>(Func<T> func)
|
||||
{
|
||||
Container.RegisterSingleton(func);
|
||||
}
|
||||
|
||||
void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
|
||||
{
|
||||
Container.Register(typeInterface, typeImplementation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves this instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
public override T Resolve<T>()
|
||||
{
|
||||
return (T)Container.GetRegistration(typeof(T), true).GetInstance();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves this instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
public override T TryResolve<T>()
|
||||
{
|
||||
var result = Container.GetRegistration(typeof(T), false);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return (T)result.GetInstance();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,9 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Emby.Common.Implementations">
|
||||
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
|
||||
@ -97,10 +100,6 @@
|
||||
<Project>{4fd51ac5-2c16-4308-a993-c3a84f3b4582}</Project>
|
||||
<Name>MediaBrowser.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
|
||||
<Project>{c4d2573a-3fd3-441f-81af-174ac4cd4e1d}</Project>
|
||||
<Name>MediaBrowser.Common.Implementations</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Server.Implementations.Persistence;
|
||||
|
@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Implementations.Updates;
|
||||
using Emby.Common.Implementations.Updates;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
@ -19,10 +19,9 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Emby.Common.Implementations.IO;
|
||||
using ImageMagickSharp;
|
||||
using MediaBrowser.Common.Implementations.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Server.Implementations.Logging;
|
||||
|
||||
namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
|
@ -64,6 +64,9 @@
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Emby.Common.Implementations">
|
||||
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
|
||||
@ -1054,10 +1057,6 @@
|
||||
<Project>{4fd51ac5-2c16-4308-a993-c3a84f3b4582}</Project>
|
||||
<Name>MediaBrowser.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
|
||||
<Project>{c4d2573a-3fd3-441f-81af-174ac4cd4e1d}</Project>
|
||||
<Name>MediaBrowser.Common.Implementations</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using MediaBrowser.Common.Implementations.Networking;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Net;
|
||||
@ -9,6 +8,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Emby.Common.Implementations.Networking;
|
||||
|
||||
namespace MediaBrowser.ServerApplication.Networking
|
||||
{
|
||||
|
126
MediaBrowser.sln
126
MediaBrowser.sln
@ -34,8 +34,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "Media
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Tests", "MediaBrowser.Tests\MediaBrowser.Tests.csproj", "{E22BFD35-0FCD-4A85-978A-C22DCD73A081}"
|
||||
@ -60,13 +58,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Startup
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{D7453B88-2266-4805-B39B-2B5A2A33E1BA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "..\BdInfo\BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.xproj", "{5A27010A-09C6-4E86-93EA-437484C10917}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{0A82260B-4C22-4FD2-869A-E510044E3502}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B} = {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
|
||||
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2} = {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}
|
||||
{9142EEFA-7570-41E1-BFCC-468BB571AF2F} = {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -205,27 +210,6 @@ Global
|
||||
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Mixed Platforms.Build.0 = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Win32.ActiveCfg = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x64.ActiveCfg = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@ -484,36 +468,6 @@ Global
|
||||
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Win32.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Win32.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x64.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x64.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@ -574,6 +528,66 @@ Global
|
||||
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Win32.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Win32.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x64.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x64.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x86.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x86.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Win32.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|Win32.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|x64.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|x64.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|x86.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release Mono|x86.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0A82260B-4C22-4FD2-869A-E510044E3502}.Release|x86.Build.0 = Release|Any CPU
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"supports": {
|
||||
"net46.app": {},
|
||||
"uwp.10.0.app": {},
|
||||
"dnxcore50.app": {}
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9,7 +10,7 @@
|
||||
},
|
||||
"frameworks": {
|
||||
"dotnet": {
|
||||
"imports": "portable-net452"
|
||||
"imports": "portable-net452+win81"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
{
|
||||
"projects": [ "src", "test" ],
|
||||
{
|
||||
"projects": [ "src", "test", "." ],
|
||||
"sdk": {
|
||||
"version": "1.0.0-preview2-003131"
|
||||
}
|
||||
|
@ -59,8 +59,6 @@
|
||||
"bin/Debug/MediaBrowser.Controller.dll": {}
|
||||
},
|
||||
"runtime": {
|
||||
"../packages/Interfaces.IO.1.0.0.5/lib/portable-net45+sl4+wp71+win8+wpa81/Interfaces.IO.dll": {},
|
||||
"../packages/Patterns.Logging.1.0.0.2/lib/portable-net45+sl4+wp71+win8+wpa81/Patterns.Logging.dll": {},
|
||||
"bin/Debug/MediaBrowser.Controller.dll": {}
|
||||
},
|
||||
"contentFiles": {
|
||||
|
@ -5,6 +5,7 @@
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"Emby.Common.Implementations": "1.0.0-*",
|
||||
"Microsoft.NETCore.App": {
|
||||
"type": "platform",
|
||||
"version": "1.0.1"
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user