Skip to content

Commit

Permalink
Enabled auto-pause on low disk space as a default setting with a thre…
Browse files Browse the repository at this point in the history
…shold of 1GB. This prevents you from running out of space which can cause other system problems and result in a confusing error message.

Added status messages for auto-pause and resume.
Made status messages wait until the user is active to display.
  • Loading branch information
RandomEngy committed Feb 16, 2021
1 parent 7cdb3eb commit f2f5ec8
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 18 deletions.
10 changes: 10 additions & 0 deletions VidCoder/App.xaml.cs
Expand Up @@ -174,6 +174,16 @@ protected override void OnStartup(StartupEventArgs e)
{
AutomationHost.StartListening();
}

ActivityService activityService = StaticResolver.Resolve<ActivityService>();
this.Activated += (object sender, EventArgs e2) =>
{
activityService.ReportActivated();
};
this.Deactivated += (object sender, EventArgs e2) =>
{
activityService.ReportDeactivated();
};

base.OnStartup(e);
}
Expand Down
4 changes: 2 additions & 2 deletions VidCoder/Model/Config/Config.cs
Expand Up @@ -87,8 +87,8 @@ private static void Initialize(SQLiteConnection connection)
cache.Add("CopyLogToCustomFolder", DatabaseConfig.Get("CopyLogToCustomFolder", false, connection));
cache.Add("LogCustomFolder", DatabaseConfig.Get("LogCustomFolder", "", connection));
cache.Add("AutoPauseLowBattery", DatabaseConfig.Get("AutoPauseLowBattery", true, connection));
cache.Add("AutoPauseLowDiskSpace", DatabaseConfig.Get("AutoPauseLowDiskSpace", false, connection));
cache.Add("AutoPauseLowDiskSpaceGb", DatabaseConfig.Get("AutoPauseLowDiskSpaceGb", 10, connection));
cache.Add("AutoPauseLowDiskSpace", DatabaseConfig.Get("AutoPauseLowDiskSpace", true, connection));
cache.Add("AutoPauseLowDiskSpaceGb", DatabaseConfig.Get("AutoPauseLowDiskSpaceGb", 1, connection));
cache.Add("AutoPauseProcesses", DatabaseConfig.Get("AutoPauseProcesses", "", connection));
cache.Add("MaxSimultaneousEncodes", DatabaseConfig.Get("MaxSimultaneousEncodes", 1, connection));
cache.Add("PreviewCount", DatabaseConfig.Get("PreviewCount", 10, connection));
Expand Down
4 changes: 2 additions & 2 deletions VidCoder/Model/Config/Config.txt
Expand Up @@ -70,8 +70,8 @@ CopyLogToOutputFolder|bool|false
CopyLogToCustomFolder|bool|false
LogCustomFolder|string|""
AutoPauseLowBattery|bool|true
AutoPauseLowDiskSpace|bool|false
AutoPauseLowDiskSpaceGb|int|10
AutoPauseLowDiskSpace|bool|true
AutoPauseLowDiskSpaceGb|int|1
AutoPauseProcesses|string|""
MaxSimultaneousEncodes|int|1
PreviewCount|int|10
Expand Down
54 changes: 54 additions & 0 deletions VidCoder/Resources/MainRes.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions VidCoder/Resources/MainRes.resx
Expand Up @@ -712,4 +712,24 @@
<value>Unmute</value>
<comment>Text for screen readers for the unmute button on video playback</comment>
</data>
<data name="AutoPauseLowBattery" xml:space="preserve">
<value>Automatically pausing due to low battery.</value>
</data>
<data name="AutoPauseLowDiskSpace" xml:space="preserve">
<value>Automatically pausing due to free disk space below {0}GB.</value>
<comment>{0} is the disk space threshold for auto-pause in GB</comment>
</data>
<data name="AutoPauseProcessDetected" xml:space="preserve">
<value>Automatically pausing, process detected: {0}</value>
<comment>{0} is the name of the process we were waiting for to pause</comment>
</data>
<data name="AutoPauseResumeBattery" xml:space="preserve">
<value>Automatically resuming, connected to AC power.</value>
</data>
<data name="AutoPauseResumeDiskSpace" xml:space="preserve">
<value>Automatically resuming, disk has free space.</value>
</data>
<data name="AutoPauseResumeProcess" xml:space="preserve">
<value>Automatically resuming, processes are gone.</value>
</data>
</root>
67 changes: 67 additions & 0 deletions VidCoder/Services/ActivityService.cs
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VidCoder.Services
{
/// <summary>
/// Tracks if the user is active on the application
/// </summary>
public class ActivityService
{
private bool active = true;

private TaskCompletionSource<object> activeTcs;

/// <summary>
/// Waits for the program to become active.
/// </summary>
public Task WaitForActiveAsync()
{
bool hasInputRecently = SystemInputTracker.GetTimeSinceLastInput() < TimeSpan.FromSeconds(30);
if (active && hasInputRecently)
{
return Task.CompletedTask;
}

if (!active)
{
// If the app is not active, we just need to wait until we're reactivated.
if (activeTcs == null)
{
activeTcs = new TaskCompletionSource<object>();
}

return activeTcs.Task;
}

// The app is active, but we have not seen user input in a while. We need to poll for it.
return this.WaitForInput();
}

public void ReportActivated()
{
active = true;
this.activeTcs?.TrySetResult(null);
}

public void ReportDeactivated()
{
active = false;
}

private async Task WaitForInput()
{
while (true)
{
await Task.Delay(2000);
if (SystemInputTracker.GetTimeSinceLastInput() < TimeSpan.FromSeconds(30))
{
return;
}
}
}
}
}
27 changes: 16 additions & 11 deletions VidCoder/Services/AutoPause.cs
Expand Up @@ -8,16 +8,17 @@
using Microsoft.AnyContainer;
using VidCoder.ViewModel;
using System.IO;
using VidCoder.Resources;

namespace VidCoder.Services
{
public class AutoPause : IAutoPause
{
private const double ProcessPollIntervalMsec = 5000;

private IProcesses processes;
private IAppLogger logger;

private readonly IProcesses processes;
private readonly IAppLogger logger;
private readonly StatusService statusService;
private HashSet<string> startingProcesses;

private Timer pollTimer;
Expand All @@ -43,10 +44,11 @@ private enum AutoPauseReason
ProcessPresent
}

public AutoPause(IProcesses processes, IAppLogger logger)
public AutoPause(IProcesses processes, IAppLogger logger, StatusService statusService)
{
this.processes = processes;
this.logger = logger;
this.statusService = statusService;
}

public event EventHandler PauseEncoding;
Expand Down Expand Up @@ -145,23 +147,25 @@ private void PollTimerElapsed(object sender, ElapsedEventArgs e)
// Encode is running normally. See if we need to auto-pause
if (shouldBePausedForLowBattery)
{
this.InvokeAutoPause("Automatically pausing due to low battery.", AutoPauseReason.LowBattery);
this.InvokeAutoPause(MainRes.AutoPauseLowBattery, AutoPauseReason.LowBattery);
}
else if (shouldBePausedForLowDiskSpace)
{
this.InvokeAutoPause($"Automatically pausing due to free disk space below {Config.AutoPauseLowDiskSpaceGb}GB", AutoPauseReason.LowDiskSpace);
this.InvokeAutoPause(string.Format(MainRes.AutoPauseLowDiskSpace, Config.AutoPauseLowDiskSpaceGb), AutoPauseReason.LowDiskSpace);
}
else if (autoPauseProcess != null)
{
this.InvokeAutoPause("Automatically pausing, process detected: " + autoPauseProcess, AutoPauseReason.ProcessPresent);
this.InvokeAutoPause(string.Format(MainRes.AutoPauseProcessDetected, autoPauseProcess), AutoPauseReason.ProcessPresent);
}
}
else if (this.state == AutoPauseState.AutoPaused)
{
if (!shouldBePausedForLowBattery && !shouldBePausedForLowDiskSpace && autoPauseProcess == null)
{
this.state = AutoPauseState.EncodeRunningNormally;
this.logger.Log(GetResumeReasonMessage(this.autoPauseReason));
string resumeMessage = GetResumeReasonMessage(this.autoPauseReason);
this.logger.Log(resumeMessage);
this.statusService.Show(resumeMessage);
SystemSleepManagement.PreventSleep();
this.ResumeEncoding?.Invoke(this, EventArgs.Empty);
}
Expand Down Expand Up @@ -250,6 +254,7 @@ private void InvokeAutoPause(string message, AutoPauseReason reason)
this.state = AutoPauseState.AutoPaused;
this.autoPauseReason = reason;
this.logger.Log(message);
this.statusService.Show(message);
this.PauseEncoding?.Invoke(this, EventArgs.Empty);
}

Expand All @@ -258,11 +263,11 @@ private static string GetResumeReasonMessage(AutoPauseReason pauseReason)
switch (pauseReason)
{
case AutoPauseReason.LowBattery:
return "Automatically resuming, connected to AC power.";
return MainRes.AutoPauseResumeBattery;
case AutoPauseReason.LowDiskSpace:
return "Automatically resuming, disk has free space.";
return MainRes.AutoPauseResumeDiskSpace;
case AutoPauseReason.ProcessPresent:
return "Automatically resuming, processes are gone.";
return MainRes.AutoPauseResumeProcess;
default:
return string.Empty;
}
Expand Down
11 changes: 10 additions & 1 deletion VidCoder/Services/StatusService.cs
Expand Up @@ -4,10 +4,19 @@ namespace VidCoder.Services
{
public class StatusService
{
private readonly ActivityService activityService;

public event EventHandler<EventArgs<string>> MessageShown;

public void Show(string message)
public StatusService(ActivityService activityService)
{
this.activityService = activityService;
}

public async void Show(string message)
{
await activityService.WaitForActiveAsync();

this.MessageShown?.Invoke(this, new EventArgs<string>(message));
}
}
Expand Down
1 change: 1 addition & 0 deletions VidCoder/Utilities/Ioc.cs
Expand Up @@ -68,6 +68,7 @@ public static void SetUp()
container.RegisterSingleton<LogCoordinator>();
container.RegisterSingleton<AllAppLogger>();
container.RegisterSingleton<PastebinService>();
container.RegisterSingleton<ActivityService>();

container.RegisterSingleton<EncodingWindowViewModel>();

Expand Down
38 changes: 38 additions & 0 deletions VidCoder/Utilities/SystemInputTracker.cs
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace VidCoder
{
public static class SystemInputTracker
{
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

public static TimeSpan GetTimeSinceLastInput()
{
int systemUptime = Environment.TickCount,
idleTicks = 0;

LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;

if (GetLastInputInfo(ref lastInputInfo))
{
idleTicks = systemUptime - (int)lastInputInfo.dwTime;
}

return new TimeSpan(0, 0, 0, 0, idleTicks);
}

private struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
}
}
13 changes: 11 additions & 2 deletions VidCoder/View/Main.xaml.cs
Expand Up @@ -206,21 +206,30 @@ public Main()
};
debugDropDown.Items.Add(addTenLogItems);

var addLongLogItem = new Fluent.MenuItem {Header = "Add long log item"};
var addLongLogItem = new Fluent.MenuItem { Header = "Add long log item" };
addLongLogItem.Click += (sender, args) =>
{
StaticResolver.Resolve<IAppLogger>().Log("This is a log item\r\nthat is split into multiple lines\r\nOh yes indeed");
};
debugDropDown.Items.Add(addLongLogItem);

var doAnActionItem = new Fluent.MenuItem {Header = "Perform action"};
var doAnActionItem = new Fluent.MenuItem { Header = "Perform action" };
doAnActionItem.Click += (sender, args) =>
{
var app = (App)System.Windows.Application.Current;
app.ChangeTheme(new Uri("/Themes/Dark.xaml", UriKind.Relative));
};
debugDropDown.Items.Add(doAnActionItem);


var showDelayedStatusItem = new Fluent.MenuItem { Header = "Show a status message in 40 seconds" };
showDelayedStatusItem.Click += async (sender, args) =>
{
await Task.Delay(TimeSpan.FromSeconds(40));
StaticResolver.Resolve<StatusService>().Show("Some delayed message");
};
debugDropDown.Items.Add(showDelayedStatusItem);

this.toolsRibbonGroupBox.Items.Add(debugDropDown);
#endif

Expand Down

0 comments on commit f2f5ec8

Please sign in to comment.