diff options
author | sr55 <[email protected]> | 2019-12-27 23:37:32 +0000 |
---|---|---|
committer | sr55 <[email protected]> | 2019-12-27 23:37:32 +0000 |
commit | 0ca1b03b3d3801b3db8661fd26e461ad2ca2e05f (patch) | |
tree | 95507b688d499c40a04e90a93d70344a9fbfebf9 /win/CS | |
parent | c8e4c8c34e97ce682a7e55a0f47e044d0d403ab7 (diff) |
WinGui: Building out the Web API needed for the background worker process. Includes initial support for encoding, logging and basic utility commands.
Diffstat (limited to 'win/CS')
-rw-r--r-- | win/CS/HandBrake.Worker/ApiRouter.cs | 68 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/App.config | 2 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/HandBrake.Worker.csproj | 16 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/HttpServer.cs | 4 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Logging/Interfaces/ILogHandler.cs | 43 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Logging/LogHandler.cs | 276 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Logging/Models/LogHandlerConfig.cs | 34 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Logging/Models/LogMessage.cs | 24 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Program.cs | 60 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/Settings.StyleCop | 17 | ||||
-rw-r--r-- | win/CS/HandBrake.Worker/handbrakepineapple.ico | bin | 0 -> 119514 bytes |
11 files changed, 515 insertions, 29 deletions
diff --git a/win/CS/HandBrake.Worker/ApiRouter.cs b/win/CS/HandBrake.Worker/ApiRouter.cs index 8d1869465..5fc18bd22 100644 --- a/win/CS/HandBrake.Worker/ApiRouter.cs +++ b/win/CS/HandBrake.Worker/ApiRouter.cs @@ -10,12 +10,17 @@ namespace HandBrake.Worker { + using System; using System.IO; using System.Net; + using System.Runtime.InteropServices; using HandBrake.Interop.Interop; using HandBrake.Interop.Interop.Json.State; using HandBrake.Interop.Utilities; + using HandBrake.Worker.Logging; + using HandBrake.Worker.Logging.Interfaces; + using HandBrake.Worker.Logging.Models; using Newtonsoft.Json; @@ -23,16 +28,21 @@ namespace HandBrake.Worker { private readonly JsonSerializerSettings jsonNetSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; private HandBrakeInstance handbrakeInstance; + private ILogHandler logHandler; - public string Initialise(HttpListenerRequest request) + public string Initialise(int verbosity) { if (this.handbrakeInstance == null) { this.handbrakeInstance = new HandBrakeInstance(); } - // TODO support verbosity - this.handbrakeInstance.Initialize(1, true); // TODO enable user setting support for nohardware + if (this.logHandler == null) + { + this.logHandler = new LogHandler(); + } + + this.handbrakeInstance.Initialize(verbosity, true); return null; } @@ -48,6 +58,7 @@ namespace HandBrake.Worker { string requestPostData = GetRequestPostData(request); + Console.WriteLine(requestPostData); this.handbrakeInstance.StartEncode(requestPostData); return null; @@ -92,6 +103,57 @@ namespace HandBrake.Worker return null; } + + /* Logging API */ + + // POST - JSON + public string ConfigureLogging(HttpListenerRequest request) + { + string requestPostData = GetRequestPostData(request); + + JsonSerializerSettings settings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + }; + LogHandlerConfig config; + if (!string.IsNullOrEmpty(requestPostData)) + { + config = JsonConvert.DeserializeObject<LogHandlerConfig>(requestPostData, settings); + } + else + { + config = new LogHandlerConfig(false, null, false, "Logging Not Configured!"); + } + + this.logHandler.ConfigureLogging(config); + return null; + } + + // GET + public string GetFullLog(HttpListenerRequest request) + { + return this.logHandler.GetFullLog(); + } + + // POST + public string GetLogFromIndex(HttpListenerRequest request) + { + string requestPostData = GetRequestPostData(request); + + if (int.TryParse(requestPostData, out int index)) + { + return this.logHandler.GetLogFromIndex(index); + } + + return null; + } + + // POST + public string GetLatestLogIndex(HttpListenerRequest request) + { + return this.logHandler.GetLatestLogIndex().ToString(); + } + private static string GetRequestPostData(HttpListenerRequest request) { if (!request.HasEntityBody) diff --git a/win/CS/HandBrake.Worker/App.config b/win/CS/HandBrake.Worker/App.config index 8fc055122..4bfa00561 100644 --- a/win/CS/HandBrake.Worker/App.config +++ b/win/CS/HandBrake.Worker/App.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <configuration> <startup> - <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1"/> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/> </startup> </configuration> diff --git a/win/CS/HandBrake.Worker/HandBrake.Worker.csproj b/win/CS/HandBrake.Worker/HandBrake.Worker.csproj index faecde169..99e85a8fa 100644 --- a/win/CS/HandBrake.Worker/HandBrake.Worker.csproj +++ b/win/CS/HandBrake.Worker/HandBrake.Worker.csproj @@ -8,7 +8,7 @@ <OutputType>Exe</OutputType> <RootNamespace>HandBrake.Worker</RootNamespace> <AssemblyName>HandBrake.Worker</AssemblyName> - <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion> + <TargetFrameworkVersion>v4.8</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <TargetFrameworkProfile /> @@ -33,6 +33,9 @@ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <Prefer32Bit>true</Prefer32Bit> </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>handbrakepineapple.ico</ApplicationIcon> + </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="System.Core" /> @@ -43,6 +46,10 @@ <ItemGroup> <Compile Include="ApiRouter.cs" /> <Compile Include="HttpServer.cs" /> + <Compile Include="Logging\Interfaces\ILogHandler.cs" /> + <Compile Include="Logging\LogHandler.cs" /> + <Compile Include="Logging\Models\LogHandlerConfig.cs" /> + <Compile Include="Logging\Models\LogMessage.cs" /> <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> @@ -50,9 +57,6 @@ <None Include="App.config" /> </ItemGroup> <ItemGroup> - <PackageReference Include="gong-wpf-dragdrop"> - <Version>2.0.1</Version> - </PackageReference> <PackageReference Include="Newtonsoft.Json"> <Version>12.0.2</Version> <HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath> @@ -64,5 +68,9 @@ <Name>HandBrake.Interop</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <Content Include="handbrakepineapple.ico" /> + </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/win/CS/HandBrake.Worker/HttpServer.cs b/win/CS/HandBrake.Worker/HttpServer.cs index 095b51b43..a2495ba39 100644 --- a/win/CS/HandBrake.Worker/HttpServer.cs +++ b/win/CS/HandBrake.Worker/HttpServer.cs @@ -35,7 +35,7 @@ namespace HandBrake.Worker Console.WriteLine(Environment.NewLine + "Available APIs: "); foreach (KeyValuePair<string, Func<HttpListenerRequest, string>> api in apiCalls) { - string url = string.Format("http://localhost:{0}/{1}/", port, api.Key); + string url = string.Format("http://127.0.0.1:{0}/{1}/", port, api.Key); this.httpListener.Prefixes.Add(url); Console.WriteLine(url); } @@ -66,6 +66,8 @@ namespace HandBrake.Worker { string path = context.Request.RawUrl.TrimStart('/').TrimEnd('/'); + Console.WriteLine("Handling call to: " + path); + if (this.apiHandlers.TryGetValue(path, out var actionToPerform)) { string rstr = actionToPerform(context.Request); diff --git a/win/CS/HandBrake.Worker/Logging/Interfaces/ILogHandler.cs b/win/CS/HandBrake.Worker/Logging/Interfaces/ILogHandler.cs new file mode 100644 index 000000000..3daa69ba0 --- /dev/null +++ b/win/CS/HandBrake.Worker/Logging/Interfaces/ILogHandler.cs @@ -0,0 +1,43 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="ILogHandler.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 ILogHandler type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrake.Worker.Logging.Interfaces +{ + using HandBrake.Worker.Logging.Models; + + public interface ILogHandler + { + /// <summary> + /// Enable logging for this worker process. + /// </summary> + /// <param name="config"> + /// Configuration for the logger. + /// </param> + /// <remarks> + /// If this is not called, all log messages from libhb will be ignored. + /// </remarks> + void ConfigureLogging(LogHandlerConfig config); + + string GetFullLog(); + + long GetLatestLogIndex(); + + /// <summary> + /// Get the log data from a given index + /// </summary> + /// <param name="index">index is zero based</param> + /// <returns>Full log as a string</returns> + string GetLogFromIndex(int index); + + /// <summary> + /// Empty the log cache and reset the log handler to defaults. + /// </summary> + void Reset(); + } +} diff --git a/win/CS/HandBrake.Worker/Logging/LogHandler.cs b/win/CS/HandBrake.Worker/Logging/LogHandler.cs new file mode 100644 index 000000000..a3390a881 --- /dev/null +++ b/win/CS/HandBrake.Worker/Logging/LogHandler.cs @@ -0,0 +1,276 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="LogHandler.cs" company="HandBrake Project (https://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 log service. +// The Interop Classes are not very OO friendly. This is a shim over the logging code to allow a nice Web API to be built on top. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrake.Worker.Logging +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + + using HandBrake.Interop.Interop.EventArgs; + using HandBrake.Worker.Logging.Interfaces; + using HandBrake.Worker.Logging.Models; + + public class LogHandler : ILogHandler + { + 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 long messageIndex; + private string diskLogPath; + private bool deleteLogFirst; + private bool isDiskLoggingEnabled; + private StreamWriter fileWriter; + private string logHeader; + + public IEnumerable<LogMessage> LogMessages + { + get + { + lock (this.lockObject) + { + return this.logMessages.ToList(); + } + } + } + + public string GetFullLog() + { + lock (this.lockObject) + { + return this.logBuilder.ToString(); + } + } + + public long GetLatestLogIndex() + { + lock (this.lockObject) + { + return this.messageIndex; + } + } + + public string GetLogFromIndex(int index) + { + StringBuilder log = new StringBuilder(); + lock (this.lockObject) + { + // Note messageIndex is not 0 based. + for (int i = index; i < this.messageIndex; i++) + { + log.AppendLine(this.logMessages[i].Content); + } + } + + return log.ToString(); + } + + public void LogMessage(string content) + { + if (!this.isLoggingEnabled) + { + return; + } + + lock (this.lockObject) + { + LogMessage msg = new LogMessage(content, this.messageIndex); + this.messageIndex = this.messageIndex + 1; + this.logMessages.Add(msg); + this.logBuilder.AppendLine(msg.Content); + this.LogMessageToDisk(msg); + + if (this.logMessages.Count > 50000) + { + this.messageIndex = this.messageIndex + 1; + msg = new LogMessage("Log Service Pausing. Too Many Log messages. This may indicate a problem with your encode.", this.messageIndex); + this.logMessages.Add(msg); + this.logBuilder.AppendLine(msg.Content); + this.LogMessageToDisk(msg); + + this.isLoggingEnabled = false; + } + } + } + + public void ConfigureLogging(LogHandlerConfig config) + { + this.isLoggingEnabled = true; + this.logHeader = config.Header; + this.LogMessage(config.Header); + + if (config.EnableDiskLogging) + { + this.EnableLoggingToDisk(config.LogFile, config.DeleteCurrentLogFirst); + } + } + + public void Reset() + { + lock (this.lockObject) + { + 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); + } + + if (this.fileWriter == null) + { + this.isDiskLoggingEnabled = false; + this.EnableLoggingToDisk(this.diskLogPath, this.deleteLogFirst); + } + + if (!string.IsNullOrEmpty(this.logHeader)) + { + this.SetupLogHeader(this.logHeader); + } + } + } + + protected void ShutdownFileWriter() + { + 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 exception doesn't warrant user interaction, but it should be logged + } + } + + private void SetupLogHeader(string header) + { + this.logHeader = header; + this.LogMessage(header); + } + + private void EnableLoggingToDisk(string logFile, bool deleteCurrentLogFirst) + { + if (this.isDiskLoggingEnabled) + { + throw new Exception("Disk Logging already enabled!"); + } + + try + { + if (!Directory.Exists(Path.GetDirectoryName(logFile))) + { + throw new Exception("Log Directory does not exist. This service will not create it for you!"); + } + + if (deleteCurrentLogFirst && File.Exists(logFile)) + { + File.Delete(logFile); + } + + this.diskLogPath = logFile; + this.isDiskLoggingEnabled = true; + this.deleteLogFirst = deleteCurrentLogFirst; + + lock (this.fileWriterLock) + { + this.fileWriter = new StreamWriter(logFile) { AutoFlush = true }; + } + } + catch (Exception exc) + { + this.LogMessage("Failed to Initialise Disk Logging. " + Environment.NewLine + exc); + + if (this.fileWriter != null) + { + lock (this.fileWriterLock) + { + this.fileWriter.Flush(); + this.fileWriter.Close(); + this.fileWriter.Dispose(); + } + } + } + } + + private void LogMessageToDisk(LogMessage msg) + { + if (!this.isDiskLoggingEnabled) + { + return; + } + + try + { + lock (this.fileWriterLock) + { + if (this.fileWriter != null && this.fileWriter.BaseStream.CanWrite) + { + this.fileWriter.WriteLine(msg.Content); + } + } + } + catch (Exception exc) + { + 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); + } + } +} diff --git a/win/CS/HandBrake.Worker/Logging/Models/LogHandlerConfig.cs b/win/CS/HandBrake.Worker/Logging/Models/LogHandlerConfig.cs new file mode 100644 index 000000000..2cbb97cb3 --- /dev/null +++ b/win/CS/HandBrake.Worker/Logging/Models/LogHandlerConfig.cs @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="LogHandlerConfig.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 LogHandlerConfig type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrake.Worker.Logging.Models +{ + public class LogHandlerConfig + { + public LogHandlerConfig(bool enableDiskLogging, string logFile, bool deleteCurrentLogFirst, string header) + { + this.EnableDiskLogging = enableDiskLogging; + this.LogFile = logFile; + this.DeleteCurrentLogFirst = deleteCurrentLogFirst; + this.Header = header; + } + + public LogHandlerConfig() + { + } + + public bool EnableDiskLogging { get; set; } + + public string LogFile { get; set; } + + public bool DeleteCurrentLogFirst { get; set; } + + public string Header { get; set; } + } +} diff --git a/win/CS/HandBrake.Worker/Logging/Models/LogMessage.cs b/win/CS/HandBrake.Worker/Logging/Models/LogMessage.cs new file mode 100644 index 000000000..c47e331b0 --- /dev/null +++ b/win/CS/HandBrake.Worker/Logging/Models/LogMessage.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="LogMessage.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> +// An Immutable log message +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrake.Worker.Logging.Models +{ + public class LogMessage + { + public LogMessage(string content, long messageIndex) + { + this.Content = content; + this.MessageIndex = messageIndex; + } + + public string Content { get; private set; } + + public long MessageIndex { get; private set; } + } +} diff --git a/win/CS/HandBrake.Worker/Program.cs b/win/CS/HandBrake.Worker/Program.cs index d65d5d36a..95c13ddec 100644 --- a/win/CS/HandBrake.Worker/Program.cs +++ b/win/CS/HandBrake.Worker/Program.cs @@ -12,29 +12,23 @@ namespace HandBrake.Worker using System; using System.Collections.Generic; using System.Net; + using System.Threading; public class Program { /* * TODO - * Methods: - * 1. Fetch Log - * 2. Fetch Log since last index. - * Services: - * 3. Support for connecting via sockets. - * 4. All methods will return a json state object response. + * Support for connecting via sockets. + * All methods will return a json state object response. */ private static ApiRouter router; + private static ManualResetEvent manualResetEvent = new ManualResetEvent(false); public static void Main(string[] args) { - Console.WriteLine("Starting Web Server ..."); - router = new ApiRouter(); - - Dictionary<string, Func<HttpListenerRequest, string>> apiHandlers = RegisterApiHandlers(); - - int port = 8080; // Default Port; + int port = 8036; // Default Port; + int verbosity = 1; if (args.Length != 0) { @@ -42,24 +36,38 @@ namespace HandBrake.Worker { if (argument.StartsWith("--port")) { - string portStr = argument.TrimStart("--port=".ToCharArray()); - if (int.TryParse(portStr, out var parsedPort)) + string value = argument.TrimStart("--port=".ToCharArray()); + if (int.TryParse(value, out var parsedPort)) { port = parsedPort; } } + + if (argument.StartsWith("--verbosity")) + { + string value = argument.TrimStart("--port=".ToCharArray()); + if (int.TryParse(value, out var verbosityVal)) + { + verbosity = verbosityVal; + } + } } } - Console.WriteLine("Using Port: {0}", port); + Console.WriteLine("Starting HandBrake Engine ..."); + router = new ApiRouter(); + router.Initialise(verbosity); + Console.WriteLine("Starting Web Server ..."); + Console.WriteLine("Using Port: {0}", port); + Dictionary<string, Func<HttpListenerRequest, string>> apiHandlers = RegisterApiHandlers(); HttpServer webServer = new HttpServer(apiHandlers, port); webServer.Run(); - Console.WriteLine("Webserver Started"); - Console.WriteLine("Press any key to exit"); + Console.WriteLine("Web Server Started"); - Console.ReadKey(); // Block from closing. + // Console.ReadKey(); // Block from closing. + manualResetEvent.WaitOne(); webServer.Stop(); } @@ -76,10 +84,22 @@ namespace HandBrake.Worker apiHandlers.Add("StopEncode", router.StopEncode); apiHandlers.Add("PollEncodeProgress", router.PollEncodeProgress); apiHandlers.Add("SetConfiguration", router.SetConfiguration); - apiHandlers.Add("Initialise", router.Initialise); - + apiHandlers.Add("Shutdown", ShutdownServer); + + // Logging + apiHandlers.Add("ConfigureLogging", router.ConfigureLogging); + apiHandlers.Add("GetFullLog", router.GetFullLog); + apiHandlers.Add("GetLatestLogIndex", router.GetLatestLogIndex); + apiHandlers.Add("GetLogFromIndex", router.GetLogFromIndex); + return apiHandlers; } + + public static string ShutdownServer(HttpListenerRequest request) + { + manualResetEvent.Set(); + return "Server Terminated"; + } } } diff --git a/win/CS/HandBrake.Worker/Settings.StyleCop b/win/CS/HandBrake.Worker/Settings.StyleCop new file mode 100644 index 000000000..29a523332 --- /dev/null +++ b/win/CS/HandBrake.Worker/Settings.StyleCop @@ -0,0 +1,17 @@ +<StyleCopSettings Version="105"> + <Parsers> + <Parser ParserId="StyleCop.CSharp.CsParser"> + <ParserSettings> + <BooleanProperty Name="AnalyzeDesignerFiles">False</BooleanProperty> + </ParserSettings> + </Parser> + </Parsers> + <Analyzers> + <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules"> + <AnalyzerSettings> + <StringProperty Name="CompanyName">HandBrake Project (http://handbrake.fr)</StringProperty> + <StringProperty Name="Copyright">This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.</StringProperty> + </AnalyzerSettings> + </Analyzer> + </Analyzers> +</StyleCopSettings>
\ No newline at end of file diff --git a/win/CS/HandBrake.Worker/handbrakepineapple.ico b/win/CS/HandBrake.Worker/handbrakepineapple.ico Binary files differnew file mode 100644 index 000000000..fd8317341 --- /dev/null +++ b/win/CS/HandBrake.Worker/handbrakepineapple.ico |