added IServerEntryPoint to replace plugin.initialize

This commit is contained in:
LukePulverenti 2013-03-02 21:47:04 -05:00
parent 71fe785c6d
commit 0ea90ef7c6
27 changed files with 487 additions and 534 deletions

View File

@ -97,6 +97,7 @@
<Compile Include="PluginService.cs" />
<Compile Include="ScheduledTasks\ScheduledTaskService.cs" />
<Compile Include="ScheduledTasks\ScheduledTasksWebSocketListener.cs" />
<Compile Include="ServerEntryPoint.cs" />
<Compile Include="SystemService.cs" />
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />
<Compile Include="UserLibrary\GenresService.cs" />

View File

@ -518,7 +518,7 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true
};
Plugin.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process);
ServerEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process);
//Logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
@ -537,7 +537,7 @@ namespace MediaBrowser.Api.Playback
{
Logger.ErrorException("Error starting ffmpeg", ex);
Plugin.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType);
ServerEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType);
state.LogFileStream.Dispose();
@ -588,7 +588,7 @@ namespace MediaBrowser.Api.Playback
process.Dispose();
Plugin.Instance.OnTranscodingFinished(outputFilePath, TranscodingJobType);
ServerEntryPoint.Instance.OnTranscodingFinished(outputFilePath, TranscodingJobType);
if (!exitCode.HasValue || exitCode.Value != 0)
{

View File

@ -77,7 +77,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
else
{
Plugin.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
ServerEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
}
// Get the current playlist text and convert to bytes
@ -94,7 +94,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
finally
{
Plugin.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
ServerEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
}
}

View File

@ -107,7 +107,7 @@ namespace MediaBrowser.Api.Playback.Progressive
var outputPath = GetOutputFilePath(state);
if (File.Exists(outputPath) && !Plugin.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
if (File.Exists(outputPath) && !ServerEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
{
return ToStaticFileResult(outputPath);
}
@ -133,7 +133,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
else
{
Plugin.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
ServerEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
}
return new ProgressiveStreamWriter

View File

@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
finally
{
Plugin.Instance.OnTranscodeEndRequest(Path, TranscodingJobType.Progressive);
ServerEntryPoint.Instance.OnTranscodeEndRequest(Path, TranscodingJobType.Progressive);
}
}

View File

@ -1,12 +1,7 @@
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Kernel;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Plugins;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Api
{
@ -15,6 +10,16 @@ namespace MediaBrowser.Api
/// </summary>
public class Plugin : BasePlugin<BasePluginConfiguration>
{
/// <summary>
/// Initializes a new instance of the <see cref="Plugin" /> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
public Plugin(IKernel kernel, IXmlSerializer xmlSerializer) : base(kernel, xmlSerializer)
{
Instance = this;
}
/// <summary>
/// Gets the name of the plugin
/// </summary>
@ -41,287 +46,5 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The instance.</value>
public static Plugin Instance { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Plugin" /> class.
/// </summary>
public Plugin()
{
Instance = 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 override void DisposeOnServer(bool dispose)
{
if (dispose)
{
var jobCount = ActiveTranscodingJobs.Count;
Parallel.ForEach(ActiveTranscodingJobs, OnTranscodeKillTimerStopped);
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if (jobCount > 0)
{
Thread.Sleep(1000);
}
}
base.DisposeOnServer(dispose);
}
/// <summary>
/// The active transcoding jobs
/// </summary>
private readonly List<TranscodingJob> ActiveTranscodingJobs = new List<TranscodingJob>();
/// <summary>
/// Called when [transcode beginning].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
/// <param name="process">The process.</param>
public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process)
{
lock (ActiveTranscodingJobs)
{
ActiveTranscodingJobs.Add(new TranscodingJob
{
Type = type,
Path = path,
Process = process,
ActiveRequestCount = 1
});
}
}
/// <summary>
/// Called when [transcode failed to start].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeFailedToStart(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.First(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
ActiveTranscodingJobs.Remove(job);
}
}
/// <summary>
/// Determines whether [has active transcoding job] [the specified path].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if [has active transcoding job] [the specified path]; otherwise, <c>false</c>.</returns>
public bool HasActiveTranscodingJob(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
return ActiveTranscodingJobs.Any(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
}
}
/// <summary>
/// Called when [transcode begin request].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeBeginRequest(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
job.ActiveRequestCount++;
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
job.KillTimer = null;
}
}
}
/// <summary>
/// Called when [transcode end request].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeEndRequest(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
job.ActiveRequestCount--;
if (job.ActiveRequestCount == 0)
{
var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 30000;
if (job.KillTimer == null)
{
job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite);
}
else
{
job.KillTimer.Change(timerDuration, Timeout.Infinite);
}
}
}
}
/// <summary>
/// Called when [transcoding finished].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodingFinished(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
ActiveTranscodingJobs.Remove(job);
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
job.KillTimer = null;
}
}
}
/// <summary>
/// Called when [transcode kill timer stopped].
/// </summary>
/// <param name="state">The state.</param>
private void OnTranscodeKillTimerStopped(object state)
{
var job = (TranscodingJob)state;
lock (ActiveTranscodingJobs)
{
ActiveTranscodingJobs.Remove(job);
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
job.KillTimer = null;
}
}
var process = job.Process;
var hasExited = true;
try
{
hasExited = process.HasExited;
}
catch (Win32Exception ex)
{
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
}
catch (InvalidOperationException ex)
{
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
}
catch (NotSupportedException ex)
{
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
}
if (hasExited)
{
return;
}
try
{
Logger.Info("Killing ffmpeg process for {0}", job.Path);
process.Kill();
}
catch (Win32Exception ex)
{
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
}
catch (InvalidOperationException ex)
{
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
}
catch (NotSupportedException ex)
{
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
}
}
}
/// <summary>
/// Class TranscodingJob
/// </summary>
public class TranscodingJob
{
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public TranscodingJobType Type { get; set; }
/// <summary>
/// Gets or sets the process.
/// </summary>
/// <value>The process.</value>
public Process Process { get; set; }
/// <summary>
/// Gets or sets the active request count.
/// </summary>
/// <value>The active request count.</value>
public int ActiveRequestCount { get; set; }
/// <summary>
/// Gets or sets the kill timer.
/// </summary>
/// <value>The kill timer.</value>
public Timer KillTimer { get; set; }
}
/// <summary>
/// Enum TranscodingJobType
/// </summary>
public enum TranscodingJobType
{
/// <summary>
/// The progressive
/// </summary>
Progressive,
/// <summary>
/// The HLS
/// </summary>
Hls
}
}

View File

@ -1,5 +1,6 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Implementations.HttpServer;
using MediaBrowser.Common.Kernel;
using MediaBrowser.Controller;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Plugins;
@ -123,12 +124,18 @@ namespace MediaBrowser.Api
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// The _app host
/// </summary>
private readonly IApplicationHost _appHost;
/// <summary>
/// Initializes a new instance of the <see cref="PluginService" /> class.
/// </summary>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="appHost">The app host.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
public PluginService(IJsonSerializer jsonSerializer)
public PluginService(IJsonSerializer jsonSerializer, IApplicationHost appHost)
: base()
{
if (jsonSerializer == null)
@ -136,6 +143,7 @@ namespace MediaBrowser.Api
throw new ArgumentNullException("jsonSerializer");
}
_appHost = appHost;
_jsonSerializer = jsonSerializer;
}
@ -146,7 +154,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetPlugins request)
{
var result = Kernel.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToList();
var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToList();
return ToOptimizedResult(result);
}
@ -158,7 +166,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetPluginAssembly request)
{
var plugin = Kernel.Plugins.First(p => p.Id == request.Id);
var plugin = _appHost.Plugins.First(p => p.Id == request.Id);
return ToStaticFileResult(plugin.AssemblyFilePath);
}
@ -170,7 +178,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetPluginConfiguration request)
{
var plugin = Kernel.Plugins.First(p => p.Id == request.Id);
var plugin = _appHost.Plugins.First(p => p.Id == request.Id);
var dateModified = plugin.ConfigurationDateLastModified;
@ -186,7 +194,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public object Get(GetPluginConfigurationFile request)
{
var plugin = Kernel.Plugins.First(p => p.Id == request.Id);
var plugin = _appHost.Plugins.First(p => p.Id == request.Id);
return ToStaticFileResult(plugin.ConfigurationFilePath);
}
@ -235,7 +243,7 @@ namespace MediaBrowser.Api
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var plugin = Kernel.Plugins.First(p => p.Id == id);
var plugin = _appHost.Plugins.First(p => p.Id == id);
var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, plugin.ConfigurationType) as BasePluginConfiguration;
@ -250,7 +258,7 @@ namespace MediaBrowser.Api
{
var kernel = (Kernel)Kernel;
var plugin = kernel.Plugins.First(p => p.Id == request.Id);
var plugin = _appHost.Plugins.First(p => p.Id == request.Id);
kernel.InstallationManager.UninstallPlugin(plugin);
}

View File

@ -0,0 +1,333 @@
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api
{
/// <summary>
/// Class ServerEntryPoint
/// </summary>
public class ServerEntryPoint : IServerEntryPoint
{
/// <summary>
/// The instance
/// </summary>
public static ServerEntryPoint Instance;
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
private ILogger Logger { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ServerEntryPoint" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
public ServerEntryPoint(ILogger logger)
{
Logger = logger;
Instance = this;
}
/// <summary>
/// Runs this instance.
/// </summary>
public void Run()
{
}
/// <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)
{
var jobCount = ActiveTranscodingJobs.Count;
Parallel.ForEach(ActiveTranscodingJobs, OnTranscodeKillTimerStopped);
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if (jobCount > 0)
{
Thread.Sleep(1000);
}
}
/// <summary>
/// The active transcoding jobs
/// </summary>
private readonly List<TranscodingJob> ActiveTranscodingJobs = new List<TranscodingJob>();
/// <summary>
/// Called when [transcode beginning].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
/// <param name="process">The process.</param>
public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process)
{
lock (ActiveTranscodingJobs)
{
ActiveTranscodingJobs.Add(new TranscodingJob
{
Type = type,
Path = path,
Process = process,
ActiveRequestCount = 1
});
}
}
/// <summary>
/// <summary>
/// The progressive
/// </summary>
/// Called when [transcode failed to start].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeFailedToStart(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.First(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
ActiveTranscodingJobs.Remove(job);
}
}
/// <summary>
/// Determines whether [has active transcoding job] [the specified path].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if [has active transcoding job] [the specified path]; otherwise, <c>false</c>.</returns>
public bool HasActiveTranscodingJob(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
return ActiveTranscodingJobs.Any(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
}
}
/// <summary>
/// Called when [transcode begin request].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeBeginRequest(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
job.ActiveRequestCount++;
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
job.KillTimer = null;
}
}
}
/// <summary>
/// Called when [transcode end request].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodeEndRequest(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
job.ActiveRequestCount--;
if (job.ActiveRequestCount == 0)
{
var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 30000;
if (job.KillTimer == null)
{
job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite);
}
else
{
job.KillTimer.Change(timerDuration, Timeout.Infinite);
}
}
}
}
/// <summary>
/// Called when [transcoding finished].
/// </summary>
/// <param name="path">The path.</param>
/// <param name="type">The type.</param>
public void OnTranscodingFinished(string path, TranscodingJobType type)
{
lock (ActiveTranscodingJobs)
{
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
ActiveTranscodingJobs.Remove(job);
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
job.KillTimer = null;
}
}
}
/// <summary>
/// Called when [transcode kill timer stopped].
/// </summary>
/// <param name="state">The state.</param>
private void OnTranscodeKillTimerStopped(object state)
{
var job = (TranscodingJob)state;
lock (ActiveTranscodingJobs)
{
ActiveTranscodingJobs.Remove(job);
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
job.KillTimer = null;
}
}
var process = job.Process;
var hasExited = true;
try
{
hasExited = process.HasExited;
}
catch (Win32Exception ex)
{
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
}
catch (InvalidOperationException ex)
{
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
}
catch (NotSupportedException ex)
{
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
}
if (hasExited)
{
return;
}
try
{
Logger.Info("Killing ffmpeg process for {0}", job.Path);
process.Kill();
}
catch (Win32Exception ex)
{
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
}
catch (InvalidOperationException ex)
{
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
}
catch (NotSupportedException ex)
{
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
}
}
}
/// <summary>
/// Class TranscodingJob
/// </summary>
public class TranscodingJob
{
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public TranscodingJobType Type { get; set; }
/// <summary>
/// Gets or sets the process.
/// </summary>
/// <value>The process.</value>
public Process Process { get; set; }
/// <summary>
/// Gets or sets the active request count.
/// </summary>
/// <value>The active request count.</value>
public int ActiveRequestCount { get; set; }
/// <summary>
/// <summary>
/// Enum TranscodingJobType
/// </summary>
/// <summary>
/// Gets or sets the kill timer.
/// </summary>
/// <value>The kill timer.</value>
public Timer KillTimer { get; set; }
}
/// <summary>
/// Enum TranscodingJobType
/// </summary>
public enum TranscodingJobType
{
/// <summary>
/// The progressive
/// </summary>
Progressive,
/// <summary>
/// The HLS
/// </summary>
Hls
}
}

View File

@ -3,6 +3,7 @@ using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Common.Implementations.WebSocket;
using MediaBrowser.Common.Kernel;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Logging;
@ -25,6 +26,12 @@ namespace MediaBrowser.Common.Implementations
/// <value>The logger.</value>
public ILogger Logger { get; protected set; }
/// <summary>
/// Gets or sets the plugins.
/// </summary>
/// <value>The plugins.</value>
public IEnumerable<IPlugin> Plugins { get; protected set; }
/// <summary>
/// Gets or sets the log manager.
/// </summary>
@ -142,12 +149,13 @@ namespace MediaBrowser.Common.Implementations
/// </summary>
protected virtual void FindParts()
{
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
Resolve<IHttpServer>().Init(GetExports<IRestfulService>(false));
Resolve<IServerManager>().AddWebSocketListeners(GetExports<IWebSocketListener>(false));
Resolve<IServerManager>().Start();
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
Plugins = GetExports<IPlugin>();
}
/// <summary>
@ -348,6 +356,17 @@ namespace MediaBrowser.Common.Implementations
}
/// <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;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>

View File

@ -1,16 +1,13 @@
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Security;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Common.Kernel
{
@ -121,12 +118,6 @@ namespace MediaBrowser.Common.Kernel
/// <value>The application paths.</value>
public TApplicationPathsType ApplicationPaths { get; private set; }
/// <summary>
/// Gets the list of currently loaded plugins
/// </summary>
/// <value>The plugins.</value>
public IEnumerable<IPlugin> Plugins { get; protected set; }
/// <summary>
/// Gets or sets the TCP manager.
/// </summary>
@ -211,9 +202,9 @@ namespace MediaBrowser.Common.Kernel
/// Initializes the Kernel
/// </summary>
/// <returns>Task.</returns>
public async Task Init()
public void Init()
{
await ReloadInternal().ConfigureAwait(false);
ReloadInternal();
OnReloadCompleted();
@ -224,64 +215,11 @@ namespace MediaBrowser.Common.Kernel
/// Performs initializations that can be reloaded at anytime
/// </summary>
/// <returns>Task.</returns>
protected virtual async Task ReloadInternal()
protected virtual void ReloadInternal()
{
// Set these to null so that they can be lazy loaded again
Configuration = null;
await OnConfigurationLoaded().ConfigureAwait(false);
FindParts();
await OnComposablePartsLoaded().ConfigureAwait(false);
ServerManager = ApplicationHost.Resolve<IServerManager>();
}
/// <summary>
/// Called when [configuration loaded].
/// </summary>
/// <returns>Task.</returns>
protected virtual Task OnConfigurationLoaded()
{
return Task.FromResult<object>(null);
}
/// <summary>
/// Composes the parts with ioc container.
/// </summary>
protected virtual void FindParts()
{
Plugins = ApplicationHost.GetExports<IPlugin>();
}
/// <summary>
/// Fires after MEF finishes finding composable parts within plugin assemblies
/// </summary>
/// <returns>Task.</returns>
protected virtual Task OnComposablePartsLoaded()
{
return Task.Run(() =>
{
// Start-up each plugin
Parallel.ForEach(Plugins, plugin =>
{
Logger.Info("Initializing {0} {1}", plugin.Name, plugin.Version);
try
{
plugin.Initialize(this, _xmlSerializer, Logger);
Logger.Info("{0} {1} initialized.", plugin.Name, plugin.Version);
}
catch (Exception ex)
{
Logger.ErrorException("Error initializing {0}", ex, plugin.Name);
}
});
});
}
/// <summary>
/// Notifies that the kernel that a change has been made that requires a restart
/// </summary>
@ -442,17 +380,5 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
/// <value>The resource pools.</value>
public ResourcePool ResourcePools { get; set; }
/// <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;
}
}
}

View File

@ -1,4 +1,5 @@
using MediaBrowser.Model.Updates;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Updates;
using System;
using System.Collections.Generic;
using System.Threading;
@ -97,5 +98,17 @@ namespace MediaBrowser.Common.Kernel
/// Shuts down.
/// </summary>
void Shutdown();
/// <summary>
/// Gets the plugins.
/// </summary>
/// <value>The plugins.</value>
IEnumerable<IPlugin> Plugins { get; }
/// <summary>
/// Removes the plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
void RemovePlugin(IPlugin plugin);
}
}

View File

@ -40,7 +40,7 @@ namespace MediaBrowser.Common.Kernel
/// Inits this instance.
/// </summary>
/// <returns>Task.</returns>
Task Init();
void Init();
/// <summary>
/// Gets or sets a value indicating whether this instance has pending kernel reload.
@ -71,12 +71,6 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
void PerformPendingRestart();
/// <summary>
/// Gets the plugins.
/// </summary>
/// <value>The plugins.</value>
IEnumerable<IPlugin> Plugins { get; }
/// <summary>
/// Gets the UDP server port number.
/// </summary>
@ -123,12 +117,5 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
/// <value>The resource pools.</value>
ResourcePool ResourcePools { get; set; }
/// <summary>
/// Removes the plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
void RemovePlugin(IPlugin plugin);
}
}

View File

@ -1,5 +1,4 @@
using MediaBrowser.Common.Kernel;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using System;
@ -14,7 +13,7 @@ namespace MediaBrowser.Common.Plugins
/// Provides a common base class for all plugins
/// </summary>
/// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
public abstract class BasePlugin<TConfigurationType> : IDisposable, IPlugin
public abstract class BasePlugin<TConfigurationType> : IPlugin
where TConfigurationType : BasePluginConfiguration
{
/// <summary>
@ -23,6 +22,12 @@ namespace MediaBrowser.Common.Plugins
/// <value>The kernel.</value>
protected IKernel Kernel { 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 plugin's current context
/// </summary>
@ -56,6 +61,12 @@ namespace MediaBrowser.Common.Plugins
}
}
/// <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 type of configuration this plugin uses
/// </summary>
@ -252,87 +263,14 @@ namespace MediaBrowser.Common.Plugins
}
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
public ILogger Logger { get; private set; }
/// <summary>
/// Gets the XML serializer.
/// </summary>
/// <value>The XML serializer.</value>
protected IXmlSerializer XmlSerializer { get; private set; }
/// <summary>
/// Starts the plugin.
/// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
/// <param name="logger">The logger.</param>
/// <exception cref="System.ArgumentNullException">kernel</exception>
public void Initialize(IKernel kernel, IXmlSerializer xmlSerializer, ILogger logger)
protected BasePlugin(IKernel kernel, IXmlSerializer xmlSerializer)
{
if (kernel == null)
{
throw new ArgumentNullException("kernel");
}
if (xmlSerializer == null)
{
throw new ArgumentNullException("xmlSerializer");
}
if (logger == null)
{
throw new ArgumentNullException("logger");
}
XmlSerializer = xmlSerializer;
Logger = logger;
Kernel = kernel;
if (kernel.KernelContext == KernelContext.Server)
{
InitializeOnServer(!File.Exists(ConfigurationFilePath));
}
}
/// <summary>
/// Starts the plugin on the server
/// </summary>
/// <param name="isFirstRun">if set to <c>true</c> [is first run].</param>
protected virtual void InitializeOnServer(bool isFirstRun)
{
}
/// <summary>
/// Disposes the plugins. Undos all actions performed during Init.
/// </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 void Dispose(bool dispose)
{
if (Kernel.KernelContext == KernelContext.Server)
{
DisposeOnServer(dispose);
}
}
/// <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 DisposeOnServer(bool dispose)
{
XmlSerializer = xmlSerializer;
}
/// <summary>
@ -351,8 +289,6 @@ namespace MediaBrowser.Common.Plugins
throw new InvalidOperationException("Cannot call Plugin.SaveConfiguration from the UI.");
}
Logger.Info("Saving configuration");
lock (_configurationSaveLock)
{
XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath);

View File

@ -1,11 +1,11 @@
using MediaBrowser.Common.Kernel;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Plugins;
using System;
namespace MediaBrowser.Common.Plugins
{
/// <summary>
/// Interface IPlugin
/// </summary>
public interface IPlugin
{
/// <summary>
@ -92,26 +92,6 @@ namespace MediaBrowser.Common.Plugins
/// <value>The data folder path.</value>
string DataFolderPath { get; }
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
ILogger Logger { get; }
/// <summary>
/// Starts the plugin.
/// </summary>
/// <param name="kernel">The kernel.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
/// <param name="logger">The logger.</param>
/// <exception cref="System.ArgumentNullException">kernel</exception>
void Initialize(IKernel kernel, IXmlSerializer xmlSerializer, ILogger logger);
/// <summary>
/// Disposes the plugins. Undos all actions performed during Init.
/// </summary>
void Dispose();
/// <summary>
/// Saves the current configuration to the file system
/// </summary>
@ -136,5 +116,11 @@ namespace MediaBrowser.Common.Plugins
/// Called when just before the plugin is uninstalled from the server.
/// </summary>
void OnUninstalling();
/// <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>
bool IsFirstRun { get; }
}
}

View File

@ -16,7 +16,7 @@ namespace MediaBrowser.Common.ScheduledTasks
{
if (isApplicationStartup)
{
await Task.Delay(2000).ConfigureAwait(false);
await Task.Delay(3000).ConfigureAwait(false);
OnTriggered();
}

View File

@ -183,7 +183,7 @@ namespace MediaBrowser.Controller
/// <summary>
/// Composes the parts with ioc container.
/// </summary>
protected override void FindParts()
protected void FindParts()
{
// For now there's no real way to inject this properly
BaseItem.LibraryManager = ApplicationHost.Resolve<ILibraryManager>();
@ -194,8 +194,6 @@ namespace MediaBrowser.Controller
ProviderManager = (ProviderManager)ApplicationHost.CreateInstance(typeof(ProviderManager));
SecurityManager = (PluginSecurityManager)ApplicationHost.CreateInstance(typeof(PluginSecurityManager));
base.FindParts();
UserDataRepositories = ApplicationHost.GetExports<IUserDataRepository>();
UserRepositories = ApplicationHost.GetExports<IUserRepository>();
DisplayPreferencesRepositories = ApplicationHost.GetExports<IDisplayPreferencesRepository>();
@ -211,15 +209,24 @@ namespace MediaBrowser.Controller
/// Performs initializations that can be reloaded at anytime
/// </summary>
/// <returns>Task.</returns>
protected override async Task ReloadInternal()
protected override async void ReloadInternal()
{
await base.ReloadInternal().ConfigureAwait(false);
base.ReloadInternal();
FindParts();
await LoadRepositories().ConfigureAwait(false);
ReloadResourcePools();
ReloadFileSystemManager();
await ApplicationHost.Resolve<IUserManager>().RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false);
foreach (var entryPoint in ApplicationHost.GetExports<IServerEntryPoint>())
{
entryPoint.Run();
}
}
/// <summary>
@ -263,11 +270,8 @@ namespace MediaBrowser.Controller
/// Called when [composable parts loaded].
/// </summary>
/// <returns>Task.</returns>
protected override async Task OnComposablePartsLoaded()
protected Task LoadRepositories()
{
// The base class will start up all the plugins
await base.OnComposablePartsLoaded().ConfigureAwait(false);
// Get the current item repository
ItemRepository = GetRepository(ItemRepositories, Configuration.ItemRepository);
var itemRepoTask = ItemRepository.Initialize();
@ -284,7 +288,7 @@ namespace MediaBrowser.Controller
DisplayPreferencesRepository = GetRepository(DisplayPreferencesRepositories, Configuration.DisplayPreferencesRepository);
var displayPreferencesRepoTask = DisplayPreferencesRepository.Initialize();
await Task.WhenAll(itemRepoTask, userRepoTask, userDataRepoTask, displayPreferencesRepoTask).ConfigureAwait(false);
return Task.WhenAll(itemRepoTask, userRepoTask, userDataRepoTask, displayPreferencesRepoTask);
}
/// <summary>

View File

@ -131,6 +131,7 @@
<Compile Include="Persistence\IUserRepository.cs" />
<Compile Include="Library\IIntroProvider.cs" />
<Compile Include="Plugins\IPluginConfigurationPage.cs" />
<Compile Include="Plugins\IServerEntryPoint.cs" />
<Compile Include="Plugins\PluginSecurityManager.cs" />
<Compile Include="Providers\FanartBaseProvider.cs" />
<Compile Include="Providers\IImageEnhancer.cs" />

View File

@ -0,0 +1,15 @@
using System;
namespace MediaBrowser.Controller.Plugins
{
/// <summary>
/// Interface IServerEntryPoint
/// </summary>
public interface IServerEntryPoint : IDisposable
{
/// <summary>
/// Runs this instance.
/// </summary>
void Run();
}
}

View File

@ -287,7 +287,7 @@ namespace MediaBrowser.Controller.Updates
{
var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
var plugins = Kernel.Plugins;
var plugins = ApplicationHost.Plugins;
if (withAutoUpdateEnabled)
{
@ -424,7 +424,7 @@ namespace MediaBrowser.Controller.Updates
if (!(Path.GetExtension(package.targetFilename) ?? "").Equals(".zip", StringComparison.OrdinalIgnoreCase))
{
// Set last update time if we were installed before
var plugin = Kernel.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
var plugin = ApplicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
if (plugin != null)
{
@ -460,7 +460,7 @@ namespace MediaBrowser.Controller.Updates
plugin.OnUninstalling();
// Remove it the quick way for now
Kernel.RemovePlugin(plugin);
ApplicationHost.RemovePlugin(plugin);
File.Delete(plugin.AssemblyFilePath);

View File

@ -287,11 +287,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// <exception cref="System.InvalidOperationException">Cannot create the root folder until plugins have loaded</exception>
public AggregateFolder CreateRootFolder()
{
if (Kernel.Plugins == null)
{
throw new InvalidOperationException("Cannot create the root folder until plugins have loaded");
}
var rootFolderPath = Kernel.ApplicationPaths.RootFolderPath;
var rootFolder = Kernel.ItemRepository.RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(rootFolderPath);

View File

@ -34,7 +34,7 @@
<ItemGroup>
<Reference Include="BdInfo, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.0\lib\net45\BdInfo.dll</HintPath>
<HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.2\lib\net45\BdInfo.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll</HintPath>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MediaBrowser.BdInfo" version="1.0.0.0" targetFramework="net45" />
<package id="MediaBrowser.BdInfo" version="1.0.0.2" targetFramework="net45" />
<package id="morelinq" version="1.0.15631-beta" targetFramework="net45" />
<package id="System.Data.SQLite" version="1.0.84.0" targetFramework="net45" />
</packages>

View File

@ -177,7 +177,7 @@ namespace MediaBrowser.ServerApplication
var now = DateTime.UtcNow;
await Kernel.Init();
Kernel.Init();
var done = (DateTime.UtcNow - now);
Logger.Info("Kernel.Init completed in {0}{1} minutes and {2} seconds.", done.Hours > 0 ? done.Hours + " Hours " : "", done.Minutes, done.Seconds);

View File

@ -253,4 +253,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal

View File

@ -2,16 +2,17 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.22</version>
<title />
<version>3.0.23</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>Media Browser Team</owners>
<owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common components shared by Media Browser Theatre and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.22" />
<dependency id="MediaBrowser.Common" version="3.0.23" />
<dependency id="NLog" version="2.0.0.2000" />
<dependency id="ServiceStack" version="3.9.37" />
<dependency id="ServiceStack.Api.Swagger" version="3.9.35" />

View File

@ -2,13 +2,14 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.22</version>
<version>3.0.23</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners />
<owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<copyright>Copyright © Media Browser 2013</copyright>
<description>Contains common model objects and interfaces used by all Media Browser solutions.</description>
</metadata>
<files>

View File

@ -2,16 +2,17 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.22</version>
<version>3.0.23</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners />
<owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.22" />
<dependency id="MediaBrowser.Common" version="3.0.23" />
</dependencies>
</metadata>
<files>