diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index cf2a556955..97c399fbe9 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -97,6 +97,7 @@ + diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ce9fe0e0be..9808564105 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -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) { diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 8c7edbe91d..d616565ca8 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -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); } } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 08e66c3874..bbfe65504b 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -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 diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 9ac93694b3..b6b73c78dd 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Playback.Progressive } finally { - Plugin.Instance.OnTranscodeEndRequest(Path, TranscodingJobType.Progressive); + ServerEntryPoint.Instance.OnTranscodeEndRequest(Path, TranscodingJobType.Progressive); } } diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 58c0ffcfa5..b8c81bbe12 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -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 /// public class Plugin : BasePlugin { + /// + /// Initializes a new instance of the class. + /// + /// The kernel. + /// The XML serializer. + public Plugin(IKernel kernel, IXmlSerializer xmlSerializer) : base(kernel, xmlSerializer) + { + Instance = this; + } + /// /// Gets the name of the plugin /// @@ -41,287 +46,5 @@ namespace MediaBrowser.Api /// /// The instance. public static Plugin Instance { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public Plugin() - { - Instance = this; - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - 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); - } - - /// - /// The active transcoding jobs - /// - private readonly List ActiveTranscodingJobs = new List(); - - /// - /// Called when [transcode beginning]. - /// - /// The path. - /// The type. - /// The process. - public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process) - { - lock (ActiveTranscodingJobs) - { - ActiveTranscodingJobs.Add(new TranscodingJob - { - Type = type, - Path = path, - Process = process, - ActiveRequestCount = 1 - }); - } - } - - /// - /// Called when [transcode failed to start]. - /// - /// The path. - /// The type. - 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); - } - } - - /// - /// Determines whether [has active transcoding job] [the specified path]. - /// - /// The path. - /// The type. - /// true if [has active transcoding job] [the specified path]; otherwise, false. - public bool HasActiveTranscodingJob(string path, TranscodingJobType type) - { - lock (ActiveTranscodingJobs) - { - return ActiveTranscodingJobs.Any(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); - } - } - - /// - /// Called when [transcode begin request]. - /// - /// The path. - /// The type. - 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; - } - } - } - - /// - /// Called when [transcode end request]. - /// - /// The path. - /// The type. - 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); - } - } - } - } - - /// - /// Called when [transcoding finished]. - /// - /// The path. - /// The type. - 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; - } - } - } - - /// - /// Called when [transcode kill timer stopped]. - /// - /// The state. - 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); - } - } - } - - /// - /// Class TranscodingJob - /// - public class TranscodingJob - { - /// - /// Gets or sets the path. - /// - /// The path. - public string Path { get; set; } - /// - /// Gets or sets the type. - /// - /// The type. - public TranscodingJobType Type { get; set; } - /// - /// Gets or sets the process. - /// - /// The process. - public Process Process { get; set; } - /// - /// Gets or sets the active request count. - /// - /// The active request count. - public int ActiveRequestCount { get; set; } - /// - /// Gets or sets the kill timer. - /// - /// The kill timer. - public Timer KillTimer { get; set; } - } - - /// - /// Enum TranscodingJobType - /// - public enum TranscodingJobType - { - /// - /// The progressive - /// - Progressive, - /// - /// The HLS - /// - Hls } } diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs index 06db7a2769..7e907c2dd2 100644 --- a/MediaBrowser.Api/PluginService.cs +++ b/MediaBrowser.Api/PluginService.cs @@ -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 /// private readonly IJsonSerializer _jsonSerializer; + /// + /// The _app host + /// + private readonly IApplicationHost _appHost; + /// /// Initializes a new instance of the class. /// /// The json serializer. + /// The app host. /// jsonSerializer - 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 /// System.Object. 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 /// System.Object. 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 /// System.Object. 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 /// System.Object. 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(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); } diff --git a/MediaBrowser.Api/ServerEntryPoint.cs b/MediaBrowser.Api/ServerEntryPoint.cs new file mode 100644 index 0000000000..663d1aeca2 --- /dev/null +++ b/MediaBrowser.Api/ServerEntryPoint.cs @@ -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 +{ + /// + /// Class ServerEntryPoint + /// + public class ServerEntryPoint : IServerEntryPoint + { + /// + /// The instance + /// + public static ServerEntryPoint Instance; + + /// + /// Gets or sets the logger. + /// + /// The logger. + private ILogger Logger { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public ServerEntryPoint(ILogger logger) + { + Logger = logger; + + Instance = this; + } + + /// + /// Runs this instance. + /// + public void Run() + { + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + 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); + } + } + + /// + /// The active transcoding jobs + /// + private readonly List ActiveTranscodingJobs = new List(); + + /// + /// Called when [transcode beginning]. + /// + /// The path. + /// The type. + /// The process. + public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process) + { + lock (ActiveTranscodingJobs) + { + ActiveTranscodingJobs.Add(new TranscodingJob + { + Type = type, + Path = path, + Process = process, + ActiveRequestCount = 1 + }); + } + } + + /// + /// + /// The progressive + /// + /// Called when [transcode failed to start]. + /// + /// The path. + /// The type. + 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); + } + } + + /// + /// Determines whether [has active transcoding job] [the specified path]. + /// + /// The path. + /// The type. + /// true if [has active transcoding job] [the specified path]; otherwise, false. + public bool HasActiveTranscodingJob(string path, TranscodingJobType type) + { + lock (ActiveTranscodingJobs) + { + return ActiveTranscodingJobs.Any(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); + } + } + + /// + /// Called when [transcode begin request]. + /// + /// The path. + /// The type. + 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; + } + } + } + + /// + /// Called when [transcode end request]. + /// + /// The path. + /// The type. + 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); + } + } + } + } + + /// + /// Called when [transcoding finished]. + /// + /// The path. + /// The type. + 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; + } + } + } + + /// + /// Called when [transcode kill timer stopped]. + /// + /// The state. + 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); + } + } + + } + + /// + /// Class TranscodingJob + /// + public class TranscodingJob + { + /// + /// Gets or sets the path. + /// + /// The path. + public string Path { get; set; } + /// + /// Gets or sets the type. + /// + /// The type. + public TranscodingJobType Type { get; set; } + /// + /// Gets or sets the process. + /// + /// The process. + public Process Process { get; set; } + /// + /// Gets or sets the active request count. + /// + /// The active request count. + public int ActiveRequestCount { get; set; } + /// + /// + /// Enum TranscodingJobType + /// + /// + /// Gets or sets the kill timer. + /// + /// The kill timer. + public Timer KillTimer { get; set; } + } + + /// + /// Enum TranscodingJobType + /// + public enum TranscodingJobType + { + /// + /// The progressive + /// + Progressive, + /// + /// The HLS + /// + Hls + } +} diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index f23d859e8f..3d7421f67f 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -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 /// The logger. public ILogger Logger { get; protected set; } + /// + /// Gets or sets the plugins. + /// + /// The plugins. + public IEnumerable Plugins { get; protected set; } + /// /// Gets or sets the log manager. /// @@ -142,12 +149,13 @@ namespace MediaBrowser.Common.Implementations /// protected virtual void FindParts() { - Resolve().AddTasks(GetExports(false)); - Resolve().Init(GetExports(false)); Resolve().AddWebSocketListeners(GetExports(false)); Resolve().Start(); + Resolve().AddTasks(GetExports(false)); + + Plugins = GetExports(); } /// @@ -348,6 +356,17 @@ namespace MediaBrowser.Common.Implementations } + /// + /// Removes the plugin. + /// + /// The plugin. + public void RemovePlugin(IPlugin plugin) + { + var list = Plugins.ToList(); + list.Remove(plugin); + Plugins = list; + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 78277ed2f3..d35ee42c93 100644 --- a/MediaBrowser.Common/Kernel/BaseKernel.cs +++ b/MediaBrowser.Common/Kernel/BaseKernel.cs @@ -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 /// The application paths. public TApplicationPathsType ApplicationPaths { get; private set; } - /// - /// Gets the list of currently loaded plugins - /// - /// The plugins. - public IEnumerable Plugins { get; protected set; } - /// /// Gets or sets the TCP manager. /// @@ -211,9 +202,9 @@ namespace MediaBrowser.Common.Kernel /// Initializes the Kernel /// /// Task. - 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 /// /// Task. - 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(); } - /// - /// Called when [configuration loaded]. - /// - /// Task. - protected virtual Task OnConfigurationLoaded() - { - return Task.FromResult(null); - } - - /// - /// Composes the parts with ioc container. - /// - protected virtual void FindParts() - { - Plugins = ApplicationHost.GetExports(); - } - - /// - /// Fires after MEF finishes finding composable parts within plugin assemblies - /// - /// Task. - 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); - } - }); - }); - } - /// /// Notifies that the kernel that a change has been made that requires a restart /// @@ -442,17 +380,5 @@ namespace MediaBrowser.Common.Kernel /// /// The resource pools. public ResourcePool ResourcePools { get; set; } - - /// - /// Removes the plugin. - /// - /// The plugin. - public void RemovePlugin(IPlugin plugin) - { - var list = Plugins.ToList(); - list.Remove(plugin); - Plugins = list; - } - } } diff --git a/MediaBrowser.Common/Kernel/IApplicationHost.cs b/MediaBrowser.Common/Kernel/IApplicationHost.cs index bb007ddff1..29934988a2 100644 --- a/MediaBrowser.Common/Kernel/IApplicationHost.cs +++ b/MediaBrowser.Common/Kernel/IApplicationHost.cs @@ -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. /// void Shutdown(); + + /// + /// Gets the plugins. + /// + /// The plugins. + IEnumerable Plugins { get; } + + /// + /// Removes the plugin. + /// + /// The plugin. + void RemovePlugin(IPlugin plugin); } } diff --git a/MediaBrowser.Common/Kernel/IKernel.cs b/MediaBrowser.Common/Kernel/IKernel.cs index d02f0af209..0d123476ca 100644 --- a/MediaBrowser.Common/Kernel/IKernel.cs +++ b/MediaBrowser.Common/Kernel/IKernel.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Common.Kernel /// Inits this instance. /// /// Task. - Task Init(); + void Init(); /// /// Gets or sets a value indicating whether this instance has pending kernel reload. @@ -71,12 +71,6 @@ namespace MediaBrowser.Common.Kernel /// void PerformPendingRestart(); - /// - /// Gets the plugins. - /// - /// The plugins. - IEnumerable Plugins { get; } - /// /// Gets the UDP server port number. /// @@ -123,12 +117,5 @@ namespace MediaBrowser.Common.Kernel /// /// The resource pools. ResourcePool ResourcePools { get; set; } - - /// - /// Removes the plugin. - /// - /// The plugin. - void RemovePlugin(IPlugin plugin); - } } diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 0d7c5c0603..c968a65c82 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -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 /// /// The type of the T configuration type. - public abstract class BasePlugin : IDisposable, IPlugin + public abstract class BasePlugin : IPlugin where TConfigurationType : BasePluginConfiguration { /// @@ -23,6 +22,12 @@ namespace MediaBrowser.Common.Plugins /// The kernel. protected IKernel Kernel { get; private set; } + /// + /// Gets the XML serializer. + /// + /// The XML serializer. + protected IXmlSerializer XmlSerializer { get; private set; } + /// /// Gets or sets the plugin's current context /// @@ -56,6 +61,12 @@ namespace MediaBrowser.Common.Plugins } } + /// + /// Gets a value indicating whether this instance is first run. + /// + /// true if this instance is first run; otherwise, false. + public bool IsFirstRun { get; private set; } + /// /// Gets the type of configuration this plugin uses /// @@ -252,87 +263,14 @@ namespace MediaBrowser.Common.Plugins } /// - /// Gets the logger. - /// - /// The logger. - public ILogger Logger { get; private set; } - - /// - /// Gets the XML serializer. - /// - /// The XML serializer. - protected IXmlSerializer XmlSerializer { get; private set; } - - /// - /// Starts the plugin. + /// Initializes a new instance of the class. /// /// The kernel. /// The XML serializer. - /// The logger. - /// kernel - 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)); - } - } - - /// - /// Starts the plugin on the server - /// - /// if set to true [is first run]. - protected virtual void InitializeOnServer(bool isFirstRun) - { - } - - /// - /// Disposes the plugins. Undos all actions performed during Init. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected void Dispose(bool dispose) - { - if (Kernel.KernelContext == KernelContext.Server) - { - DisposeOnServer(dispose); - } - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void DisposeOnServer(bool dispose) - { - + XmlSerializer = xmlSerializer; } /// @@ -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); diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs index 7d5fddb9ae..ace82d83fc 100644 --- a/MediaBrowser.Common/Plugins/IPlugin.cs +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -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 { + /// + /// Interface IPlugin + /// public interface IPlugin { /// @@ -92,26 +92,6 @@ namespace MediaBrowser.Common.Plugins /// The data folder path. string DataFolderPath { get; } - /// - /// Gets the logger. - /// - /// The logger. - ILogger Logger { get; } - - /// - /// Starts the plugin. - /// - /// The kernel. - /// The XML serializer. - /// The logger. - /// kernel - void Initialize(IKernel kernel, IXmlSerializer xmlSerializer, ILogger logger); - - /// - /// Disposes the plugins. Undos all actions performed during Init. - /// - void Dispose(); - /// /// Saves the current configuration to the file system /// @@ -136,5 +116,11 @@ namespace MediaBrowser.Common.Plugins /// Called when just before the plugin is uninstalled from the server. /// void OnUninstalling(); + + /// + /// Gets a value indicating whether this instance is first run. + /// + /// true if this instance is first run; otherwise, false. + bool IsFirstRun { get; } } } \ No newline at end of file diff --git a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs index e48551425b..6dc5c6b38b 100644 --- a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Common.ScheduledTasks { if (isApplicationStartup) { - await Task.Delay(2000).ConfigureAwait(false); + await Task.Delay(3000).ConfigureAwait(false); OnTriggered(); } diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 16175783fd..99e0ef783b 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -183,7 +183,7 @@ namespace MediaBrowser.Controller /// /// Composes the parts with ioc container. /// - protected override void FindParts() + protected void FindParts() { // For now there's no real way to inject this properly BaseItem.LibraryManager = ApplicationHost.Resolve(); @@ -194,8 +194,6 @@ namespace MediaBrowser.Controller ProviderManager = (ProviderManager)ApplicationHost.CreateInstance(typeof(ProviderManager)); SecurityManager = (PluginSecurityManager)ApplicationHost.CreateInstance(typeof(PluginSecurityManager)); - base.FindParts(); - UserDataRepositories = ApplicationHost.GetExports(); UserRepositories = ApplicationHost.GetExports(); DisplayPreferencesRepositories = ApplicationHost.GetExports(); @@ -211,15 +209,24 @@ namespace MediaBrowser.Controller /// Performs initializations that can be reloaded at anytime /// /// Task. - 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().RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false); + + foreach (var entryPoint in ApplicationHost.GetExports()) + { + entryPoint.Run(); + } } /// @@ -263,11 +270,8 @@ namespace MediaBrowser.Controller /// Called when [composable parts loaded]. /// /// Task. - 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); } /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 71116881e6..63468a8e80 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -131,6 +131,7 @@ + diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs new file mode 100644 index 0000000000..de6de8f845 --- /dev/null +++ b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs @@ -0,0 +1,15 @@ +using System; + +namespace MediaBrowser.Controller.Plugins +{ + /// + /// Interface IServerEntryPoint + /// + public interface IServerEntryPoint : IDisposable + { + /// + /// Runs this instance. + /// + void Run(); + } +} diff --git a/MediaBrowser.Controller/Updates/InstallationManager.cs b/MediaBrowser.Controller/Updates/InstallationManager.cs index d616e88984..f67f690aed 100644 --- a/MediaBrowser.Controller/Updates/InstallationManager.cs +++ b/MediaBrowser.Controller/Updates/InstallationManager.cs @@ -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); diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 18108842f9..ba54b5d2b4 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -287,11 +287,6 @@ namespace MediaBrowser.Server.Implementations.Library /// Cannot create the root folder until plugins have loaded 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); diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index f18df77c27..83136aed97 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -34,7 +34,7 @@ False - ..\packages\MediaBrowser.BdInfo.1.0.0.0\lib\net45\BdInfo.dll + ..\packages\MediaBrowser.BdInfo.1.0.0.2\lib\net45\BdInfo.dll ..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index c20edb5ab5..fb84d9aa7c 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs index dddd877827..95ece67e6c 100644 --- a/MediaBrowser.ServerApplication/App.xaml.cs +++ b/MediaBrowser.ServerApplication/App.xaml.cs @@ -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); diff --git a/MediaBrowser.sln b/MediaBrowser.sln index b473b20625..0fe54d0059 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -253,4 +253,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index c005b145be..cc6679348e 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,16 +2,17 @@ MediaBrowser.Common.Internal - 3.0.22 - + <version>3.0.23</version> + <title>MediaBrowser.Common.Internal Luke - Media Browser Team + ebr,Luke,scottisafool https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false Contains common components shared by Media Browser Theatre and Media Browser Server. Not intended for plugin developer consumption. + Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index bd53633028..cf832f548a 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,13 +2,14 @@ MediaBrowser.Common - 3.0.22 + 3.0.23 MediaBrowser.Common Media Browser Team - + ebr,Luke,scottisafool https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false + Copyright © Media Browser 2013 Contains common model objects and interfaces used by all Media Browser solutions. diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index be7a20a8a2..7a195fe554 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,16 +2,17 @@ MediaBrowser.Server.Core - 3.0.22 + 3.0.23 Media Browser.Server.Core Media Browser Team - + ebr,Luke,scottisafool https://github.com/MediaBrowser/MediaBrowser http://www.mb3admin.com/images/mb3icons1-1.png false Contains core components required to build plugins for Media Browser Server. + Copyright © Media Browser 2013 - +