- Automatic pause on "Low" or "Critical" battery alarms. The % level is set in Windows power settings. Automatic Resume when AC returns, if it was paused by an alarm.
- Automatic encode pause when destination drive drops below 2GB. (May make this a preference set later)
- Behaviour of pause queue on low disk space with a user defined level in preferences is unchanged.
#2109 #2181
<Compile Include="Services\Encode\Model\Models\Video\VideoProfile.cs" />\r
<Compile Include="Services\Encode\Model\Models\Video\VideoTune.cs" />\r
<Compile Include="Services\Interfaces\INotifyIconService.cs" />\r
+ <Compile Include="Services\Interfaces\ISystemService.cs" />\r
<Compile Include="Services\Logging\EventArgs\LogEventArgs.cs" />\r
<Compile Include="Services\Logging\Interfaces\ILog.cs" />\r
<Compile Include="Services\Logging\LogService.cs" />\r
<Compile Include="Services\Scan\Model\Subtitle.cs" />\r
<Compile Include="Services\Scan\Model\Title.cs" />\r
<Compile Include="Services\NotifyIconService.cs" />\r
+ <Compile Include="Services\SystemService.cs" />\r
<Compile Include="Services\UserSettingService.cs" />\r
<Compile Include="Startup\StartupOptions.cs" />\r
<Compile Include="Utilities\AppcastReader.cs" />\r
}
}
+ /// <summary>
+ /// Looks up a localized string similar to AC Mains power detected. Resuming encode... ({0} %).
+ /// </summary>
+ public static string SystemService_ACMains {
+ get {
+ return ResourceManager.GetString("SystemService_ACMains", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to System Battery Critical! ({0} %).
+ /// </summary>
+ public static string SystemService_CriticalBattery {
+ get {
+ return ResourceManager.GetString("SystemService_CriticalBattery", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to System Battery Low! ({0} %). Your encode has been paused to protect the system. System sleep is set to allowed!.
+ /// </summary>
+ public static string SystemService_LowBatteryLog {
+ get {
+ return ResourceManager.GetString("SystemService_LowBatteryLog", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Remaining drive storage has dropped below {0} GB on the destination drive. Pausing encode....
+ /// </summary>
+ public static string SystemService_LowDiskSpaceLog {
+ get {
+ return ResourceManager.GetString("SystemService_LowDiskSpaceLog", resourceCulture);
+ }
+ }
+
/// <summary>
/// Looks up a localized string similar to {1}%, Pass {2} of {3}
///Remaining Time: {4}.
<data name="Options_LowDiskspaceSizeGB" xml:space="preserve">\r
<value>GB</value>\r
</data>\r
+ <data name="SystemService_ACMains" xml:space="preserve">\r
+ <value>AC Mains power detected. Resuming encode... ({0} %)</value>\r
+ </data>\r
+ <data name="SystemService_CriticalBattery" xml:space="preserve">\r
+ <value>System Battery Critical! ({0} %)</value>\r
+ </data>\r
+ <data name="SystemService_LowBatteryLog" xml:space="preserve">\r
+ <value>System Battery Low! ({0} %). Your encode has been paused to protect the system. System sleep is set to allowed!</value>\r
+ </data>\r
+ <data name="SystemService_LowDiskSpaceLog" xml:space="preserve">\r
+ <value>Remaining drive storage has dropped below {0} GB on the destination drive. Pausing encode...</value>\r
+ </data>\r
</root>
\ No newline at end of file
/// Kill the process
/// </summary>
void Stop();
+
+
+ /// <summary>
+ /// Get a copy of the Active job
+ /// </summary>
+ EncodeTask GetActiveJob();
}
}
\ No newline at end of file
}
}
+ public EncodeTask GetActiveJob()
+ {
+ if (this.currentTask != null)
+ {
+ EncodeTask task = new EncodeTask(this.currentTask); // Decouple our current copy.
+ return task;
+ }
+
+ return null;
+ }
+
#region HandBrakeInstance Event Handlers.
/// <summary>
--- /dev/null
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="ISystemService.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
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Interfaces
+{
+ public interface ISystemService
+ {
+ void Start();
+ }
+}
\ No newline at end of file
QueueTask job = this.GetNextJobForProcessing();
if (job != null)
{
- if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(job.Task.Destination, this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseOnLowDiskspaceLevel)))
+ if (this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(job.Task.Destination, this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))
{
LogService.GetLogger().LogMessage(Resources.PauseOnLowDiskspace, LogMessageType.ScanOrEncode, LogLevel.Info);
job.Status = QueueItemStatus.Waiting;
--- /dev/null
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="SystemService.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>
+// Monitor the system health for common problems that will directly impact encodes.
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services
+{
+ using System;
+ using System.Runtime.CompilerServices;
+ using System.Timers;
+
+ using HandBrakeWPF.Properties;
+ using HandBrakeWPF.Services.Encode.Interfaces;
+ using HandBrakeWPF.Services.Encode.Model;
+ using HandBrakeWPF.Services.Interfaces;
+ using HandBrakeWPF.Services.Logging;
+ using HandBrakeWPF.Services.Logging.Interfaces;
+ using HandBrakeWPF.Services.Logging.Model;
+ using HandBrakeWPF.Utilities;
+
+ using Ookii.Dialogs.Wpf;
+
+ public class SystemService : ISystemService
+ {
+ private readonly IUserSettingService userSettingService;
+ private readonly IEncode encodeService;
+ private readonly ILog log = LogService.GetLogger();
+ 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)
+ {
+ this.userSettingService = userSettingService;
+ this.encodeService = encodeService;
+ }
+
+ public void Start()
+ {
+ if (this.pollTimer == null)
+ {
+ this.pollTimer = new Timer();
+ this.pollTimer.Interval = 10000; // Check every 10 seconds.
+ this.pollTimer.Elapsed += (o, e) =>
+ {
+ this.CheckSystem();
+ };
+
+ this.pollTimer.Start();
+ }
+ }
+
+ private void CheckSystem()
+ {
+ this.PowerCheck();
+ this.StorageCheck();
+ }
+
+ private void StorageCheck()
+ {
+ string directory = this.encodeService.GetActiveJob()?.Destination;
+ if (!string.IsNullOrEmpty(directory) && this.encodeService.IsEncoding)
+ {
+ long lowLevel = this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseEncodeOnLowDiskspaceLevel);
+ if (!this.storageLowPause && this.userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(directory, lowLevel))
+ {
+ LogService.GetLogger().LogMessage(
+ string.Format(
+ Resources.SystemService_LowDiskSpaceLog,
+ lowLevel / 1000 / 1000 / 1000),
+ LogMessageType.Application,
+ LogLevel.Info);
+ this.encodeService.Pause();
+ this.storageLowPause = true;
+ }
+ }
+ }
+
+ private void PowerCheck()
+ {
+ Win32.PowerState state = Win32.PowerState.GetPowerState();
+
+ if (state == null || state.BatteryFlag == Win32.BatteryFlag.NoSystemBattery || state.BatteryFlag == Win32.BatteryFlag.Unknown)
+ {
+ return; // Only run if we have a battery.
+ }
+
+ if (state.ACLineStatus == Win32.ACLineStatus.Offline && state.BatteryFlag == Win32.BatteryFlag.Low && !this.lowStateHit)
+ {
+ if (this.encodeService.IsEncoding && !this.encodeService.IsPasued)
+ {
+ this.lowPowerPause = true;
+ this.encodeService.Pause();
+ }
+
+ Win32.AllowSleep();
+
+ this.ServiceLogMessage(string.Format(Resources.SystemService_LowBatteryLog, state.BatteryLifePercent));
+ this.lowStateHit = true;
+ }
+
+ if (state.ACLineStatus == Win32.ACLineStatus.Offline && state.BatteryFlag == Win32.BatteryFlag.Critical && !this.criticalStateHit)
+ {
+ if (this.encodeService.IsEncoding && !this.encodeService.IsPasued)
+ {
+ this.lowPowerPause = true;
+ this.encodeService.Pause(); // In case we missed the low state!
+ }
+
+ Win32.AllowSleep();
+
+ this.ServiceLogMessage(string.Format(Resources.SystemService_CriticalBattery, state.BatteryLifePercent));
+ this.criticalStateHit = true;
+ }
+
+ // Reset the flags when we start charging.
+ if (state.ACLineStatus == Win32.ACLineStatus.Online && state.BatteryFlag >= Win32.BatteryFlag.Low)
+ {
+ if (this.lowPowerPause && this.encodeService.IsPasued)
+ {
+ this.encodeService.Resume();
+ this.ServiceLogMessage(string.Format(Resources.SystemService_ACMains, state.BatteryLifePercent));
+ }
+
+ this.lowPowerPause = false;
+ this.criticalStateHit = false;
+ this.lowStateHit = false;
+ }
+ }
+
+ private void ServiceLogMessage(string message)
+ {
+ this.log.LogMessage(string.Format("{0}# {1}{0}", Environment.NewLine, message), LogMessageType.Application, LogLevel.Info);
+ }
+ }
+}
this.container.Singleton<ICountdownAlertViewModel, CountdownAlertViewModel>();\r
this.container.Singleton<IMiniViewModel, MiniViewModel>();\r
this.container.Singleton<IStaticPreviewViewModel, StaticPreviewViewModel>();\r
-\r
+ this.container.Singleton<ISystemService, SystemService>();\r
+ \r
// Tab Components\r
this.container.Singleton<IAudioViewModel, AudioViewModel>();\r
this.container.Singleton<IPictureSettingsViewModel, PictureSettingsViewModel>();\r
public const string SendFileToArgs = "SendFileToArgs";\r
public const string PreventSleep = "PreventSleep";\r
public const string PauseOnLowDiskspace = "PauseOnLowDiskspace";\r
- public const string PauseOnLowDiskspaceLevel = "LowDiskSpaceWarningLevelInBytes";\r
+ public const string PauseQueueOnLowDiskspaceLevel = "LowDiskSpaceWarningLevelInBytes";\r
+ public const string PauseEncodeOnLowDiskspaceLevel = "LowDiskSpaceEncodePauseLevelInBytes";\r
public const string RemovePunctuation = "RemovePunctuation";\r
public const string ShowPresetPanel = "ShowPresetPanelOption";\r
public const string ResetWhenDoneAction = "ResetWhenDoneAction";\r
{
executor = marshaller;
}
+
+ [StructLayout(LayoutKind.Sequential)]
+ public class PowerState
+ {
+ public ACLineStatus ACLineStatus;
+ public BatteryFlag BatteryFlag;
+ public Byte BatteryLifePercent;
+ public Byte SystemStatusFlag;
+ public Int32 BatteryLifeTime;
+ public Int32 BatteryFullLifeTime;
+
+ public static PowerState GetPowerState()
+ {
+ PowerState state = new PowerState();
+ if (GetSystemPowerStatusRef(state))
+ {
+ return state;
+ }
+
+ return null;
+ }
+
+ [DllImport("Kernel32", EntryPoint = "GetSystemPowerStatus")]
+ private static extern bool GetSystemPowerStatusRef(PowerState sps);
+ }
+
+ public enum ACLineStatus : byte
+ {
+ Offline = 0,
+ Online = 1,
+ Unknown = 255
+ }
+
+ public enum BatteryFlag : byte
+ {
+ High = 1,
+ Low = 2,
+ Critical = 4,
+ Charging = 8,
+ NoSystemBattery = 128,
+ Unknown = 255
+ }
}
}
+
/// The viewmodel for HandBrakes main window.\r
/// </summary>\r
/// <remarks>whenDoneService must be a serivce here!</remarks>\r
- public MainViewModel(IUserSettingService userSettingService, IScan scanService, IPresetService presetService, \r
- IErrorService errorService, IUpdateService updateService, \r
- IPrePostActionService whenDoneService, IWindowManager windowManager, IPictureSettingsViewModel pictureSettingsViewModel, IVideoViewModel videoViewModel, ISummaryViewModel summaryViewModel,\r
- IFiltersViewModel filtersViewModel, IAudioViewModel audioViewModel, ISubtitlesViewModel subtitlesViewModel,\r
- IChaptersViewModel chaptersViewModel, IStaticPreviewViewModel staticPreviewViewModel,\r
- IQueueViewModel queueViewModel, IMetaDataViewModel metaDataViewModel, INotifyIconService notifyIconService)\r
+ public MainViewModel(\r
+ IUserSettingService userSettingService,\r
+ IScan scanService,\r
+ IPresetService presetService,\r
+ IErrorService errorService,\r
+ IUpdateService updateService,\r
+ IPrePostActionService whenDoneService,\r
+ IWindowManager windowManager,\r
+ IPictureSettingsViewModel pictureSettingsViewModel,\r
+ IVideoViewModel videoViewModel,\r
+ ISummaryViewModel summaryViewModel,\r
+ IFiltersViewModel filtersViewModel,\r
+ IAudioViewModel audioViewModel,\r
+ ISubtitlesViewModel subtitlesViewModel,\r
+ IChaptersViewModel chaptersViewModel,\r
+ IStaticPreviewViewModel staticPreviewViewModel,\r
+ IQueueViewModel queueViewModel,\r
+ IMetaDataViewModel metaDataViewModel,\r
+ INotifyIconService notifyIconService,\r
+ ISystemService systemService)\r
: base(userSettingService)\r
{\r
this.scanService = scanService;\r
// Setup Commands\r
this.QueueCommand = new QueueCommands(this.QueueViewModel);\r
\r
+ // Monitor the system.\r
+ systemService.Start();\r
}\r
\r
#region View Model Properties\r
\r
if (!DriveUtilities.HasMinimumDiskSpace(\r
this.Destination,\r
- this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseOnLowDiskspaceLevel)))\r
+ this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))\r
{\r
return new AddQueueError(Resources.Main_LowDiskspace, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);\r
}\r
\r
this.PreventSleep = userSettingService.GetUserSetting<bool>(UserSettingConstants.PreventSleep);\r
this.PauseOnLowDiskspace = userSettingService.GetUserSetting<bool>(UserSettingConstants.PauseOnLowDiskspace);\r
- this.PauseOnLowDiskspaceLevel = this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseOnLowDiskspaceLevel);\r
+ this.PauseOnLowDiskspaceLevel = this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel);\r
\r
// Log Verbosity Level\r
this.logVerbosityOptions.Clear();\r
this.userSettingService.SetUserSetting(UserSettingConstants.ProcessPriority, this.SelectedPriority);\r
this.userSettingService.SetUserSetting(UserSettingConstants.PreventSleep, this.PreventSleep);\r
this.userSettingService.SetUserSetting(UserSettingConstants.PauseOnLowDiskspace, this.PauseOnLowDiskspace);\r
- this.userSettingService.SetUserSetting(UserSettingConstants.PauseOnLowDiskspaceLevel, this.PauseOnLowDiskspaceLevel);\r
+ this.userSettingService.SetUserSetting(UserSettingConstants.PauseQueueOnLowDiskspaceLevel, this.PauseOnLowDiskspaceLevel);\r
this.userSettingService.SetUserSetting(UserSettingConstants.Verbosity, this.SelectedVerbosity);\r
this.userSettingService.SetUserSetting(UserSettingConstants.SaveLogWithVideo, this.CopyLogToEncodeDirectory);\r
this.userSettingService.SetUserSetting(UserSettingConstants.SaveLogToCopyDirectory, this.CopyLogToSepcficedLocation);\r
\r
var firstOrDefault = this.QueueTasks.FirstOrDefault(s => s.Status == QueueItemStatus.Waiting);\r
if (firstOrDefault != null && !DriveUtilities.HasMinimumDiskSpace(firstOrDefault.Task.Destination,\r
- this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseOnLowDiskspaceLevel)))\r
+ this.userSettingService.GetUserSetting<long>(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))\r
{\r
this.errorService.ShowMessageBox(Resources.Main_LowDiskspace, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);\r
return;\r
<anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d4p1:type="q1:long" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance">10000000000</anyType>\r
</value>\r
</item>\r
+ <item>\r
+ <key>\r
+ <string>LowDiskSpaceEncodePauseLevelInBytes</string>\r
+ </key>\r
+ <value>\r
+ <anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d4p1:type="q1:long" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance">2000000000</anyType>\r
+ </value>\r
+ </item>\r
<item>\r
<key>\r
<string>ForcePresetReset</string>\r