diff options
Diffstat (limited to 'MediaBrowser.Api/Plugin.cs')
| -rw-r--r-- | MediaBrowser.Api/Plugin.cs | 343 |
1 files changed, 329 insertions, 14 deletions
diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 8def96da8..c9e1d7c95 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -1,14 +1,329 @@ -using MediaBrowser.Common.Plugins;
-using System.ComponentModel.Composition;
-
-namespace MediaBrowser.Api
-{
- [Export(typeof(BasePlugin))]
- public class Plugin : BasePlugin
- {
- public override string Name
- {
- get { return "Media Browser API"; }
- }
- }
-}
+using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Api +{ + /// <summary> + /// Class Plugin + /// </summary> + [Export(typeof(IPlugin))] + public class Plugin : BasePlugin<BasePluginConfiguration> + { + /// <summary> + /// Gets the name of the plugin + /// </summary> + /// <value>The name.</value> + public override string Name + { + get { return "Web Api"; } + } + + /// <summary> + /// Gets a value indicating whether this instance is a core plugin. + /// </summary> + /// <value><c>true</c> if this instance is a core plugin; otherwise, <c>false</c>.</value> + public override bool IsCorePlugin + { + get + { + return true; + } + } + + /// <summary> + /// Gets the instance. + /// </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 + } +} |
