diff options
Diffstat (limited to 'win/CS/HandBrakeWPF')
-rw-r--r-- | win/CS/HandBrakeWPF/Factories/HBConfigurationFactory.cs | 4 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/HandBrakeWPF.csproj | 7 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs | 78 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Instance/Model/ServerResponse.cs | 24 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Instance/RemoteInstance.cs | 188 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs | 13 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Services/Scan/LibScan.cs | 19 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/UserSettingConstants.cs | 10 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs | 1 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/defaultsettings.xml | 16 |
10 files changed, 326 insertions, 34 deletions
diff --git a/win/CS/HandBrakeWPF/Factories/HBConfigurationFactory.cs b/win/CS/HandBrakeWPF/Factories/HBConfigurationFactory.cs index 568f7e390..c4d199ef8 100644 --- a/win/CS/HandBrakeWPF/Factories/HBConfigurationFactory.cs +++ b/win/CS/HandBrakeWPF/Factories/HBConfigurationFactory.cs @@ -45,7 +45,9 @@ namespace HandBrakeWPF.Factories SaveLogToCopyDirectory = UserSettingService.GetUserSetting<bool>(UserSettingConstants.SaveLogToCopyDirectory),
SaveLogWithVideo = UserSettingService.GetUserSetting<bool>(UserSettingConstants.SaveLogWithVideo),
SaveLogCopyDirectory = UserSettingService.GetUserSetting<string>(UserSettingConstants.SaveLogCopyDirectory),
- };
+ RemoteServiceEnabled = UserSettingService.GetUserSetting<bool>(UserSettingConstants.RemoteServiceEnabled),
+ RemoteServicePort = UserSettingService.GetUserSetting<int>(UserSettingConstants.RemoteServicePort)
+ };
return config;
}
diff --git a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj index 1c1d4da42..bbd1fb88d 100644 --- a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj +++ b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj @@ -90,6 +90,7 @@ <Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
+ <Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@@ -153,6 +154,8 @@ <Compile Include="Helpers\MP4Helper.cs" />
<Compile Include="Helpers\TimeSpanHelper.cs" />
<Compile Include="Helpers\Validate.cs" />
+ <Compile Include="Instance\HandBrakeInstanceManager.cs" />
+ <Compile Include="Instance\Model\ServerResponse.cs" />
<Compile Include="Instance\RemoteInstance.cs" />
<Compile Include="Model\Audio\AudioBehaviourTrack.cs" />
<Compile Include="Model\Audio\AudioTrackDefaultsMode.cs" />
@@ -718,6 +721,10 @@ <Project>{087a2ba8-bac2-4577-a46f-07ff9d420016}</Project>
<Name>HandBrake.Interop</Name>
</ProjectReference>
+ <ProjectReference Include="..\HandBrake.Worker\HandBrake.Worker.csproj">
+ <Project>{f8370f37-b226-4830-aee7-6d7ae403e3d2}</Project>
+ <Name>HandBrake.Worker</Name>
+ </ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
diff --git a/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs b/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs new file mode 100644 index 000000000..3fcf00e8f --- /dev/null +++ b/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs @@ -0,0 +1,78 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="HandBrakeInstanceManager.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> +// The hand brake instance manager. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Instance +{ + using HandBrake.Interop.Interop; + using HandBrake.Interop.Interop.Interfaces; + using HandBrake.Interop.Model; + + /// <summary> + /// The HandBrake Instance manager. + /// Only supports scanning right now. + /// </summary> + public static class HandBrakeInstanceManager + { + private static IEncodeInstance encodeInstance; + + /// <summary> + /// Initializes static members of the <see cref="HandBrakeInstanceManager"/> class. + /// </summary> + static HandBrakeInstanceManager() + { + } + + /// <summary> + /// The init. + /// </summary> + public static void Init() + { + // Nothing to do. Triggers static constructor. + } + + /// <summary> + /// The get encode instance. + /// </summary> + /// <param name="verbosity"> + /// The verbosity. + /// </param> + /// <param name="configuration"> + /// The configuratio. + /// </param> + /// <returns> + /// The <see cref="IHandBrakeInstance"/>. + /// </returns> + public static IEncodeInstance GetEncodeInstance(int verbosity, HBConfiguration configuration) + { + if (encodeInstance != null) + { + encodeInstance.Dispose(); + encodeInstance = null; + } + + IEncodeInstance newInstance; + + if (configuration.RemoteServiceEnabled) + { + newInstance = new RemoteInstance(configuration.RemoteServicePort); + } + else + { + newInstance = new HandBrakeInstance(); + } + + newInstance.Initialize(verbosity); + encodeInstance = newInstance; + + HandBrakeUtils.SetDvdNav(!configuration.IsDvdNavDisabled); + + return encodeInstance; + } + } +} diff --git a/win/CS/HandBrakeWPF/Instance/Model/ServerResponse.cs b/win/CS/HandBrakeWPF/Instance/Model/ServerResponse.cs new file mode 100644 index 000000000..8b66a642c --- /dev/null +++ b/win/CS/HandBrakeWPF/Instance/Model/ServerResponse.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="ServerResponse.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> +// A model of the response from the HandBrake Worker instance. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Instance.Model +{ + public class ServerResponse + { + public ServerResponse(bool wasSuccessful, string jsonResponse) + { + this.WasSuccessful = wasSuccessful; + this.JsonResponse = jsonResponse; + } + + public bool WasSuccessful { get; private set; } + + public string JsonResponse { get; private set; } + } +} diff --git a/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs b/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs index bfefb6caf..375dd2db1 100644 --- a/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs +++ b/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs @@ -4,54 +4,111 @@ // </copyright> // <summary> // An Implementation of IEncodeInstance that works with a remote process rather than locally in-process. +// This class is effectivly just a shim. // </summary> // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.Instance { using System; + using System.Collections.Generic; using System.Diagnostics; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + using System.Timers; using HandBrake.Interop.Interop.EventArgs; using HandBrake.Interop.Interop.Interfaces; using HandBrake.Interop.Interop.Json.Encode; + using HandBrake.Interop.Interop.Json.State; - public class RemoteInstance : IEncodeInstance + using HandBrakeWPF.Instance.Model; + + using Newtonsoft.Json; + + /* + * TODO: + * 1. Add support for logging. + * 2. Code to detect what ports are in use / select one within range. + * 3. Add support for communciating via socket instead of HTTP. + */ + + public class RemoteInstance : IEncodeInstance, IDisposable { + private const double EncodePollIntervalMs = 500; + private readonly HttpClient client = new HttpClient(); + private readonly string serverUrl; + private readonly int port; private Process workerProcess; + private Timer encodePollTimer; + + public RemoteInstance(int port) + { + this.port = port; + this.serverUrl = "http://localhost/"; + } public event EventHandler<EncodeCompletedEventArgs> EncodeCompleted; public event EventHandler<EncodeProgressEventArgs> EncodeProgress; - public void PauseEncode() + public async void PauseEncode() { - throw new NotImplementedException(); + this.StopPollingProgres(); + await this.MakeHttpGetRequest("PauseEncode"); } - public void ResumeEncode() + public async void ResumeEncode() { - throw new NotImplementedException(); + await this.MakeHttpGetRequest("ResumeEncode"); + this.MonitorEncodeProgress(); } - public void StartEncode(JsonEncodeObject jobToStart) + public async void StartEncode(JsonEncodeObject jobToStart) { - throw new NotImplementedException(); + JsonSerializerSettings settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + string job = JsonConvert.SerializeObject(jobToStart, Formatting.Indented, settings); + + var values = new Dictionary<string, string> { { "jpb", job } }; + await this.MakeHttpPostRequest("StartEncode", values); + + this.MonitorEncodeProgress(); } - public void StopEncode() + public async void StopEncode() { - throw new NotImplementedException(); + this.StopPollingProgres(); + await this.MakeHttpGetRequest("StopEncode"); } - protected virtual void OnEncodeCompleted(EncodeCompletedEventArgs e) + public JsonState GetEncodeProgress() { - this.EncodeCompleted?.Invoke(this, e); + Task<ServerResponse> response = this.MakeHttpGetRequest("PollEncodeProgress"); + response.Wait(); + + if (!response.Result.WasSuccessful) + { + return null; + } + + string statusJson = response.Result?.JsonResponse; + + JsonState state = JsonConvert.DeserializeObject<JsonState>(statusJson); + return state; } - protected virtual void OnEncodeProgress(EncodeProgressEventArgs e) + public void Initialize(int verbosity) { - this.EncodeProgress?.Invoke(this, e); + this.StartServer(); + } + + public void Dispose() + { + this.client?.Dispose(); + this.workerProcess?.Dispose(); + this.StopEncode(); + this.StopServer(); } private void StartServer() @@ -59,19 +116,42 @@ namespace HandBrakeWPF.Instance if (this.workerProcess == null || this.workerProcess.HasExited) { this.workerProcess = new Process(); - - // TODO Take default port from preferences, then find a usable port thereafter. this.workerProcess.StartInfo = - new ProcessStartInfo("HandBrake.Worker.exe", "--port=8080") + new ProcessStartInfo("HandBrake.Worker.exe", string.Format("--port={0}", this.port)) { WindowStyle = ProcessWindowStyle.Normal }; this.workerProcess.Start(); this.workerProcess.Exited += this.WorkerProcess_Exited; + Debug.WriteLine("Worker Process Started. PID = {0}", this.workerProcess.Id); } } + private void MonitorEncodeProgress() + { + this.encodePollTimer = new Timer(); + this.encodePollTimer.Interval = EncodePollIntervalMs; + + this.encodePollTimer.Elapsed += (o, e) => + { + try + { + this.PollEncodeProgress(); + } + catch (Exception exc) + { + Debug.WriteLine(exc); + } + }; + this.encodePollTimer.Start(); + } + + private void StopPollingProgres() + { + this.encodePollTimer?.Stop(); + } + private void WorkerProcess_Exited(object sender, EventArgs e) { Debug.WriteLine("Worker Process has exited"); @@ -84,5 +164,81 @@ namespace HandBrakeWPF.Instance this.workerProcess.Kill(); } } + + private async void PollEncodeProgress() + { + ServerResponse response = await this.MakeHttpGetRequest("PollEncodeProgress"); + if (!response.WasSuccessful) + { + return; + } + + string statusJson = response.JsonResponse; + + JsonState state = JsonConvert.DeserializeObject<JsonState>(statusJson); + + TaskState taskState = state != null ? TaskState.FromRepositoryValue(state.State) : null; + + if (taskState != null && (taskState == TaskState.Working || taskState == TaskState.Muxing || taskState == TaskState.Searching)) + { + if (this.EncodeProgress != null) + { + var progressEventArgs = new EncodeProgressEventArgs( + fractionComplete: state.Working.Progress, + currentFrameRate: state.Working.Rate, + averageFrameRate: state.Working.RateAvg, + estimatedTimeLeft: new TimeSpan(state.Working.Hours, state.Working.Minutes, state.Working.Seconds), + passId: state.Working.PassID, + pass: state.Working.Pass, + passCount: state.Working.PassCount, + stateCode: taskState.Code); + + this.EncodeProgress(this, progressEventArgs); + } + } + else if (taskState != null && taskState == TaskState.WorkDone) + { + this.encodePollTimer.Stop(); + + this.EncodeCompleted?.Invoke(sender: this, e: new EncodeCompletedEventArgs(state.WorkDone.Error != 0)); + } + } + + private async Task<ServerResponse> MakeHttpPostRequest(string urlPath, Dictionary<string, string> postValues) + { + if (postValues == null || !postValues.Any()) + { + throw new InvalidOperationException("No Post Values Found."); + } + + if (postValues.Any()) + { + FormUrlEncodedContent content = new FormUrlEncodedContent(postValues); + HttpResponseMessage response = await this.client.PostAsync(this.serverUrl + urlPath, content); + if (response != null) + { + string returnContent = await response.Content.ReadAsStringAsync(); + ServerResponse serverResponse = new ServerResponse(response.IsSuccessStatusCode, returnContent); + + return serverResponse; + } + } + + return null; + } + + private async Task<ServerResponse> MakeHttpGetRequest(string urlPath) + { + HttpResponseMessage response = await this.client.GetAsync(this.serverUrl + urlPath); + if (response != null) + { + string returnContent = await response.Content.ReadAsStringAsync(); + ServerResponse serverResponse = new ServerResponse(response.IsSuccessStatusCode, returnContent); + + return serverResponse; + } + + return null; + } } } diff --git a/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs b/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs index 85c5a42b1..a00344836 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs @@ -13,7 +13,6 @@ namespace HandBrakeWPF.Services.Encode using System.Diagnostics; using System.IO; - using HandBrake.Interop.Interop; using HandBrake.Interop.Interop.EventArgs; using HandBrake.Interop.Interop.Interfaces; using HandBrake.Interop.Interop.Json.State; @@ -24,11 +23,12 @@ namespace HandBrakeWPF.Services.Encode using HandBrakeWPF.Services.Encode.Factories; using EncodeTask = Model.EncodeTask; + using HandBrakeInstanceManager = Instance.HandBrakeInstanceManager; using IEncode = Interfaces.IEncode; - using ILog = HandBrakeWPF.Services.Logging.Interfaces.ILog; - using LogLevel = HandBrakeWPF.Services.Logging.Model.LogLevel; - using LogMessageType = HandBrakeWPF.Services.Logging.Model.LogMessageType; - using LogService = HandBrakeWPF.Services.Logging.LogService; + using ILog = Logging.Interfaces.ILog; + using LogLevel = Logging.Model.LogLevel; + using LogMessageType = Logging.Model.LogMessageType; + using LogService = Logging.LogService; /// <summary> /// LibHB Implementation of IEncode @@ -80,8 +80,7 @@ namespace HandBrakeWPF.Services.Encode this.log.Reset(); // Reset so we have a clean log for the start of the encode. this.ServiceLogMessage("Starting Encode ..."); - HandBrakeUtils.SetDvdNav(!configuration.IsDvdNavDisabled); - this.instance = task.IsPreviewEncode ? HandBrakeInstanceManager.GetPreviewInstance(configuration.Verbosity) : HandBrakeInstanceManager.GetEncodeInstance(configuration.Verbosity); + this.instance = task.IsPreviewEncode ? HandBrake.Interop.Interop.HandBrakeInstanceManager.GetPreviewInstance(configuration.Verbosity, configuration) : HandBrakeInstanceManager.GetEncodeInstance(configuration.Verbosity, configuration); this.instance.EncodeCompleted += this.InstanceEncodeCompleted; this.instance.EncodeProgress += this.InstanceEncodeProgress; diff --git a/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs b/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs index 7bcc5531b..d17a5d3c9 100644 --- a/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs +++ b/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs @@ -30,14 +30,14 @@ namespace HandBrakeWPF.Services.Scan using HandBrakeWPF.Services.Scan.Model; using HandBrakeWPF.Utilities; - using Chapter = HandBrakeWPF.Services.Scan.Model.Chapter; - using ILog = HandBrakeWPF.Services.Logging.Interfaces.ILog; - using LogLevel = HandBrakeWPF.Services.Logging.Model.LogLevel; - using LogMessageType = HandBrakeWPF.Services.Logging.Model.LogMessageType; - using LogService = HandBrakeWPF.Services.Logging.LogService; + using Chapter = Model.Chapter; + using ILog = Logging.Interfaces.ILog; + using LogLevel = Logging.Model.LogLevel; + using LogMessageType = Logging.Model.LogMessageType; + using LogService = Logging.LogService; using ScanProgressEventArgs = HandBrake.Interop.Interop.EventArgs.ScanProgressEventArgs; - using Subtitle = HandBrakeWPF.Services.Scan.Model.Subtitle; - using Title = HandBrakeWPF.Services.Scan.Model.Title; + using Subtitle = Model.Subtitle; + using Title = Model.Title; /// <summary> /// Scan a Source @@ -129,7 +129,7 @@ namespace HandBrakeWPF.Services.Scan this.postScanOperation = postAction; // Create a new HandBrake Instance. - this.instance = HandBrakeInstanceManager.GetScanInstance(configuraiton.Verbosity); + this.instance = HandBrake.Interop.Interop.HandBrakeInstanceManager.GetScanInstance(configuraiton.Verbosity); this.instance.ScanProgress += this.InstanceScanProgress; this.instance.ScanCompleted += this.InstanceScanCompleted; @@ -266,8 +266,7 @@ namespace HandBrakeWPF.Services.Scan this.ServiceLogMessage("Starting Scan ..."); this.instance.StartScan(sourcePath.ToString(), previewCount, minDuration, title != 0 ? title : 0); - if (this.ScanStarted != null) - this.ScanStarted(this, System.EventArgs.Empty); + this.ScanStarted?.Invoke(this, System.EventArgs.Empty); } catch (Exception exc) { diff --git a/win/CS/HandBrakeWPF/UserSettingConstants.cs b/win/CS/HandBrakeWPF/UserSettingConstants.cs index 34d24d25d..9e4d37a17 100644 --- a/win/CS/HandBrakeWPF/UserSettingConstants.cs +++ b/win/CS/HandBrakeWPF/UserSettingConstants.cs @@ -256,6 +256,16 @@ namespace HandBrakeWPF /// </summary>
public static string WhenDoneAudioFile = "WhenDoneAudioFile";
+ /// <summary>
+ /// Setting to store whether we are using a Worker Process or in-process encoding.
+ /// </summary>
+ public static string RemoteServiceEnabled = "RemoteServiceEnabled";
+
+ /// <summary>
+ /// The port that the worker process is running on.
+ /// </summary>
+ public static string RemoteServicePort = "RemoteServicePort";
+
#endregion
}
}
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs index 70fc5250e..9548fb573 100644 --- a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs @@ -56,6 +56,7 @@ namespace HandBrakeWPF.ViewModels using DataFormats = System.Windows.DataFormats;
using DragEventArgs = System.Windows.DragEventArgs;
using Execute = Caliburn.Micro.Execute;
+ using HandBrakeInstanceManager = HandBrakeWPF.Instance.HandBrakeInstanceManager;
using LogManager = HandBrakeWPF.Helpers.LogManager;
using MessageBox = System.Windows.MessageBox;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
diff --git a/win/CS/HandBrakeWPF/defaultsettings.xml b/win/CS/HandBrakeWPF/defaultsettings.xml index c48921fff..853cf5396 100644 --- a/win/CS/HandBrakeWPF/defaultsettings.xml +++ b/win/CS/HandBrakeWPF/defaultsettings.xml @@ -520,4 +520,20 @@ <anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d4p1:type="q1:string" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance">Choose a File:</anyType>
</value>
</item>
+ <item>
+ <key>
+ <string>RemoteServiceEnabled</string>
+ </key>
+ <value>
+ <anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d4p1:type="q1:boolean" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance">false</anyType>
+ </value>
+ </item>
+ <item>
+ <key>
+ <string>RemoteServicePort</string>
+ </key>
+ <value>
+ <anyType xmlns:q1="http://www.w3.org/2001/XMLSchema" d4p1:type="q1:int" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance">8080</anyType>
+ </value>
+ </item>
</dictionary>
\ No newline at end of file |