diff options
Diffstat (limited to 'win/CS/HandBrakeWPF/Services')
16 files changed, 725 insertions, 356 deletions
diff --git a/win/CS/HandBrakeWPF/Services/Encode/EncodeBase.cs b/win/CS/HandBrakeWPF/Services/Encode/EncodeBase.cs index 6d34a2efd..5563c2ed5 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/EncodeBase.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/EncodeBase.cs @@ -14,10 +14,8 @@ namespace HandBrakeWPF.Services.Encode using System.Globalization; using System.IO; - using HandBrake.Interop.Interop.EventArgs; using HandBrake.Interop.Model; - using HandBrakeWPF.Services.Encode.Interfaces; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Utilities; @@ -28,20 +26,18 @@ namespace HandBrakeWPF.Services.Encode using EncodeTask = HandBrakeWPF.Services.Encode.Model.EncodeTask; using GeneralApplicationException = HandBrakeWPF.Exceptions.GeneralApplicationException; using ILog = HandBrakeWPF.Services.Logging.Interfaces.ILog; - using LogService = HandBrakeWPF.Services.Logging.LogService; /// <summary> /// A Base Class for the Encode Services. /// </summary> public class EncodeBase { - private readonly ILog logService; + protected ILog encodeLogService; private readonly IUserSettingService userSettingService; - - public EncodeBase(ILog logService, IUserSettingService userSettingService) + + public EncodeBase(IUserSettingService userSettingService) { - this.logService = logService; this.userSettingService = userSettingService; } @@ -136,7 +132,7 @@ namespace HandBrakeWPF.Services.Encode string encodeDestinationPath = Path.GetDirectoryName(destination); string destinationFile = Path.GetFileName(destination); string encodeLogFile = destinationFile + " " + DateTime.Now.ToString(CultureInfo.InvariantCulture).Replace("/", "-").Replace(":", "-") + ".txt"; - string logContent = this.logService.GetFullLog(); + string logContent = this.encodeLogService.GetFullLog(); // Make sure the log directory exists. if (!Directory.Exists(logDir)) diff --git a/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs b/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs index b71d06d3f..e6d8cb7c0 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs @@ -12,9 +12,9 @@ namespace HandBrakeWPF.Services.Encode using System; using System.Diagnostics; using System.IO; + using System.Windows.Forms.VisualStyles; using HandBrake.Interop.Interop.EventArgs; - using HandBrake.Interop.Interop.HbLib; using HandBrake.Interop.Interop.Interfaces; using HandBrake.Interop.Interop.Json.State; using HandBrake.Interop.Interop.Providers.Interfaces; @@ -24,11 +24,12 @@ namespace HandBrakeWPF.Services.Encode using HandBrakeWPF.Properties; using HandBrakeWPF.Services.Encode.Factories; using HandBrakeWPF.Services.Interfaces; + using HandBrakeWPF.Services.Logging.Interfaces; + using HandBrakeWPF.Utilities; using EncodeTask = Model.EncodeTask; using HandBrakeInstanceManager = Instance.HandBrakeInstanceManager; using IEncode = Interfaces.IEncode; - using ILog = Logging.Interfaces.ILog; using LogService = Logging.LogService; /// <summary> @@ -36,40 +37,27 @@ namespace HandBrakeWPF.Services.Encode /// </summary> public class LibEncode : EncodeBase, IEncode { - #region Private Variables - - private readonly ILog log; private readonly IUserSettingService userSettingService; + private readonly ILogInstanceManager logInstanceManager; private readonly IHbFunctionsProvider hbFunctionsProvider; private IEncodeInstance instance; private DateTime startTime; private EncodeTask currentTask; private HBConfiguration currentConfiguration; private bool isPreviewInstance; + private bool isLoggingInitialised; + private int encodeCounter; - #endregion - - public LibEncode(IHbFunctionsProvider hbFunctionsProvider, ILog logService, IUserSettingService userSettingService) : base(logService, userSettingService) + public LibEncode(IHbFunctionsProvider hbFunctionsProvider, IUserSettingService userSettingService, ILogInstanceManager logInstanceManager, int encodeCounter) : base(userSettingService) { - this.log = logService; this.userSettingService = userSettingService; + this.logInstanceManager = logInstanceManager; this.hbFunctionsProvider = hbFunctionsProvider; + this.encodeCounter = encodeCounter; } - /// <summary> - /// Gets a value indicating whether is pasued. - /// </summary> public bool IsPasued { get; private set; } - /// <summary> - /// Start with a LibHb EncodeJob Object - /// </summary> - /// <param name="task"> - /// The task. - /// </param> - /// <param name="configuration"> - /// The configuration. - /// </param> public void Start(EncodeTask task, HBConfiguration configuration, string basePresetName) { try @@ -85,10 +73,17 @@ namespace HandBrakeWPF.Services.Encode this.currentTask = task; this.currentConfiguration = configuration; - // Create a new HandBrake instance - // Setup the HandBrake Instance - this.log.Reset(); // Reset so we have a clean log for the start of the encode. + if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ProcessIsolationEnabled)) + { + this.InitLogging(task.IsPreviewEncode); + } + else + { + this.encodeLogService = this.logInstanceManager.MasterLogInstance; + this.encodeLogService.Reset(); + } + if (this.instance != null) { // Cleanup @@ -112,7 +107,7 @@ namespace HandBrakeWPF.Services.Encode } int verbosity = this.userSettingService.GetUserSetting<int>(UserSettingConstants.Verbosity); - this.instance = task.IsPreviewEncode ? HandBrakeInstanceManager.GetPreviewInstance(verbosity, this.userSettingService) : HandBrakeInstanceManager.GetEncodeInstance(verbosity, configuration, this.log, userSettingService); + this.instance = task.IsPreviewEncode ? HandBrakeInstanceManager.GetPreviewInstance(verbosity, this.userSettingService) : HandBrakeInstanceManager.GetEncodeInstance(verbosity, configuration, this.encodeLogService, userSettingService); this.instance.EncodeCompleted += this.InstanceEncodeCompleted; this.instance.EncodeProgress += this.InstanceEncodeProgress; @@ -138,9 +133,6 @@ namespace HandBrakeWPF.Services.Encode } } - /// <summary> - /// Pause the currently running encode. - /// </summary> public void Pause() { if (this.instance != null) @@ -151,9 +143,6 @@ namespace HandBrakeWPF.Services.Encode } } - /// <summary> - /// Resume the currently running encode. - /// </summary> public void Resume() { if (this.instance != null) @@ -164,9 +153,6 @@ namespace HandBrakeWPF.Services.Encode } } - /// <summary> - /// Kill the process - /// </summary> public void Stop() { try @@ -188,38 +174,23 @@ namespace HandBrakeWPF.Services.Encode { if (this.currentTask != null) { - EncodeTask task = new EncodeTask(this.currentTask); // Decouple our current copy. + EncodeTask task = new EncodeTask(this.currentTask); // Shallow copy our current instance. return task; } return null; } - #region HandBrakeInstance Event Handlers. - - /// <summary> - /// Service Log Message. - /// </summary> - /// <param name="message">Log message content</param> protected void ServiceLogMessage(string message) { - this.log.LogMessage(string.Format("{0}# {1}", Environment.NewLine, message)); + this.encodeLogService.LogMessage(string.Format("{0}# {1}", Environment.NewLine, message)); } protected void TimedLogMessage(string message) { - this.log.LogMessage(string.Format("[{0}] {1}", DateTime.Now.ToString("hh:mm:ss"), message)); + this.encodeLogService.LogMessage(string.Format("[{0}] {1}", DateTime.Now.ToString("hh:mm:ss"), message)); } - /// <summary> - /// Encode Progress Event Handler - /// </summary> - /// <param name="sender"> - /// The sender. - /// </param> - /// <param name="e"> - /// The Interop.EncodeProgressEventArgs. - /// </param> private void InstanceEncodeProgress(object sender, EncodeProgressEventArgs e) { EventArgs.EncodeProgressEventArgs args = new EventArgs.EncodeProgressEventArgs @@ -238,16 +209,7 @@ namespace HandBrakeWPF.Services.Encode this.InvokeEncodeStatusChanged(args); } - - /// <summary> - /// Encode Completed Event Handler - /// </summary> - /// <param name="sender"> - /// The sender. - /// </param> - /// <param name="e"> - /// The e. - /// </param> + private void InstanceEncodeCompleted(object sender, EncodeCompletedEventArgs e) { this.IsEncoding = false; @@ -304,6 +266,18 @@ namespace HandBrakeWPF.Services.Encode return 0; } - #endregion + private void InitLogging(bool isPreview) + { + if (!isLoggingInitialised) + { + string logType = isPreview ? "preview" : "encode"; + string filename = string.Format("activity_log.{0}.{1}.{2}.txt", encodeCounter, logType, GeneralUtilities.ProcessId); + string logFile = Path.Combine(DirectoryUtilities.GetLogDirectory(), filename); + this.encodeLogService = new LogService(); + this.encodeLogService.ConfigureLogging(logFile); + this.logInstanceManager.RegisterLoggerInstance(filename, this.encodeLogService, false); + isLoggingInitialised = true; + } + } } } diff --git a/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILog.cs b/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILog.cs index d82a1ed0d..57594528a 100644 --- a/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILog.cs +++ b/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILog.cs @@ -12,17 +12,14 @@ namespace HandBrakeWPF.Services.Logging.Interfaces using System; using System.Collections.Generic; - using HandBrake.Worker.Logging.Interfaces; using HandBrake.Worker.Logging.Models; - using HandBrakeWPF.Services.Logging.Model; - using LogEventArgs = HandBrakeWPF.Services.Logging.EventArgs.LogEventArgs; /// <summary> /// The Log interface. /// </summary> - public interface ILog : ILogHandler + public interface ILog { /// <summary> /// The message logged. @@ -37,13 +34,13 @@ namespace HandBrakeWPF.Services.Logging.Interfaces /// <summary> /// Enable logging for this worker process. /// </summary> - /// <param name="config"> - /// Configuration for the logger. + /// <param name="filename"> + /// The filename. /// </param> /// <remarks> /// If this is not called, all log messages from libhb will be ignored. /// </remarks> - void ConfigureLogging(LogHandlerConfig config); + void ConfigureLogging(string filename); /// <summary> /// Log a message. @@ -52,5 +49,14 @@ namespace HandBrakeWPF.Services.Logging.Interfaces /// The content of the log message, /// </param> void LogMessage(string content); + + string GetFullLog(); + + List<LogMessage> GetLogMessages(); + + /// <summary> + /// Empty the log cache and reset the log handler to defaults. + /// </summary> + void Reset(); } }
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILogInstanceManager.cs b/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILogInstanceManager.cs new file mode 100644 index 000000000..c8af64601 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Logging/Interfaces/ILogInstanceManager.cs @@ -0,0 +1,50 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="ILogInstanceManager.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// <summary> +// Defines the ILogInstanceManager type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Logging.Interfaces +{ + using System; + using System.Collections.Generic; + + public interface ILogInstanceManager + { + event EventHandler NewLogInstanceRegistered; + + string ApplicationAndScanLog { get; } + + ILog MasterLogInstance { get; } + + /// <summary> + /// Register an ILog instance. + /// </summary> + /// <param name="filename"> + /// This is the key associated with the log instance. + /// </param> + /// <param name="log"> + /// The ILog instance + /// </param> + /// <param name="isMaster"> + /// True indicates it's the log instance for the parent handbrake process. + /// </param> + void RegisterLoggerInstance(string filename, ILog log, bool isMaster); + + /// <summary> + /// Gets a list of files without their associated ILog instances. + /// </summary> + /// <returns>List of filenames being logged</returns> + List<string> GetLogFiles(); + + /// <summary> + /// Get the ILog instance for a given filename key + /// </summary> + /// <param name="filename">The key of the log instance</param> + /// <returns>An ILog instance or null if invalid key</returns> + ILog GetLogInstance(string filename); + } +} diff --git a/win/CS/HandBrakeWPF/Services/Logging/LogInstanceManager.cs b/win/CS/HandBrakeWPF/Services/Logging/LogInstanceManager.cs new file mode 100644 index 000000000..4e1290024 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Logging/LogInstanceManager.cs @@ -0,0 +1,93 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="LogInstanceManager.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// <summary> +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Logging +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using HandBrakeWPF.Services.Interfaces; + using HandBrakeWPF.Services.Logging.Interfaces; + + using Microsoft.Win32.SafeHandles; + + public class LogInstanceManager : ILogInstanceManager + { + private Dictionary<string, ILog> logInstances = new Dictionary<string, ILog>(); + + private int maxInstances; + + public LogInstanceManager(IUserSettingService userSettingService) + { + this.maxInstances = userSettingService.GetUserSetting<int>(UserSettingConstants.SimultaneousEncodes); + } + + public event EventHandler NewLogInstanceRegistered; + + public string ApplicationAndScanLog { get; private set; } + + public ILog MasterLogInstance { get; private set; } + + public void RegisterLoggerInstance(string filename, ILog log, bool isMaster) + { + if (string.IsNullOrEmpty(this.ApplicationAndScanLog)) + { + // The application startup sets the initial log file. + this.ApplicationAndScanLog = filename; + } + + this.logInstances.Add(filename, log); + + this.CleanupInstance(); + + if (isMaster) + { + this.MasterLogInstance = log; + } + + this.OnNewLogInstanceRegistered(); + } + + public List<string> GetLogFiles() + { + return this.logInstances.Keys.ToList(); + } + + public ILog GetLogInstance(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + return null; + } + + ILog logger; + if (this.logInstances.TryGetValue(filename, out logger)) + { + return logger; + } + + return null; + } + + protected virtual void OnNewLogInstanceRegistered() + { + this.NewLogInstanceRegistered?.Invoke(this, System.EventArgs.Empty); + } + + private void CleanupInstance() + { + List<string> encodeLogs = this.logInstances.Keys.Where(f => f.Contains(".encode.")).ToList(); + string removalKey = this.logInstances.Keys.OrderBy(k => k).FirstOrDefault(w => w.Contains(".encode.")); + if (encodeLogs.Count > this.maxInstances) + { + this.logInstances.Remove(removalKey); + } + } + } +} diff --git a/win/CS/HandBrakeWPF/Services/Logging/LogService.cs b/win/CS/HandBrakeWPF/Services/Logging/LogService.cs index 738cb2b3a..18a8ffaee 100644 --- a/win/CS/HandBrakeWPF/Services/Logging/LogService.cs +++ b/win/CS/HandBrakeWPF/Services/Logging/LogService.cs @@ -18,47 +18,30 @@ namespace HandBrakeWPF.Services.Logging using System.IO; using System.Linq; using System.Text; - using System.Timers; - using HandBrake.Interop.Interop; - using HandBrake.Interop.Interop.EventArgs; - using HandBrake.Worker.Logging.Interfaces; using HandBrake.Worker.Logging.Models; - using HandBrakeWPF.Instance; - using HandBrakeWPF.Instance.Model; - using HandBrakeWPF.Services.Interfaces; - using HandBrakeWPF.Services.Logging.Model; using HandBrakeWPF.Utilities; - using Newtonsoft.Json; - using ILog = Interfaces.ILog; using LogEventArgs = EventArgs.LogEventArgs; - public class LogService : ILog + public class LogService : ILog, IDisposable { - // TODO List. - // Maybe make the event weak? - // Make this class Thread Safe. - private static ILog loggerInstance; private readonly object lockObject = new object(); private readonly object fileWriterLock = new object(); private readonly StringBuilder logBuilder = new StringBuilder(); - + private readonly List<LogMessage> logMessages = new List<LogMessage>(); + private bool isLoggingEnabled; - private List<LogMessage> logMessages = new List<LogMessage>(); private int messageIndex; private string diskLogPath; - private bool deleteLogFirst; private bool isDiskLoggingEnabled; private StreamWriter fileWriter; private string logHeader; - public LogService(IUserSettingService userSettingService) + public LogService() { - HandBrakeUtils.MessageLogged += this.HandBrakeUtils_MessageLogged; - HandBrakeUtils.ErrorLogged += this.HandBrakeUtils_ErrorLogged; } public event EventHandler<LogEventArgs> MessageLogged; @@ -86,7 +69,7 @@ namespace HandBrakeWPF.Services.Logging LogMessage msg = new LogMessage(content, this.messageIndex); lock (this.lockObject) { - this.messageIndex = this.messageIndex + 1; + this.messageIndex = this.messageIndex + 1; this.logMessages.Add(msg); this.logBuilder.AppendLine(msg.Content); this.LogMessageToDisk(msg); @@ -106,22 +89,19 @@ namespace HandBrakeWPF.Services.Logging this.OnMessageLogged(msg); // Must be outside lock to be thread safe. } - public void ConfigureLogging(LogHandlerConfig config) + public void ConfigureLogging(string filename) { this.isLoggingEnabled = true; - if (config.EnableDiskLogging) + if (!string.IsNullOrEmpty(filename) && !Directory.Exists(Path.GetDirectoryName(filename))) { - if (!string.IsNullOrEmpty(config.LogFile) && !Directory.Exists(Path.GetDirectoryName(config.LogFile))) - { - Directory.CreateDirectory(Path.GetDirectoryName(config.LogFile)); - } - - this.EnableLoggingToDisk(config.LogFile, config.DeleteCurrentLogFirst); + Directory.CreateDirectory(Path.GetDirectoryName(filename)); } - this.logHeader = config.Header; - this.LogMessage(config.Header); + this.EnableLoggingToDisk(filename); + + this.logHeader = GeneralUtilities.CreateLogHeader().ToString(); + this.LogMessage(logHeader); } public string GetFullLog() @@ -155,14 +135,6 @@ namespace HandBrakeWPF.Services.Logging return log; } - public long GetLatestLogIndex() - { - lock (this.lockObject) - { - return this.messageIndex; - } - } - public async void Reset() { lock (this.lockObject) @@ -170,30 +142,13 @@ namespace HandBrakeWPF.Services.Logging this.logMessages.Clear(); this.logBuilder.Clear(); this.messageIndex = 0; - - try - { - lock (this.fileWriterLock) - { - if (this.fileWriter != null) - { - this.fileWriter.Flush(); - this.fileWriter.Close(); - this.fileWriter.Dispose(); - } - - this.fileWriter = null; - } - } - catch (Exception exc) - { - Debug.WriteLine(exc); - } + + this.ShutdownFileWriter(); if (this.fileWriter == null) { this.isDiskLoggingEnabled = false; - this.EnableLoggingToDisk(this.diskLogPath, this.deleteLogFirst); + this.EnableLoggingToDisk(this.diskLogPath); } if (!string.IsNullOrEmpty(this.logHeader)) @@ -205,16 +160,18 @@ namespace HandBrakeWPF.Services.Logging } } + public void Dispose() + { + this.ShutdownFileWriter(); + } + protected virtual void OnMessageLogged(LogMessage msg) { var onMessageLogged = this.MessageLogged; - if (onMessageLogged != null) - { - onMessageLogged.Invoke(this, new LogEventArgs(msg)); - } + onMessageLogged?.Invoke(this, new LogEventArgs(msg)); } - protected void ShutdownFileWriter() + private void ShutdownFileWriter() { try { @@ -241,7 +198,7 @@ namespace HandBrakeWPF.Services.Logging this.LogReset?.Invoke(this, System.EventArgs.Empty); } - private void EnableLoggingToDisk(string logFile, bool deleteCurrentLogFirst) + private void EnableLoggingToDisk(string logFile) { if (this.isDiskLoggingEnabled) { @@ -255,14 +212,13 @@ namespace HandBrakeWPF.Services.Logging throw new Exception("Log Directory does not exist. This service will not create it for you!"); } - if (deleteCurrentLogFirst && File.Exists(logFile)) + if (File.Exists(logFile)) { File.Delete(logFile); } this.diskLogPath = logFile; this.isDiskLoggingEnabled = true; - this.deleteLogFirst = deleteCurrentLogFirst; lock (this.fileWriterLock) { @@ -313,30 +269,5 @@ namespace HandBrakeWPF.Services.Logging Debug.WriteLine(exc); // This exception doesn't warrant user interaction, but it should be logged } } - - private void HandBrakeUtils_ErrorLogged(object sender, MessageLoggedEventArgs e) - { - if (e == null || string.IsNullOrEmpty(e.Message)) - { - return; - } - - this.LogMessage(e.Message); - } - - private void HandBrakeUtils_MessageLogged(object sender, MessageLoggedEventArgs e) - { - if (e == null || string.IsNullOrEmpty(e.Message)) - { - return; - } - - this.LogMessage(e.Message); - } - - void ILogHandler.ShutdownFileWriter() - { - throw new NotImplementedException(); - } } } diff --git a/win/CS/HandBrakeWPF/Services/PrePostActionService.cs b/win/CS/HandBrakeWPF/Services/PrePostActionService.cs index e8118f2a3..2eba7b38c 100644 --- a/win/CS/HandBrakeWPF/Services/PrePostActionService.cs +++ b/win/CS/HandBrakeWPF/Services/PrePostActionService.cs @@ -34,7 +34,6 @@ namespace HandBrakeWPF.Services public class PrePostActionService : IPrePostActionService
{
private readonly ILog log;
- private readonly IQueueService queueProcessor;
private readonly IUserSettingService userSettingService;
private readonly IWindowManager windowManager;
private readonly IScan scanService;
@@ -42,14 +41,26 @@ namespace HandBrakeWPF.Services public PrePostActionService(IQueueService queueProcessor, IUserSettingService userSettingService, IWindowManager windowManager, IScan scanService, ILog logService)
{
this.log = logService;
- this.queueProcessor = queueProcessor;
this.userSettingService = userSettingService;
this.windowManager = windowManager;
this.scanService = scanService;
- this.queueProcessor.QueueCompleted += this.QueueProcessorQueueCompleted;
- this.queueProcessor.EncodeService.EncodeCompleted += this.EncodeService_EncodeCompleted;
- this.queueProcessor.EncodeService.EncodeStarted += this.EncodeService_EncodeStarted;
+ queueProcessor.QueueCompleted += this.QueueProcessorQueueCompleted;
+ queueProcessor.QueuePaused += this.QueueProcessor_QueuePaused;
+ queueProcessor.EncodeCompleted += this.EncodeService_EncodeCompleted;
+ queueProcessor.JobProcessingStarted += this.EncodeService_EncodeStarted;
+ }
+
+ private void QueueProcessor_QueuePaused(object sender, EventArgs e)
+ {
+ // Allow the system to sleep again.
+ Execute.OnUIThread(() =>
+ {
+ if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PreventSleep))
+ {
+ Win32.AllowSleep();
+ }
+ });
}
/// <summary>
@@ -90,15 +101,6 @@ namespace HandBrakeWPF.Services {
this.PlayWhenDoneSound();
}
-
- // Allow the system to sleep again.
- Execute.OnUIThread(() =>
- {
- if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PreventSleep))
- {
- Win32.AllowSleep();
- }
- });
}
/// <summary>
@@ -172,6 +174,15 @@ namespace HandBrakeWPF.Services break;
}
}
+
+ // Allow the system to sleep again.
+ Execute.OnUIThread(() =>
+ {
+ if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PreventSleep))
+ {
+ Win32.AllowSleep();
+ }
+ });
}
private void SendToApplication(string source, string destination)
diff --git a/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs b/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs new file mode 100644 index 000000000..10fffcbf0 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs @@ -0,0 +1,131 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="ActiveJob.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// <summary> +// Defines the ActiveJob type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Queue +{ + using System; + + using HandBrake.Interop.Interop.Providers.Interfaces; + + using HandBrakeWPF.Services.Encode; + using HandBrakeWPF.Services.Encode.EventArgs; + using HandBrakeWPF.Services.Encode.Interfaces; + using HandBrakeWPF.Services.Interfaces; + using HandBrakeWPF.Services.Logging.Interfaces; + using HandBrakeWPF.Services.Queue.JobEventArgs; + using HandBrakeWPF.Services.Queue.Model; + + public class ActiveJob : IDisposable + { + private readonly QueueTask job; + private readonly IEncode encodeService; + + public ActiveJob(QueueTask task, IHbFunctionsProvider hbFunctionsProvider, IUserSettingService userSettingService, ILogInstanceManager logInstanceManager, int jobId) + { + this.job = task; + this.encodeService = new LibEncode(hbFunctionsProvider, userSettingService, logInstanceManager, jobId); + } + + public event EventHandler<ActiveJobCompletedEventArgs> JobFinished; + + public event EventHandler<EncodeProgressEventArgs> JobStatusUpdated; + + public QueueTask Job => this.job; + + public bool IsPaused { get; private set; } + + public bool IsEncoding { get; set; } + + public void Start() + { + this.IsPaused = false; + this.IsEncoding = true; + + if (this.encodeService.IsPasued) + { + this.encodeService.Resume(); + this.job.Statistics.SetPaused(false); + this.job.Status = QueueItemStatus.InProgress; + } + else if (!this.encodeService.IsEncoding) + { + this.job.Status = QueueItemStatus.InProgress; + this.job.Statistics.StartTime = DateTime.Now; + + this.encodeService.EncodeCompleted += this.EncodeServiceEncodeCompleted; + this.encodeService.EncodeStatusChanged += this.EncodeStatusChanged; + this.encodeService.Start(this.job.Task, this.job.Configuration, this.job.SelectedPresetKey); + } + } + + public void Pause() + { + if (this.encodeService.IsEncoding && !this.encodeService.IsPasued) + { + this.IsPaused = true; + this.encodeService.Pause(); + this.job.Statistics.SetPaused(true); + this.job.Status = QueueItemStatus.Paused; + this.IsEncoding = false; + } + } + + public void Stop() + { + if (this.encodeService.IsEncoding) + { + this.encodeService.Stop(); + } + + this.IsEncoding = false; + this.IsPaused = false; + this.encodeService.EncodeStatusChanged -= this.EncodeStatusChanged; + } + + public void Dispose() + { + this.encodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; + this.encodeService.EncodeStatusChanged -= this.EncodeStatusChanged; + } + + private void EncodeStatusChanged(object sender, EncodeProgressEventArgs e) + { + this.job?.JobProgress.Update(e); + this.OnJobStatusUpdated(e); + } + + private void EncodeServiceEncodeCompleted(object sender, EncodeCompletedEventArgs e) + { + this.IsEncoding = false; + this.IsPaused = false; + + this.job.Status = !e.Successful ? QueueItemStatus.Error : QueueItemStatus.Completed; + this.job.Statistics.EndTime = DateTime.Now; + this.job.Statistics.CompletedActivityLogPath = e.ActivityLogPath; + this.job.Statistics.FinalFileSize = e.FinalFilesizeInBytes; + + this.job.JobProgress.ClearStatusDisplay(); + + this.encodeService.EncodeStatusChanged -= this.EncodeStatusChanged; + this.encodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; + + this.OnJobFinished(e); + } + + private void OnJobFinished(EncodeCompletedEventArgs e) + { + this.JobFinished?.Invoke(this, new ActiveJobCompletedEventArgs(this, e)); + } + + private void OnJobStatusUpdated(EncodeProgressEventArgs e) + { + this.JobStatusUpdated?.Invoke(this, e); + } + } +} diff --git a/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs b/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs index 1bab4f01d..1a2a04cce 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs @@ -10,24 +10,29 @@ namespace HandBrakeWPF.Services.Queue.Interfaces { using System; + using System.Collections.Generic; using System.Collections.ObjectModel; - using System.ComponentModel; - using HandBrakeWPF.Services.Queue.Model; - using IEncode = Encode.Interfaces.IEncode; + using HandBrakeWPF.EventArgs; + using HandBrakeWPF.Services.Encode.EventArgs; + using HandBrakeWPF.Services.Encode.Interfaces; + using HandBrakeWPF.Services.Queue.Model; /// <summary> /// The Queue Processor /// </summary> public interface IQueueService { - #region Events - /// <summary> /// Fires when the Queue has started /// </summary> - event QueueService.QueueProgressStatus JobProcessingStarted; + event EventHandler<QueueProgressEventArgs> JobProcessingStarted; + + /// <summary> + /// Fires when the status of any running job changes on the queue. Including progress. + /// </summary> + event EventHandler QueueJobStatusChanged; /// <summary> /// Fires when a job is Added, Removed or Re-Ordered. @@ -38,16 +43,14 @@ namespace HandBrakeWPF.Services.Queue.Interfaces /// <summary> /// Fires when the entire encode queue has completed. /// </summary> - event QueueService.QueueCompletedEventDelegate QueueCompleted; + event EventHandler<QueueCompletedEventArgs> QueueCompleted; /// <summary> /// Fires when a pause to the encode queue has been requested. /// </summary> event EventHandler QueuePaused; - #endregion - - #region Properties + event EventHandler<EncodeCompletedEventArgs> EncodeCompleted; /// <summary> /// Gets the number of jobs in the queue @@ -60,30 +63,19 @@ namespace HandBrakeWPF.Services.Queue.Interfaces int ErrorCount { get; } /// <summary> - /// Gets the IEncodeService instance. - /// </summary> - IEncode EncodeService { get; } - - /// <summary> /// Gets a value indicating whether IsProcessing. /// </summary> bool IsProcessing { get; } - /// <summary> - /// Gets or sets Last Processed Job. - /// This is set when the job is poped of the queue by GetNextJobForProcessing(); - /// </summary> - QueueTask LastProcessedJob { get; set; } + bool IsEncoding { get; } + + bool IsPaused { get; } /// <summary> /// Gets The current queue. /// </summary> ObservableCollection<QueueTask> Queue { get; } - #endregion - - #region Public Methods - /// <summary> /// Add a job to the Queue. /// This method is Thread Safe. @@ -223,12 +215,15 @@ namespace HandBrakeWPF.Services.Queue.Interfaces /// Pause the queue but allow the current encode to complete. /// </summary> void Pause(); - + /// <summary> - /// Pause and Encode and the Queue. + /// Get the status of all running queue jobs. /// </summary> - void PauseEncode(); + /// <returns> + /// A list of QueueProgressStatus items + /// </returns> + List<QueueProgressStatus> GetQueueProgressStatus(); - #endregion + List<string> GetActiveJobDestinationDirectories(); } }
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Queue/JobEventArgs/ActiveJobCompletedEventArgs.cs b/win/CS/HandBrakeWPF/Services/Queue/JobEventArgs/ActiveJobCompletedEventArgs.cs new file mode 100644 index 000000000..dae67e640 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Queue/JobEventArgs/ActiveJobCompletedEventArgs.cs @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="ActiveJobCompletedEventArgs.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Queue.JobEventArgs +{ + using System; + + using HandBrakeWPF.Services.Encode.EventArgs; + + public class ActiveJobCompletedEventArgs : EventArgs + { + public ActiveJobCompletedEventArgs(ActiveJob job, EncodeCompletedEventArgs encodeCompletedEventArgs) + { + this.EncodeEventArgs = encodeCompletedEventArgs; + this.Job = job; + } + + public ActiveJob Job { get; private set; } + + public EncodeCompletedEventArgs EncodeEventArgs { get; private set; } + } +} diff --git a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueItemStatus.cs b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueItemStatus.cs index c398e6a32..d7d0dbce2 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueItemStatus.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueItemStatus.cs @@ -27,5 +27,8 @@ namespace HandBrakeWPF.Services.Queue.Model [DisplayName("Error")]
Error,
+
+ [DisplayName("Paused")]
+ Paused,
}
}
diff --git a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueProgressStatus.cs b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueProgressStatus.cs new file mode 100644 index 000000000..f759dfeb8 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueProgressStatus.cs @@ -0,0 +1,133 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="QueueProgressStatus.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// <summary> +// Defines the QueueProgressStatus type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Queue.Model +{ + using System; + using System.Xaml; + + using Caliburn.Micro; + + using HandBrakeWPF.Properties; + using HandBrakeWPF.Services.Encode.EventArgs; + + public class QueueProgressStatus : PropertyChangedBase + { + private string jobStatus; + private bool intermediateProgress; + private double progressValue; + + private EncodeProgressEventArgs progressEventArgs; + + public QueueProgressStatus() + { + } + + public string JobStatus + { + get + { + return this.jobStatus; + } + + set + { + this.jobStatus = value; + this.NotifyOfPropertyChange(() => this.JobStatus); + } + } + + public bool IntermediateProgress + { + get => this.intermediateProgress; + set + { + if (value == this.intermediateProgress) return; + this.intermediateProgress = value; + this.NotifyOfPropertyChange(() => this.IntermediateProgress); + } + } + + public double ProgressValue + { + get => this.progressValue; + set + { + if (value == this.progressValue) return; + this.progressValue = value; + this.NotifyOfPropertyChange(() => this.ProgressValue); + } + } + + public int? Task => this.progressEventArgs?.Task ?? 0; + + public int? TaskCount => this.progressEventArgs?.TaskCount ?? 0; + + public TimeSpan? EstimatedTimeLeft => this.progressEventArgs?.EstimatedTimeLeft ?? null; + + public void Update(EncodeProgressEventArgs e) + { + progressEventArgs = e; + this.IntermediateProgress = false; + + string totalHrsLeft = e.EstimatedTimeLeft.Days >= 1 ? string.Format(@"{0:d\:hh\:mm\:ss}", e.EstimatedTimeLeft) : string.Format(@"{0:hh\:mm\:ss}", e.EstimatedTimeLeft); + string elapsedTimeHrs = e.ElapsedTime.Days >= 1 ? string.Format(@"{0:d\:hh\:mm\:ss}", e.ElapsedTime) : string.Format(@"{0:hh\:mm\:ss}", e.ElapsedTime); + + if (e.IsSubtitleScan) + { + this.JobStatus = string.Format(Resources.MainViewModel_EncodeStatusChanged_SubScan_StatusLabel, + e.Task, + e.TaskCount, + e.PercentComplete, + totalHrsLeft, + elapsedTimeHrs, + null); + + this.ProgressValue = e.PercentComplete; + } + else if (e.IsMuxing) + { + this.JobStatus = Resources.MainView_Muxing; + this.IntermediateProgress = true; + } + else if (e.IsSearching) + { + this.JobStatus = string.Format(Resources.MainView_ProgressStatusWithTask, Resources.MainView_Searching, e.PercentComplete, e.EstimatedTimeLeft, null); + this.ProgressValue = e.PercentComplete; + } + else + { + this.JobStatus = + string.Format(Resources.QueueViewModel_EncodeStatusChanged_StatusLabel, + e.Task, + e.TaskCount, + e.PercentComplete, + e.CurrentFrameRate, + e.AverageFrameRate, + totalHrsLeft, + elapsedTimeHrs, + null); + this.ProgressValue = e.PercentComplete; + } + } + + public void SetPaused() + { + this.ClearStatusDisplay(); + this.JobStatus = Resources.QueueViewModel_QueuePaused; + } + + public void ClearStatusDisplay() + { + this.JobStatus = string.Empty; + this.ProgressValue = 0; + this.IntermediateProgress = false; + } + } +} diff --git a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs index 3ee126ce9..208c721e4 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs @@ -35,6 +35,7 @@ namespace HandBrakeWPF.Services.Queue.Model id = id + 1;
this.Id = string.Format("{0}.{1}", GeneralUtilities.ProcessId, id);
this.Statistics = new QueueStats();
+ this.JobProgress = new QueueProgressStatus();
}
public QueueTask(EncodeTask task, HBConfiguration configuration, string scannedSourcePath, Preset currentPreset, bool isPresetModified)
@@ -57,6 +58,7 @@ namespace HandBrakeWPF.Services.Queue.Model this.Statistics = new QueueStats();
this.TaskId = Guid.NewGuid().ToString();
+ this.JobProgress = new QueueProgressStatus();
}
[JsonProperty]
@@ -81,6 +83,7 @@ namespace HandBrakeWPF.Services.Queue.Model this.status = value;
this.NotifyOfPropertyChange(() => this.Status);
this.NotifyOfPropertyChange(() => this.ShowEncodeProgress);
+ this.NotifyOfPropertyChange(() => this.IsJobStatusVisible);
}
}
@@ -94,6 +97,12 @@ namespace HandBrakeWPF.Services.Queue.Model public QueueStats Statistics { get; set; }
[JsonIgnore]
+ public QueueProgressStatus JobProgress { get; set; }
+
+ [JsonIgnore]
+ public bool IsJobStatusVisible => this.Status == QueueItemStatus.InProgress;
+
+ [JsonIgnore]
public string SelectedPresetKey
{
get
diff --git a/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs b/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs index 9ebd4d33b..6cbf709e5 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs @@ -12,12 +12,10 @@ namespace HandBrakeWPF.Services.Queue using System; using System.Collections.Generic; using System.Collections.ObjectModel; - using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Windows; - using System.Windows.Media.Imaging; using Caliburn.Micro; @@ -33,6 +31,9 @@ namespace HandBrakeWPF.Services.Queue using HandBrakeWPF.Services.Encode.Factories; using HandBrakeWPF.Services.Encode.Model; using HandBrakeWPF.Services.Interfaces; + using HandBrakeWPF.Services.Logging.Interfaces; + using HandBrakeWPF.Services.Queue.Interfaces; + using HandBrakeWPF.Services.Queue.JobEventArgs; using HandBrakeWPF.Services.Queue.Model; using HandBrakeWPF.Utilities; @@ -41,46 +42,53 @@ namespace HandBrakeWPF.Services.Queue using EncodeCompletedEventArgs = HandBrakeWPF.Services.Encode.EventArgs.EncodeCompletedEventArgs; using Execute = Caliburn.Micro.Execute; using GeneralApplicationException = HandBrakeWPF.Exceptions.GeneralApplicationException; - using IEncode = HandBrakeWPF.Services.Encode.Interfaces.IEncode; using ILog = HandBrakeWPF.Services.Logging.Interfaces.ILog; - using LogService = HandBrakeWPF.Services.Logging.LogService; using QueueCompletedEventArgs = HandBrakeWPF.EventArgs.QueueCompletedEventArgs; using QueueProgressEventArgs = HandBrakeWPF.EventArgs.QueueProgressEventArgs; - public class QueueService : Interfaces.IQueueService + public class QueueService : IQueueService { private static readonly object QueueLock = new object(); + + private readonly List<ActiveJob> activeJobs = new List<ActiveJob>(); private readonly IUserSettingService userSettingService; private readonly ILog logService; private readonly IErrorService errorService; + private readonly ILogInstanceManager logInstanceManager; + private readonly IHbFunctionsProvider hbFunctionsProvider; private readonly ObservableCollection<QueueTask> queue = new ObservableCollection<QueueTask>(); private readonly string queueFile; private bool clearCompleted; + private int allowedInstances; + private int jobIdCounter = 0; - public QueueService(IEncode encodeService, IUserSettingService userSettingService, ILog logService, IErrorService errorService) + public QueueService(IUserSettingService userSettingService, ILog logService, IErrorService errorService, ILogInstanceManager logInstanceManager, IHbFunctionsProvider hbFunctionsProvider) { this.userSettingService = userSettingService; this.logService = logService; this.errorService = errorService; - this.EncodeService = encodeService; + this.logInstanceManager = logInstanceManager; + this.hbFunctionsProvider = hbFunctionsProvider; // If this is the first instance, just use the main queue file, otherwise add the instance id to the filename. this.queueFile = string.Format("{0}{1}.json", QueueRecoveryHelper.QueueFileName, GeneralUtilities.ProcessId); - } - public delegate void QueueProgressStatus(object sender, QueueProgressEventArgs e); - - public delegate void QueueCompletedEventDelegate(object sender, QueueCompletedEventArgs e); + this.allowedInstances = this.userSettingService.GetUserSetting<int>(UserSettingConstants.SimultaneousEncodes); + } - public event QueueProgressStatus JobProcessingStarted; + public event EventHandler<QueueProgressEventArgs> JobProcessingStarted; public event EventHandler QueueChanged; - public event QueueCompletedEventDelegate QueueCompleted; + public event EventHandler<QueueCompletedEventArgs> QueueCompleted; public event EventHandler QueuePaused; + public event EventHandler QueueJobStatusChanged; + + public event EventHandler<EncodeCompletedEventArgs> EncodeCompleted; + public int Count { get @@ -97,11 +105,11 @@ namespace HandBrakeWPF.Services.Queue } } - public IEncode EncodeService { get; private set; } + public bool IsPaused { get; private set; } public bool IsProcessing { get; private set; } - public QueueTask LastProcessedJob { get; set; } + public bool IsEncoding => this.activeJobs.Any(service => service.IsEncoding); public ObservableCollection<QueueTask> Queue { @@ -110,8 +118,7 @@ namespace HandBrakeWPF.Services.Queue return this.queue; } } - - + public void Add(QueueTask job) { lock (QueueLock) @@ -227,7 +234,7 @@ namespace HandBrakeWPF.Services.Queue foreach (QueueTask task in reloadedQueue) { // Reset the imported jobs that were running in a previous session. - if (task.Status == QueueItemStatus.InProgress) + if (task.Status == QueueItemStatus.InProgress || task.Status == QueueItemStatus.Paused) { task.Status = QueueItemStatus.Waiting; task.Statistics.Reset(); @@ -249,8 +256,7 @@ namespace HandBrakeWPF.Services.Queue } } } - - + public bool CheckForDestinationPathDuplicates(string destination) { foreach (QueueTask job in this.queue) @@ -396,7 +402,7 @@ namespace HandBrakeWPF.Services.Queue if (item.Status != QueueItemStatus.Completed) { // Reset InProgress/Error to Waiting so it can be processed - if (item.Status == QueueItemStatus.InProgress) + if (item.Status == QueueItemStatus.InProgress || item.Status == QueueItemStatus.Paused) { item.Status = QueueItemStatus.Error; } @@ -418,19 +424,17 @@ namespace HandBrakeWPF.Services.Queue public void Pause() { - this.IsProcessing = false; - this.InvokeQueuePaused(EventArgs.Empty); - } - - public void PauseEncode() - { - if (this.EncodeService.IsEncoding && !this.EncodeService.IsPasued) + foreach (ActiveJob job in this.activeJobs) { - this.EncodeService.Pause(); - this.LastProcessedJob.Statistics.SetPaused(true); + if (job.IsEncoding && !job.IsPaused) + { + job.Pause(); + } } - - this.Pause(); + + this.IsProcessing = false; + this.IsPaused = true; + this.InvokeQueuePaused(EventArgs.Empty); } public void Start(bool isClearCompleted) @@ -440,89 +444,63 @@ namespace HandBrakeWPF.Services.Queue return; } + this.IsPaused = false; this.clearCompleted = isClearCompleted; - this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; - this.EncodeService.EncodeCompleted += this.EncodeServiceEncodeCompleted; - - if (this.EncodeService.IsPasued) + // Unpause all active jobs. + foreach (ActiveJob job in this.activeJobs) { - this.EncodeService.Resume(); - this.IsProcessing = true; - this.InvokeJobProcessingStarted(new QueueProgressEventArgs(this.LastProcessedJob)); - this.LastProcessedJob.Statistics.SetPaused(false); + job.Start(); + this.InvokeJobProcessingStarted(new QueueProgressEventArgs(job.Job)); } - if (!this.EncodeService.IsEncoding) - { - this.ProcessNextJob(); - } - else - { - this.IsProcessing = true; - } + this.ProcessNextJob(); + this.IsProcessing = true; } public void Stop() { - if (this.EncodeService.IsEncoding) + foreach (ActiveJob job in this.activeJobs) { - this.EncodeService.Stop(); + if (job.IsEncoding || job.IsPaused) + { + job.Stop(); + } } this.IsProcessing = false; - this.InvokeQueuePaused(EventArgs.Empty); + this.IsPaused = false; + this.InvokeQueueChanged(EventArgs.Empty); + this.InvokeQueueCompleted(new QueueCompletedEventArgs(true)); } - protected virtual void OnQueueCompleted(QueueCompletedEventArgs e) + public List<QueueProgressStatus> GetQueueProgressStatus() { - QueueCompletedEventDelegate handler = this.QueueCompleted; - if (handler != null) + // TODO make thread safe. + List<QueueProgressStatus> statuses = new List<QueueProgressStatus>(); + foreach (ActiveJob job in this.activeJobs) { - handler(this, e); + statuses.Add(job.Job.JobProgress); } - this.IsProcessing = false; + return statuses; } - private void EncodeServiceEncodeCompleted(object sender, EncodeCompletedEventArgs e) + public List<string> GetActiveJobDestinationDirectories() { - this.LastProcessedJob.Status = QueueItemStatus.Completed; - this.LastProcessedJob.Statistics.EndTime = DateTime.Now; - this.LastProcessedJob.Statistics.CompletedActivityLogPath = e.ActivityLogPath; - this.LastProcessedJob.Statistics.FinalFileSize = e.FinalFilesizeInBytes; - - // Clear the completed item of the queue if the setting is set. - if (this.clearCompleted) - { - this.ClearCompleted(); - } - - if (!e.Successful) + // TODO need to make thread safe. + List<string> directories = new List<string>(); + foreach (ActiveJob job in this.activeJobs) { - this.LastProcessedJob.Status = QueueItemStatus.Error; + directories.Add(job.Job.Task.Destination); } - // Move onto the next job. - if (this.IsProcessing) - { - this.ProcessNextJob(); - } - else - { - this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; - this.BackupQueue(string.Empty); - this.OnQueueCompleted(new QueueCompletedEventArgs(true)); - } + return directories; } private void InvokeJobProcessingStarted(QueueProgressEventArgs e) { - QueueProgressStatus handler = this.JobProcessingStarted; - if (handler != null) - { - handler(this, e); - } + this.JobProcessingStarted?.Invoke(this, e); } private void InvokeQueueChanged(EventArgs e) @@ -542,73 +520,90 @@ namespace HandBrakeWPF.Services.Queue handler(this, e); } } - - private void InvokeQueuePaused(EventArgs e) + + private void ProcessNextJob() { - this.IsProcessing = false; - - EventHandler handler = this.QueuePaused; - if (handler != null) + if (this.activeJobs.Count >= this.allowedInstances) { - handler(this, e); + return; } - } - private void ProcessNextJob() - { QueueTask job = this.GetNextJobForProcessing(); if (job != null) { - if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(job.Task.Destination, this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel))) + if (CheckDiskSpace(job)) { - this.logService.LogMessage(Resources.PauseOnLowDiskspace); - job.Status = QueueItemStatus.Waiting; - this.Pause(); - this.BackupQueue(string.Empty); return; // Don't start the next job. } - job.Status = QueueItemStatus.InProgress; - job.Statistics.StartTime = DateTime.Now; - this.LastProcessedJob = job; + this.jobIdCounter = this.jobIdCounter + 1; + ActiveJob activeJob = new ActiveJob(job, this.hbFunctionsProvider, this.userSettingService, this.logInstanceManager, this.jobIdCounter); + activeJob.JobFinished += this.ActiveJob_JobFinished; + activeJob.JobStatusUpdated += this.ActiveJob_JobStatusUpdated; + this.activeJobs.Add(activeJob); + + activeJob.Start(); + this.IsProcessing = true; this.InvokeQueueChanged(EventArgs.Empty); this.InvokeJobProcessingStarted(new QueueProgressEventArgs(job)); - - if (!Directory.Exists(Path.GetDirectoryName(job.Task.Destination))) - { - this.EncodeServiceEncodeCompleted(null, new EncodeCompletedEventArgs(false, null, "Destination Directory Missing", null, null, null, 0)); - this.BackupQueue(string.Empty); - return; - } - - this.EncodeService.Start(job.Task, job.Configuration, job.SelectedPresetKey); this.BackupQueue(string.Empty); + + this.ProcessNextJob(); } else { - // No more jobs to process, so unsubscribe the event - this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; - this.BackupQueue(string.Empty); // Fire the event to tell connected services. - this.OnQueueCompleted(new QueueCompletedEventArgs(false)); - } - } - - /// <summary> - /// For a given set of tasks, return the Queue JSON that can be used for the CLI. - /// </summary> - /// <param name="tasks"> - /// The tasks. - /// </param> - /// <param name="configuration"> - /// The configuration. - /// </param> - /// <returns> - /// The <see cref="string"/>. - /// </returns> + this.InvokeQueueCompleted(new QueueCompletedEventArgs(false)); + } + } + + private void ActiveJob_JobStatusUpdated(object sender, Encode.EventArgs.EncodeProgressEventArgs e) + { + this.OnQueueJobStatusChanged(); + } + + private void ActiveJob_JobFinished(object sender, ActiveJobCompletedEventArgs e) + { + this.activeJobs.Remove(e.Job); + this.OnEncodeCompleted(e.EncodeEventArgs); + + if (!this.IsPaused && this.IsProcessing) + { + this.ProcessNextJob(); + } + } + + private void InvokeQueueCompleted(QueueCompletedEventArgs e) + { + this.IsProcessing = false; + this.QueueCompleted?.Invoke(this, e); + } + + private void OnQueueJobStatusChanged() + { + // TODO add support for delayed notificaitons here to avoid overloading the UI when we run multiple encodes. + this.QueueJobStatusChanged?.Invoke(this, EventArgs.Empty); + } + + private void OnEncodeCompleted(EncodeCompletedEventArgs e) + { + this.EncodeCompleted?.Invoke(this, e); + } + + private void InvokeQueuePaused(EventArgs e) + { + this.IsProcessing = false; + + EventHandler handler = this.QueuePaused; + if (handler != null) + { + handler(this, e); + } + } + private string GetQueueJson(List<EncodeTask> tasks, HBConfiguration configuration) { JsonSerializerSettings settings = new JsonSerializerSettings @@ -628,5 +623,19 @@ namespace HandBrakeWPF.Services.Queue return JsonConvert.SerializeObject(queueJobs, Formatting.Indented, settings); } + + private bool CheckDiskSpace(QueueTask job) + { + if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(job.Task.Destination, this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel))) + { + this.logService.LogMessage(Resources.PauseOnLowDiskspace); + job.Status = QueueItemStatus.Waiting; + this.Pause(); + this.BackupQueue(string.Empty); + return true; // Don't start the next job. + } + + return false; + } } }
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/SystemService.cs b/win/CS/HandBrakeWPF/Services/SystemService.cs index d9fa46716..e07281b3b 100644 --- a/win/CS/HandBrakeWPF/Services/SystemService.cs +++ b/win/CS/HandBrakeWPF/Services/SystemService.cs @@ -13,7 +13,6 @@ namespace HandBrakeWPF.Services using System.Timers; using HandBrakeWPF.Properties; - using HandBrakeWPF.Services.Encode.Interfaces; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Logging.Interfaces; using HandBrakeWPF.Services.Queue.Interfaces; @@ -22,23 +21,20 @@ namespace HandBrakeWPF.Services public class SystemService : ISystemService { private readonly IUserSettingService userSettingService; - private readonly IEncode encodeService; - private readonly ILog log = null; private readonly IQueueService queueService; + private readonly ILog log; private Timer pollTimer; - private bool criticalStateHit = false; private bool lowStateHit = false; private bool lowPowerPause = false; private bool storageLowPause = false; - public SystemService(IUserSettingService userSettingService, IEncode encodeService, ILog logService, IQueueService queueService) + public SystemService(IUserSettingService userSettingService, ILog logService, IQueueService queueService) { this.log = logService; this.queueService = queueService; this.userSettingService = userSettingService; - this.encodeService = encodeService; } public void Start() @@ -64,8 +60,15 @@ namespace HandBrakeWPF.Services private void StorageCheck() { - string directory = this.encodeService.GetActiveJob()?.Destination; - if (!string.IsNullOrEmpty(directory) && this.encodeService.IsEncoding) + foreach (string directory in this.queueService.GetActiveJobDestinationDirectories()) + { + this.CheckDiskSpaceForDirectory(directory); + } + } + + private void CheckDiskSpaceForDirectory(string directory) + { + if (!string.IsNullOrEmpty(directory) && this.queueService.IsEncoding) { long lowLevel = this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel); if (!this.storageLowPause && this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(directory, lowLevel)) @@ -98,7 +101,7 @@ namespace HandBrakeWPF.Services if (state.ACLineStatus == Win32.ACLineStatus.Offline && state.BatteryLifePercent <= lowBatteryLevel && !this.lowStateHit) { - if (this.encodeService.IsEncoding && !this.encodeService.IsPasued) + if (this.queueService.IsEncoding && !this.queueService.IsPaused) { this.lowPowerPause = true; this.queueService.Pause(); @@ -113,14 +116,13 @@ namespace HandBrakeWPF.Services // Reset the flags when we start charging. if (state.ACLineStatus == Win32.ACLineStatus.Online) { - if (this.lowPowerPause && this.encodeService.IsPasued) + if (this.lowPowerPause && this.queueService.IsPaused) { this.queueService.Start(this.userSettingService.GetUserSetting<bool>(UserSettingConstants.ClearCompletedFromQueue)); this.ServiceLogMessage(string.Format(Resources.SystemService_ACMains, state.BatteryLifePercent)); } this.lowPowerPause = false; - this.criticalStateHit = false; this.lowStateHit = false; } } diff --git a/win/CS/HandBrakeWPF/Services/UserSettingService.cs b/win/CS/HandBrakeWPF/Services/UserSettingService.cs index 91ef1e5b7..095e6f819 100644 --- a/win/CS/HandBrakeWPF/Services/UserSettingService.cs +++ b/win/CS/HandBrakeWPF/Services/UserSettingService.cs @@ -306,6 +306,7 @@ namespace HandBrakeWPF.Services // Experimental
defaults.Add(UserSettingConstants.ProcessIsolationEnabled, true);
defaults.Add(UserSettingConstants.ProcessIsolationPort, 8037);
+ defaults.Add(UserSettingConstants.SimultaneousEncodes, 1);
// Misc
defaults.Add(UserSettingConstants.ShowPresetPanel, false);
|