diff options
author | sr55 <[email protected]> | 2011-03-13 16:44:49 +0000 |
---|---|---|
committer | sr55 <[email protected]> | 2011-03-13 16:44:49 +0000 |
commit | b6a5d4eba610711d15ed99dc5f2e9e126ce06086 (patch) | |
tree | e72eb5be161c3ac9b01bbc3b3efcd4ab8f91e06c /win/CS/HandBrake.ApplicationServices/Services | |
parent | 38f64c238720fe0524f99b380fbb1a8c795a7586 (diff) |
Rename Direction C# to CS
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3846 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'win/CS/HandBrake.ApplicationServices/Services')
12 files changed, 2833 insertions, 0 deletions
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode.cs new file mode 100644 index 000000000..4a6333da7 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Encode.cs @@ -0,0 +1,595 @@ +/* Encode.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.ComponentModel;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Text;
+ using System.Threading;
+ using System.Windows.Forms;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Functions;
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Parsing;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+ using HandBrake.ApplicationServices.Utilities;
+
+ /// <summary>
+ /// Class which handles the CLI
+ /// </summary>
+ public class Encode : IEncode
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// The Log Buffer
+ /// </summary>
+ private StringBuilder logBuffer;
+
+ /// <summary>
+ /// The Log file writer
+ /// </summary>
+ private StreamWriter fileWriter;
+
+ /// <summary>
+ /// Gets The Process Handle
+ /// </summary>
+ private IntPtr processHandle;
+
+ /// <summary>
+ /// Gets the Process ID
+ /// </summary>
+ private int processId;
+
+ /// <summary>
+ /// Windows 7 API Pack wrapper
+ /// </summary>
+ private Win7 windowsSeven = new Win7();
+
+ /// <summary>
+ /// A Lock for the filewriter
+ /// </summary>
+ static readonly object fileWriterLock = new object();
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Encode"/> class.
+ /// </summary>
+ public Encode()
+ {
+ this.EncodeStarted += this.EncodeEncodeStarted;
+ GrowlCommunicator.Register();
+ }
+
+ #region Delegates and Event Handlers
+
+ /// <summary>
+ /// Fires when a new CLI QueueTask starts
+ /// </summary>
+ public event EventHandler EncodeStarted;
+
+ /// <summary>
+ /// Fires when a CLI QueueTask finishes.
+ /// </summary>
+ public event EncodeCompletedStatus EncodeCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ public event EncodeProgessStatus EncodeStatusChanged;
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets The HB Process
+ /// </summary>
+ protected Process HbProcess { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether IsEncoding.
+ /// </summary>
+ public bool IsEncoding { get; private set; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ public string ActivityLog
+ {
+ get
+ {
+ if (this.IsEncoding == false)
+ {
+ try
+ {
+ ReadFile(); // Read the last log file back in if it exists
+ }
+ catch (Exception exc)
+ {
+ return exc.ToString();
+ }
+ }
+
+ return string.IsNullOrEmpty(this.logBuffer.ToString()) ? "No log data available..." : this.logBuffer.ToString();
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Execute a HandBrakeCLI process.
+ /// </summary>
+ /// <param name="encodeQueueTask">
+ /// The encodeQueueTask.
+ /// </param>
+ /// <param name="enableLogging">
+ /// Enable Logging. When Disabled we onlt parse Standard Ouput for progress info. Standard Error log data is ignored.
+ /// </param>
+ public void Start(QueueTask encodeQueueTask, bool enableLogging)
+ {
+ try
+ {
+ QueueTask queueTask = encodeQueueTask;
+
+ if (queueTask == null)
+ {
+ throw new ArgumentNullException("QueueTask was null");
+ }
+
+ if (IsEncoding)
+ {
+ throw new Exception("HandBrake is already encodeing.");
+ }
+
+ IsEncoding = true;
+
+ if (enableLogging)
+ {
+ try
+ {
+ SetupLogging(queueTask);
+ }
+ catch (Exception)
+ {
+ IsEncoding = false;
+ throw;
+ }
+ }
+
+ if (Init.PreventSleep)
+ {
+ Win32.PreventSleep();
+ }
+
+ string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
+ ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, queueTask.Query)
+ {
+ RedirectStandardOutput = true,
+ RedirectStandardError = enableLogging ? true : false,
+ UseShellExecute = false,
+ CreateNoWindow = !Init.ShowCliForInGuiEncodeStatus ? true : false
+ };
+
+ this.HbProcess = new Process { StartInfo = cliStart };
+
+ this.HbProcess.Start();
+
+ if (enableLogging)
+ {
+ this.HbProcess.ErrorDataReceived += HbProcErrorDataReceived;
+ this.HbProcess.BeginErrorReadLine();
+ }
+
+ this.processId = HbProcess.Id;
+ this.processHandle = HbProcess.Handle;
+
+ // Set the process Priority
+ if (this.processId != -1)
+ {
+ this.HbProcess.EnableRaisingEvents = true;
+ this.HbProcess.Exited += this.HbProcessExited;
+ }
+
+ // Set the Process Priority
+ switch (Init.ProcessPriority)
+ {
+ case "Realtime":
+ this.HbProcess.PriorityClass = ProcessPriorityClass.RealTime;
+ break;
+ case "High":
+ this.HbProcess.PriorityClass = ProcessPriorityClass.High;
+ break;
+ case "Above Normal":
+ this.HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal;
+ break;
+ case "Normal":
+ this.HbProcess.PriorityClass = ProcessPriorityClass.Normal;
+ break;
+ case "Low":
+ this.HbProcess.PriorityClass = ProcessPriorityClass.Idle;
+ break;
+ default:
+ this.HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal;
+ break;
+ }
+
+ // Fire the Encode Started Event
+ if (this.EncodeStarted != null)
+ this.EncodeStarted(this, new EventArgs());
+ }
+ catch (Exception exc)
+ {
+ if (this.EncodeCompleted != null)
+ this.EncodeCompleted(this, new EncodeCompletedEventArgs(false, exc, "An Error has occured in EncodeService.Run()"));
+ }
+ }
+
+ /// <summary>
+ /// Stop the Encode
+ /// </summary>
+ public void Stop()
+ {
+ this.Stop(null);
+ }
+
+ /// <summary>
+ /// Kill the CLI process
+ /// </summary>
+ /// <param name="exc">
+ /// The Exception that has occured.
+ /// This will get bubbled up through the EncodeCompletedEventArgs
+ /// </param>
+ public void Stop(Exception exc)
+ {
+ try
+ {
+ if (this.HbProcess != null && !this.HbProcess.HasExited)
+ {
+ this.HbProcess.Kill();
+ }
+ }
+ catch (Exception)
+ {
+ // No need to report anything to the user. If it fails, it's probably already stopped.
+ }
+
+
+ if (exc == null)
+ {
+ if (this.EncodeCompleted != null)
+ this.EncodeCompleted(this, new EncodeCompletedEventArgs(true, null, string.Empty));
+ }
+ else
+ {
+ if (this.EncodeCompleted != null)
+ this.EncodeCompleted(this, new EncodeCompletedEventArgs(false, exc, "An Error has occured."));
+ }
+ }
+
+ /// <summary>
+ /// Attempt to Safely kill a DirectRun() CLI
+ /// NOTE: This will not work with a MinGW CLI
+ /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html
+ /// </summary>
+ public void SafelyStop()
+ {
+ if ((int)this.processHandle == 0)
+ return;
+
+ // Allow the CLI to exit cleanly
+ Win32.SetForegroundWindow((int)this.processHandle);
+ SendKeys.Send("^C");
+ SendKeys.Flush();
+
+ /*/if (HbProcess != null)
+ //{
+ // HbProcess.StandardInput.AutoFlush = true;
+ // HbProcess.StandardInput.WriteLine("^c^z");
+ //}*/
+ }
+
+ /// <summary>
+ /// Save a copy of the log to the users desired location or a default location
+ /// if this feature is enabled in options.
+ /// </summary>
+ /// <param name="destination">
+ /// The Destination File Path
+ /// </param>
+ public void ProcessLogs(string destination)
+ {
+ try
+ {
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
+ "\\HandBrake\\logs";
+ string tempLogFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId));
+
+ string encodeDestinationPath = Path.GetDirectoryName(destination);
+ string destinationFile = Path.GetFileName(destination);
+ string encodeLogFile = destinationFile + " " +
+ DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";
+
+ // Make sure the log directory exists.
+ if (!Directory.Exists(logDir))
+ Directory.CreateDirectory(logDir);
+
+ // Copy the Log to HandBrakes log folder in the users applciation data folder.
+ File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));
+
+ // Save a copy of the log file in the same location as the enocde.
+ if (Init.SaveLogWithVideo)
+ File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));
+
+ // Save a copy of the log file to a user specified location
+ if (Directory.Exists(Init.SaveLogPath))
+ if (Init.SaveLogPath != String.Empty && Init.SaveLogToSpecifiedPath)
+ File.Copy(tempLogFile, Path.Combine(Init.SaveLogPath, encodeLogFile));
+ }
+ catch (Exception exc)
+ {
+ // This exception doesn't warrent user interaction, but it should be logged (TODO)
+ }
+ }
+
+ #endregion
+
+ #region Private Helper Methods
+
+ /// <summary>
+ /// The HandBrakeCLI process has exited.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void HbProcessExited(object sender, EventArgs e)
+ {
+ IsEncoding = false;
+ if (windowsSeven.IsWindowsSeven)
+ {
+ windowsSeven.SetTaskBarProgressToNoProgress();
+ }
+
+ if (Init.PreventSleep)
+ {
+ Win32.AllowSleep();
+ }
+
+ try
+ {
+ lock (fileWriterLock)
+ {
+ // This is just a quick hack to ensure that we are done processing the logging data.
+ // Logging data comes in after the exit event has processed sometimes. We should really impliment ISyncronizingInvoke
+ // and set the SyncObject on the process. I think this may resolve this properly.
+ // For now, just wait 2.5 seconds to let any trailing log messages come in and be processed.
+ Thread.Sleep(2500);
+
+ this.HbProcess.CancelErrorRead();
+
+ if (fileWriter != null)
+ {
+ fileWriter.Close();
+ fileWriter.Dispose();
+ }
+
+ fileWriter = null;
+ }
+ }
+ catch (Exception exc)
+ {
+ // This exception doesn't warrent user interaction, but it should be logged (TODO)
+ }
+
+ if (this.EncodeCompleted != null)
+ this.EncodeCompleted(this, new EncodeCompletedEventArgs(true, null, string.Empty));
+ }
+
+ /// <summary>
+ /// Read the log file
+ /// </summary>
+ private void ReadFile()
+ {
+ logBuffer = new StringBuilder();
+ lock (logBuffer)
+ {
+ // last_encode_log.txt is the primary log file. Since .NET can't read this file whilst the CLI is outputing to it (Not even in read only mode),
+ // we'll need to make a copy of it.
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
+ string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId));
+ string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId));
+ int logFilePosition = 0;
+
+ try
+ {
+ // Copy the log file.
+ if (File.Exists(logFile))
+ File.Copy(logFile, logFile2, true);
+ else
+ return;
+
+ // Start the Reader
+ // Only use text which continues on from the last read line
+ using (StreamReader sr = new StreamReader(logFile2))
+ {
+ string line;
+ int i = 1;
+ while ((line = sr.ReadLine()) != null)
+ {
+ if (i > logFilePosition)
+ {
+ logBuffer.AppendLine(line);
+ logFilePosition++;
+ }
+ i++;
+ }
+ sr.Close();
+ }
+ }
+ catch (Exception exc)
+ {
+ logBuffer.Append(
+ Environment.NewLine + "Unable to read Log file..." + Environment.NewLine + exc +
+ Environment.NewLine);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Setup the logging.
+ /// </summary>
+ /// <param name="encodeQueueTask">
+ /// The encode QueueTask.
+ /// </param>
+ private void SetupLogging(QueueTask encodeQueueTask)
+ {
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
+ string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId));
+ string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId));
+
+ try
+ {
+ logBuffer = new StringBuilder();
+
+ // Clear the current Encode Logs
+ if (File.Exists(logFile)) File.Delete(logFile);
+ if (File.Exists(logFile2)) File.Delete(logFile2);
+
+ fileWriter = new StreamWriter(logFile) { AutoFlush = true };
+
+ fileWriter.WriteLine(UtilityService.CreateCliLogHeader(encodeQueueTask));
+ logBuffer.AppendLine(UtilityService.CreateCliLogHeader(encodeQueueTask));
+ }
+ catch (Exception)
+ {
+ if (fileWriter != null)
+ {
+ fileWriter.Close();
+ fileWriter.Dispose();
+ }
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Recieve the Standard Error information and process it
+ /// </summary>
+ /// <param name="sender">
+ /// The Sender Object
+ /// </param>
+ /// <param name="e">
+ /// DataReceived EventArgs
+ /// </param>
+ private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (!String.IsNullOrEmpty(e.Data))
+ {
+ try
+ {
+ lock (logBuffer)
+ logBuffer.AppendLine(e.Data);
+
+ lock (fileWriterLock)
+ {
+ if (fileWriter != null && fileWriter.BaseStream.CanWrite)
+ {
+ fileWriter.WriteLine(e.Data);
+
+ // If the logging grows past 100MB, kill the encode and stop.
+ if (fileWriter.BaseStream.Length > 100000000)
+ {
+ this.Stop(
+ new Exception(
+ "The encode has been stopped. The log file has grown to over 100MB which indicates a serious problem has occured with the encode." +
+ "Please check the encode log for an indication of what the problem is."));
+ }
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ // Do Nothing.
+ }
+ }
+ }
+
+ /// <summary>
+ /// Encode Started
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void EncodeEncodeStarted(object sender, EventArgs e)
+ {
+ Thread monitor = new Thread(EncodeMonitor);
+ monitor.Start();
+ }
+
+ /// <summary>
+ /// Monitor the QueueTask
+ /// </summary>
+ private void EncodeMonitor()
+ {
+ try
+ {
+ Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);
+ encode.OnEncodeProgress += EncodeOnEncodeProgress;
+ while (!encode.EndOfStream)
+ encode.ReadEncodeStatus();
+ }
+ catch (Exception exc)
+ {
+ EncodeOnEncodeProgress(null, 0, 0, 0, 0, 0, "Unknown, status not available..");
+ }
+ }
+
+ /// <summary>
+ /// Displays the Encode status in the GUI
+ /// </summary>
+ /// <param name="sender">The sender</param>
+ /// <param name="currentTask">The current task</param>
+ /// <param name="taskCount">Number of tasks</param>
+ /// <param name="percentComplete">Percent complete</param>
+ /// <param name="currentFps">Current encode speed in fps</param>
+ /// <param name="avg">Avg encode speed</param>
+ /// <param name="timeRemaining">Time Left</param>
+ private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, string timeRemaining)
+ {
+ EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs
+ {
+ AverageFrameRate = avg,
+ CurrentFrameRate = currentFps,
+ EstimatedTimeLeft = Converters.EncodeToTimespan(timeRemaining),
+ PercentComplete = percentComplete,
+ Task = currentTask,
+ TaskCount = taskCount
+ };
+
+ if (this.EncodeStatusChanged != null)
+ this.EncodeStatusChanged(this, eventArgs);
+
+ if (windowsSeven.IsWindowsSeven)
+ {
+ int percent;
+ int.TryParse(Math.Round(percentComplete).ToString(), out percent);
+
+ windowsSeven.SetTaskBarProgress(percent);
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IEncode.cs b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IEncode.cs new file mode 100644 index 000000000..7ba739617 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IEncode.cs @@ -0,0 +1,96 @@ +/* IEncode.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services.Interfaces
+{
+ using System;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Model;
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public delegate void EncodeCompletedStatus(object sender, EncodeCompletedEventArgs e);
+
+ /// <summary>
+ /// The IEncode Interface
+ /// </summary>
+ public interface IEncode
+ {
+ /// <summary>
+ /// Fires when a new CLI Job starts
+ /// </summary>
+ event EventHandler EncodeStarted;
+
+ /// <summary>
+ /// Fires when a CLI job finishes.
+ /// </summary>
+ event EncodeCompletedStatus EncodeCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ event EncodeProgessStatus EncodeStatusChanged;
+
+ /// <summary>
+ /// Gets a value indicating whether IsEncoding.
+ /// </summary>
+ bool IsEncoding { get; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ string ActivityLog { get; }
+
+ /// <summary>
+ /// Start with a LibHb EncodeJob Object
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ /// <param name="enableLogging">
+ /// The enable Logging.
+ /// </param>
+ void Start(QueueTask job, bool enableLogging);
+
+ /// <summary>
+ /// Kill the CLI process
+ /// </summary>
+ void Stop();
+
+ /// <summary>
+ /// Attempt to Safely kill a DirectRun() CLI
+ /// NOTE: This will not work with a MinGW CLI
+ /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html
+ /// </summary>
+ void SafelyStop();
+
+ /// <summary>
+ /// Copy the log file to the desired destinations
+ /// </summary>
+ /// <param name="destination">
+ /// The destination.
+ /// </param>
+ void ProcessLogs(string destination);
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IPresetService.cs b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IPresetService.cs new file mode 100644 index 000000000..30d6b2f3a --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IPresetService.cs @@ -0,0 +1,93 @@ +/* IPresetService.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services.Interfaces
+{
+ using System.Collections.ObjectModel;
+
+ using HandBrake.ApplicationServices.Model;
+
+ /// <summary>
+ /// The Preset Service Interface
+ /// </summary>
+ public interface IPresetService
+ {
+ /// <summary>
+ /// Gets or sets a Collection of presets.
+ /// </summary>
+ ObservableCollection<Preset> Presets { get; set; }
+
+ /// <summary>
+ /// Add a new preset to the system
+ /// </summary>
+ /// <param name="preset">
+ /// A Preset to add
+ /// </param>
+ /// <returns>
+ /// True if added,
+ /// False if name already exists
+ /// </returns>
+ bool Add(Preset preset);
+
+ /// <summary>
+ /// Remove a preset with a given name from either the built in or user preset list.
+ /// </summary>
+ /// <param name="preset">
+ /// The Preset to remove
+ /// </param>
+ void Remove(Preset preset);
+
+ /// <summary>
+ /// Remove a group of presets by category
+ /// </summary>
+ /// <param name="category">
+ /// The Category to remove
+ /// </param>
+ void RemoveGroup(string category);
+
+ /// <summary>
+ /// Get a Preset
+ /// </summary>
+ /// <param name="name">
+ /// The name of the preset to get
+ /// </param>
+ /// <returns>
+ /// A Preset or null object
+ /// </returns>
+ Preset GetPreset(string name);
+
+ /// <summary>
+ /// Clear Built-in Presets
+ /// </summary>
+ void ClearBuiltIn();
+
+ /// <summary>
+ /// Clear all presets
+ /// </summary>
+ void ClearAll();
+
+ /// <summary>
+ /// Reads the CLI's CLI output format and load's them into the preset List Preset
+ /// </summary>
+ /// <param name="cliPath">
+ /// The Path to the CLI, leave blank for current folder.
+ /// </param>
+ void UpdateBuiltInPresets(string cliPath);
+
+ /// <summary>
+ /// Check if the built in Presets stored are not out of date.
+ /// Update them if they are.
+ /// </summary>
+ /// <returns>true if out of date</returns>
+ bool CheckIfPresetsAreOutOfDate();
+
+ /// <summary>
+ /// Check if the preset "name" exists in either Presets or UserPresets lists.
+ /// </summary>
+ /// <param name="name">Name of the preset</param>
+ /// <returns>True if found</returns>
+ bool CheckIfPresetExists(string name);
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IQueueManager.cs b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IQueueManager.cs new file mode 100644 index 000000000..2c9005cfb --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IQueueManager.cs @@ -0,0 +1,113 @@ +/* IQueueManager.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services.Interfaces
+{
+ using System;
+ using System.Collections.ObjectModel;
+
+ using HandBrake.ApplicationServices.Model;
+
+ /// <summary>
+ /// The Queue Manager Interface
+ /// </summary>
+ public interface IQueueManager
+ {
+ /// <summary>
+ /// Fires when a job is Added, Removed or Re-Ordered.
+ /// Should be used for triggering an update of the Queue Window.
+ /// </summary>
+ event EventHandler QueueChanged;
+
+ /// <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; }
+
+ /// <summary>
+ /// Gets The current queue.
+ /// </summary>
+ ReadOnlyCollection<QueueTask> Queue { get; }
+
+ /// <summary>
+ /// Gets the number of jobs in the queue
+ /// </summary>
+ int Count { get; }
+
+ /// <summary>
+ /// Add a job to the Queue.
+ /// This method is Thread Safe.
+ /// </summary>
+ /// <param name="job">
+ /// The encode Job object.
+ /// </param>
+ void Add(QueueTask job);
+
+ /// <summary>
+ /// Remove a job from the Queue.
+ /// This method is Thread Safe
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ void Remove(QueueTask job);
+
+ /// <summary>
+ /// Get the first job on the queue for processing.
+ /// This also removes the job from the Queue and sets the LastProcessedJob
+ /// </summary>
+ /// <returns>
+ /// An encode Job object.
+ /// </returns>
+ QueueTask GetNextJobForProcessing();
+
+ /// <summary>
+ /// Moves an item up one position in the queue.
+ /// </summary>
+ /// <param name="index">The zero-based location of the job in the queue.</param>
+ void MoveUp(int index);
+
+ /// <summary>
+ /// Moves an item down one position in the queue.
+ /// </summary>
+ /// <param name="index">The zero-based location of the job in the queue.</param>
+ void MoveDown(int index);
+
+ /// <summary>
+ /// Backup any changes to the queue file
+ /// </summary>
+ /// <param name="exportPath">
+ /// If this is not null or empty, this will be used instead of the standard backup location.
+ /// </param>
+ void BackupQueue(string exportPath);
+
+ /// <summary>
+ /// Restore a Queue from file or from the queue backup file.
+ /// </summary>
+ /// <param name="importPath">
+ /// The import path. String.Empty or null will result in the default file being loaded.
+ /// </param>
+ void RestoreQueue(string importPath);
+
+ /// <summary>
+ /// Checks the current queue for an existing instance of the specified destination.
+ /// </summary>
+ /// <param name="destination">The destination of the encode.</param>
+ /// <returns>Whether or not the supplied destination is already in the queue.</returns>
+ bool CheckForDestinationPathDuplicates(string destination);
+
+ /// <summary>
+ /// Create a batch script from the queue
+ /// </summary>
+ /// <param name="path">
+ /// The path to the location for the script to be saved.
+ /// </param>
+ /// <returns>
+ /// True if sucessful
+ /// </returns>
+ bool WriteBatchScriptToFile(string path);
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IQueueProcessor.cs b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IQueueProcessor.cs new file mode 100644 index 000000000..3d71688cb --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IQueueProcessor.cs @@ -0,0 +1,51 @@ +namespace HandBrake.ApplicationServices.Services.Interfaces
+{
+ using System;
+
+ /// <summary>
+ /// The Queue Processor
+ /// </summary>
+ public interface IQueueProcessor
+ {
+ /// <summary>
+ /// Fires when the Queue has started
+ /// </summary>
+ event QueueProcessor.QueueProgressStatus JobProcessingStarted;
+
+ /// <summary>
+ /// Fires when a pause to the encode queue has been requested.
+ /// </summary>
+ event EventHandler QueuePaused;
+
+ /// <summary>
+ /// Fires when the entire encode queue has completed.
+ /// </summary>
+ event EventHandler QueueCompleted;
+
+ /// <summary>
+ /// Gets the IEncodeService instance.
+ /// </summary>
+ IEncode EncodeService { get; }
+
+ /// <summary>
+ /// Gets the IQueueManager instance.
+ /// </summary>
+ IQueueManager QueueManager { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether IsProcessing.
+ /// </summary>
+ bool IsProcessing { get; }
+
+ /// <summary>
+ /// Starts encoding the first job in the queue and continues encoding until all jobs
+ /// have been encoded.
+ /// </summary>
+ void Start();
+
+ /// <summary>
+ /// Requests a pause of the encode queue.
+ /// </summary>
+ void Pause();
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IScan.cs b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IScan.cs new file mode 100644 index 000000000..6cbce5273 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Interfaces/IScan.cs @@ -0,0 +1,83 @@ +/* IScan.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services.Interfaces
+{
+ using System;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Parsing;
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public delegate void ScanProgessStatus(object sender, ScanProgressEventArgs e);
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The ScanCompletedEventArgs.
+ /// </param>
+ public delegate void ScanCompletedStatus(object sender, ScanCompletedEventArgs e);
+
+ /// <summary>
+ /// The IScan Interface
+ /// </summary>
+ public interface IScan
+ {
+ /// <summary>
+ /// Scan has Started
+ /// </summary>
+ event EventHandler ScanStared;
+
+ /// <summary>
+ /// Scan has completed
+ /// </summary>
+ event ScanCompletedStatus ScanCompleted;
+
+ /// <summary>
+ /// Scan process has changed to a new title
+ /// </summary>
+ event ScanProgessStatus ScanStatusChanged;
+
+ /// <summary>
+ /// Gets a value indicating whether IsScanning.
+ /// </summary>
+ bool IsScanning { get; }
+
+ /// <summary>
+ /// Gets the Souce Data.
+ /// </summary>
+ Source SouceData { get; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ string ActivityLog { get; }
+
+ /// <summary>
+ /// Scan a Source Path.
+ /// Title 0: scan all
+ /// </summary>
+ /// <param name="sourcePath">Path to the file to scan</param>
+ /// <param name="title">int title number. 0 for scan all</param>
+ void Scan(string sourcePath, int title);
+
+ /// <summary>
+ /// Kill the scan
+ /// </summary>
+ void Stop();
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs b/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs new file mode 100644 index 000000000..6f200e447 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs @@ -0,0 +1,328 @@ +/* LibScan.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Threading;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Model.Encoding;
+ using HandBrake.ApplicationServices.Parsing;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+ using HandBrake.Interop;
+
+ using AudioTrack = HandBrake.ApplicationServices.Parsing.AudioTrack;
+ using Cropping = HandBrake.ApplicationServices.Model.Encoding.Cropping;
+ using ScanProgressEventArgs = HandBrake.Interop.ScanProgressEventArgs;
+ using Size = System.Drawing.Size;
+
+ /// <summary>
+ /// Scan a Source
+ /// </summary>
+ public class LibScan : IScan
+ {
+ /*
+ * TODO
+ * 1. Expose the Previews code.
+ * 2. Expose the Logging.
+ *
+ */
+
+ #region Private Variables
+
+ /// <summary>
+ /// Lock for the log file
+ /// </summary>
+ static readonly object LogLock = new object();
+
+ /// <summary>
+ /// LibHB Instance
+ /// </summary>
+ private readonly HandBrakeInstance instance;
+
+ /// <summary>
+ /// Log data from HandBrakeInstance
+ /// </summary>
+ private readonly StringBuilder logging;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibScan"/> class.
+ /// </summary>
+ public LibScan()
+ {
+ logging = new StringBuilder();
+
+ instance = new HandBrakeInstance();
+ instance.Initialize(1);
+ instance.ScanProgress += this.InstanceScanProgress;
+ instance.ScanCompleted += this.InstanceScanCompleted;
+
+ HandBrakeUtils.MessageLogged += this.HandBrakeInstanceMessageLogged;
+ HandBrakeUtils.ErrorLogged += this.HandBrakeInstanceErrorLogged;
+ }
+
+ #region Events
+
+ /// <summary>
+ /// Scan has Started
+ /// </summary>
+ public event EventHandler ScanStared;
+
+ /// <summary>
+ /// Scan has completed
+ /// </summary>
+ public event ScanCompletedStatus ScanCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ public event ScanProgessStatus ScanStatusChanged;
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets a value indicating whether IsScanning.
+ /// </summary>
+ public bool IsScanning { get; private set; }
+
+ /// <summary>
+ /// Gets the Souce Data.
+ /// </summary>
+ public Source SouceData { get; private set; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ public string ActivityLog
+ {
+ get
+ {
+ return logging.ToString();
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Scan a Source Path.
+ /// Title 0: scan all
+ /// </summary>
+ /// <param name="sourcePath">Path to the file to scan</param>
+ /// <param name="title">int title number. 0 for scan all</param>
+ public void Scan(string sourcePath, int title)
+ {
+ Thread t = new Thread(unused => this.ScanSource(sourcePath, title));
+ t.Start();
+ }
+
+ /// <summary>
+ /// Kill the scan
+ /// </summary>
+ public void Stop()
+ {
+ instance.StopScan();
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Start a scan for a given source path and title
+ /// </summary>
+ /// <param name="sourcePath">Path to the source file</param>
+ /// <param name="title">the title number to look at</param>
+ private void ScanSource(object sourcePath, int title)
+ {
+ try
+ {
+ IsScanning = true;
+ if (this.ScanStared != null)
+ this.ScanStared(this, new EventArgs());
+
+ if (title != 0)
+ instance.StartScan(sourcePath.ToString(), 10, title);
+ else
+ instance.StartScan(sourcePath.ToString(), 10);
+ }
+ catch (Exception exc)
+ {
+ this.Stop();
+
+ if (this.ScanCompleted != null)
+ this.ScanCompleted(this, new ScanCompletedEventArgs(false, exc, "An Error has occured in ScanService.ScanSource()"));
+ }
+ }
+
+ #endregion
+
+ #region HandBrakeInstance Event Handlers
+ /// <summary>
+ /// Scan Completed Event Handler
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void InstanceScanCompleted(object sender, EventArgs e)
+ {
+ this.SouceData = new Source { Titles = ConvertTitles(this.instance.Titles) };
+
+ IsScanning = false;
+
+ if (this.ScanCompleted != null)
+ this.ScanCompleted(this, new ScanCompletedEventArgs(true, null, string.Empty));
+ }
+
+ /// <summary>
+ /// Scan Progress Event Handler
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void InstanceScanProgress(object sender, ScanProgressEventArgs e)
+ {
+ if (this.ScanStatusChanged != null)
+ {
+ ApplicationServices.EventArgs.ScanProgressEventArgs eventArgs =
+ new ApplicationServices.EventArgs.ScanProgressEventArgs
+ {
+ CurrentTitle = e.CurrentTitle,
+ Titles = e.Titles
+ };
+
+ this.ScanStatusChanged(this, eventArgs);
+ }
+ }
+
+ /// <summary>
+ /// Log a message
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The MessageLoggedEventArgs.
+ /// </param>
+ private void HandBrakeInstanceErrorLogged(object sender, MessageLoggedEventArgs e)
+ {
+ lock (LogLock)
+ {
+ this.logging.AppendLine(e.Message);
+ }
+ }
+
+ /// <summary>
+ /// Log a message
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The MessageLoggedEventArgs.
+ /// </param>
+ private void HandBrakeInstanceMessageLogged(object sender, MessageLoggedEventArgs e)
+ {
+ lock (LogLock)
+ {
+ this.logging.AppendLine(e.Message);
+ }
+ }
+
+ /// <summary>
+ /// Convert Interop Title objects to App Services Title object
+ /// </summary>
+ /// <param name="titles">
+ /// The titles.
+ /// </param>
+ /// <returns>
+ /// The convert titles.
+ /// </returns>
+ private static List<Title> ConvertTitles(IEnumerable<SourceData.Title> titles)
+ {
+ List<Title> titleList = new List<Title>();
+ foreach (SourceData.Title title in titles)
+ {
+ Title converted = new Title
+ {
+ TitleNumber = title.TitleNumber,
+ Duration = title.Duration,
+ Resolution = new Size(title.Resolution.Width, title.Resolution.Height),
+ AspectRatio = title.AspectRatio,
+ AngleCount = title.AngleCount,
+ ParVal = new Size(title.ParVal.Width, title.ParVal.Height),
+ AutoCropDimensions =
+ new Cropping(
+ title.AutoCropDimensions.Top,
+ title.AutoCropDimensions.Bottom,
+ title.AutoCropDimensions.Left,
+ title.AutoCropDimensions.Right),
+ Fps = title.Framerate
+ };
+
+ foreach (SourceData.Chapter chapter in title.Chapters)
+ {
+ converted.Chapters.Add(new Chapter(chapter.ChapterNumber, string.Empty, chapter.Duration));
+ }
+
+ foreach (SourceData.AudioTrack track in title.AudioTracks)
+ {
+ converted.AudioTracks.Add(new AudioTrack(track.TrackNumber, track.Language, track.LanguageCode, track.Description, string.Empty, track.SampleRate, track.Bitrate));
+ }
+
+ foreach (SourceData.Subtitle track in title.Subtitles)
+ {
+ SubtitleType convertedType = new SubtitleType();
+
+ switch (track.SubtitleSource)
+ {
+ case SourceData.SubtitleSource.VobSub:
+ convertedType = SubtitleType.VobSub;
+ break;
+ case SourceData.SubtitleSource.UTF8:
+ convertedType = SubtitleType.UTF8Sub;
+ break;
+ case SourceData.SubtitleSource.TX3G:
+ convertedType = SubtitleType.TX3G;
+ break;
+ case SourceData.SubtitleSource.SSA:
+ convertedType = SubtitleType.SSA;
+ break;
+ case SourceData.SubtitleSource.SRT:
+ convertedType = SubtitleType.SRT;
+ break;
+ case SourceData.SubtitleSource.CC608:
+ convertedType = SubtitleType.CC;
+ break;
+ case SourceData.SubtitleSource.CC708:
+ convertedType = SubtitleType.CC;
+ break;
+ }
+
+ converted.Subtitles.Add(new Subtitle(track.TrackNumber, track.Language, track.LanguageCode, convertedType));
+ }
+
+ titleList.Add(converted);
+ }
+
+ return titleList;
+ }
+ #endregion
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/PresetService.cs b/win/CS/HandBrake.ApplicationServices/Services/PresetService.cs new file mode 100644 index 000000000..f46a8bad1 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/PresetService.cs @@ -0,0 +1,403 @@ +/* PresetService.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.ComponentModel;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Linq;
+ using System.Text.RegularExpressions;
+ using System.Windows.Data;
+ using System.Xml.Serialization;
+
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+
+ /// <summary>
+ /// The preset service manages HandBrake's presets
+ /// </summary>
+ public class PresetService : IPresetService
+ {
+ /**
+ * TODO:
+ * - Wire this into the Forms and WPF UI's
+ * - Note: This is untested so far. It'll likely need fixes before it can be used.
+ * - Maybe change the collection to a dictionary to allow easier lookups?
+ **/
+
+ #region Private Variables
+
+ /// <summary>
+ /// XML Serializer
+ /// </summary>
+ private static readonly XmlSerializer Ser = new XmlSerializer(typeof(List<Preset>));
+
+ /// <summary>
+ /// The User Preset file
+ /// </summary>
+ private readonly string userPresetFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\user_presets.xml";
+
+ /// <summary>
+ /// The Built In Presets File
+ /// </summary>
+ private readonly string builtInPresetFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\presets.xml";
+
+ /// <summary>
+ /// A Collection of presets
+ /// </summary>
+ private ObservableCollection<Preset> presets = new ObservableCollection<Preset>();
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PresetService"/> class.
+ /// </summary>
+ public PresetService()
+ {
+ // this.Presets = CollectionViewSource.GetDefaultView(this.presets);
+ this.LoadPresets();
+ }
+
+ /// <summary>
+ /// Gets or sets a Collection of presets.
+ /// </summary>
+ public ObservableCollection<Preset> Presets
+ {
+ get
+ {
+ return this.presets;
+ }
+
+ set
+ {
+ this.presets = value;
+ }
+ }
+
+ /// <summary>
+ /// The last preset added.
+ /// </summary>
+ public Preset LastPresetAdded { get; set; }
+
+ #region Public Methods
+
+ /// <summary>
+ /// Add a new preset to the system
+ /// </summary>
+ /// <param name="preset">
+ /// A Preset to add
+ /// </param>
+ /// <returns>
+ /// True if added,
+ /// False if name already exists
+ /// </returns>
+ public bool Add(Preset preset)
+ {
+ if (this.CheckIfPresetExists(preset.Name) == false)
+ {
+ this.presets.Add(preset);
+ this.LastPresetAdded = preset;
+
+ // Update the presets file
+ this.UpdatePresetFiles();
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Update a preset
+ /// </summary>
+ /// <param name="update">
+ /// The updated preset
+ /// </param>
+ public void Update(Preset update)
+ {
+ // TODO - Change this to be a lookup
+ foreach (Preset preset in this.presets)
+ {
+ if (preset.Name == update.Name)
+ {
+ preset.Query = update.Query;
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Remove a preset with a given name from either the built in or user preset list.
+ /// </summary>
+ /// <param name="preset">
+ /// The Preset to remove
+ /// </param>
+ public void Remove(Preset preset)
+ {
+ this.presets.Remove(preset);
+ this.UpdatePresetFiles();
+ }
+
+ /// <summary>
+ /// Remove a group of presets by category
+ /// </summary>
+ /// <param name="category">
+ /// The Category to remove
+ /// </param>
+ public void RemoveGroup(string category)
+ {
+ List<Preset> removeList = this.presets.Where(p => p.Category == category).ToList();
+ foreach (Preset preset in removeList)
+ {
+ this.presets.Remove(preset);
+ }
+
+ this.UpdatePresetFiles();
+ }
+
+ /// <summary>
+ /// Get a Preset
+ /// </summary>
+ /// <param name="name">
+ /// The name of the preset to get
+ /// </param>
+ /// <returns>
+ /// A Preset or null object
+ /// </returns>
+ public Preset GetPreset(string name)
+ {
+ return this.presets.FirstOrDefault(item => item.Name == name);
+ }
+
+ /// <summary>
+ /// Clear Built-in Presets
+ /// </summary>
+ public void ClearBuiltIn()
+ {
+ List<Preset> remove = this.presets.Where(p => p.IsBuildIn).ToList();
+ foreach (Preset preset in remove)
+ {
+ this.presets.Remove(preset);
+ }
+ }
+
+ /// <summary>
+ /// Clear all presets
+ /// </summary>
+ public void ClearAll()
+ {
+ this.presets.Clear();
+ }
+
+ /// <summary>
+ /// Reads the CLI's CLI output format and load's them into the preset List Preset
+ /// </summary>
+ /// <param name="cliPath">
+ /// The Path to the CLI, leave blank for current folder.
+ /// </param>
+ public void UpdateBuiltInPresets(string cliPath)
+ {
+ // Create a new tempory file and execute the CLI to get the built in Presets.
+ string handbrakeCLIPath = Path.Combine(cliPath, "HandBrakeCLI.exe");
+ string presetsPath = Path.Combine(Path.GetTempPath(), "temp_presets.dat");
+ string strCmdLine = String.Format(@"cmd /c """"{0}"" --preset-list >""{1}"" 2>&1""", handbrakeCLIPath, presetsPath);
+
+ ProcessStartInfo getPresets = new ProcessStartInfo("CMD.exe", strCmdLine) { WindowStyle = ProcessWindowStyle.Hidden };
+
+ Process hbproc = Process.Start(getPresets);
+ hbproc.WaitForExit();
+ hbproc.Dispose();
+ hbproc.Close();
+
+ // Clear the current built in Presets and now parse the tempory Presets file.
+ this.ClearBuiltIn();
+
+ if (File.Exists(presetsPath))
+ {
+ StreamReader presetInput = new StreamReader(presetsPath);
+
+ string category = String.Empty;
+
+ while (!presetInput.EndOfStream)
+ {
+ string line = presetInput.ReadLine();
+
+ // Found the beginning of a preset block
+ if (line != null && line.Contains("<") && !line.Contains("<<"))
+ {
+ category = line.Replace("<", string.Empty).Trim();
+ }
+
+ // Found a preset
+ if (line != null && line.Contains("+"))
+ {
+ Regex r = new Regex("(: )"); // Split on hyphens.
+ string[] presetName = r.Split(line);
+
+ bool pic = false;
+ if (presetName[2].Contains("crop"))
+ {
+ pic = true;
+ }
+
+ Preset newPreset = new Preset
+ {
+ Category = category,
+ Name = presetName[0].Replace("+", string.Empty).Trim(),
+ Query = presetName[2],
+ Version = Init.Version,
+ CropSettings = pic,
+ Description = string.Empty, // Maybe one day we will populate this.
+ IsBuildIn = true
+ };
+ this.presets.Add(newPreset);
+ }
+ }
+ presetInput.Close();
+ presetInput.Dispose();
+ }
+
+ // Finally, Create a new or update the current Presets.xml file
+ this.UpdatePresetFiles();
+ }
+
+ /// <summary>
+ /// Check if the built in Presets stored are not out of date.
+ /// Update them if they are.
+ /// </summary>
+ /// <returns>true if out of date</returns>
+ public bool CheckIfPresetsAreOutOfDate()
+ {
+ // Update built-in Presets if the built-in Presets belong to an older version.
+ if (this.presets.Count != 0)
+ {
+ if (this.presets[0].Version != Init.Version)
+ {
+ this.UpdateBuiltInPresets(string.Empty);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Check if the preset "name" exists in either Presets or UserPresets lists.
+ /// </summary>
+ /// <param name="name">Name of the preset</param>
+ /// <returns>True if found</returns>
+ public bool CheckIfPresetExists(string name)
+ {
+ return name == string.Empty || this.presets.Any(item => item.Name == name);
+ }
+
+ #endregion
+
+ #region Private Helpers
+
+ /// <summary>
+ /// Recover from a courrpted preset file
+ /// Add .old to the current filename, and delete the current file.
+ /// </summary>
+ /// <param name="file">
+ /// The broken presets file.
+ /// </param>
+ private static void RecoverFromCorruptedPresetFile(string file)
+ {
+ // Recover from Error.
+ if (File.Exists(file))
+ {
+ string disabledFile = file + ".old";
+ File.Move(file, disabledFile);
+ if (File.Exists(file))
+ {
+ File.Delete(file);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Load in the Built-in and User presets into the collection
+ /// </summary>
+ private void LoadPresets()
+ {
+ // First clear the Presets arraylists
+ this.presets.Clear();
+
+ // Load in the users Presets from UserPresets.xml
+ try
+ {
+ if (File.Exists(this.builtInPresetFile))
+ {
+ StreamReader reader = new StreamReader(this.builtInPresetFile);
+ List<Preset> list = (List<Preset>)Ser.Deserialize(reader);
+ foreach (Preset preset in list)
+ {
+ this.presets.Add(preset);
+ }
+
+ reader.Close();
+ }
+ }
+ catch (Exception)
+ {
+ RecoverFromCorruptedPresetFile(this.builtInPresetFile);
+ this.UpdateBuiltInPresets(string.Empty);
+ }
+
+ // Load in the users Presets from UserPresets.xml
+ try
+ {
+ if (File.Exists(this.userPresetFile))
+ {
+ StreamReader reader = new StreamReader(this.userPresetFile);
+ List<Preset> list = (List<Preset>)Ser.Deserialize(reader);
+ foreach (Preset preset in list)
+ {
+ this.presets.Add(preset);
+ }
+
+ reader.Close();
+ }
+ }
+ catch (Exception)
+ {
+ RecoverFromCorruptedPresetFile(this.userPresetFile);
+ }
+ }
+
+ /// <summary>
+ /// Update the preset files
+ /// </summary>
+ private void UpdatePresetFiles()
+ {
+ try
+ {
+ using (FileStream strm = new FileStream(this.builtInPresetFile, FileMode.Create, FileAccess.Write))
+ {
+ Ser.Serialize(strm, this.presets.Where(p => p.IsBuildIn).ToList());
+ strm.Close();
+ strm.Dispose();
+ }
+
+ using (FileStream strm = new FileStream(this.userPresetFile, FileMode.Create, FileAccess.Write))
+ {
+ Ser.Serialize(strm, this.presets.Where(p => p.IsBuildIn == false).ToList());
+ strm.Close();
+ strm.Dispose();
+ }
+ }
+ catch (Exception exc)
+ {
+ throw new Exception("Unable to write to the file. Please make sure the location has the correct permissions for file writing.\n Error Information: \n\n", exc);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/win/CS/HandBrake.ApplicationServices/Services/QueueManager.cs b/win/CS/HandBrake.ApplicationServices/Services/QueueManager.cs new file mode 100644 index 000000000..7f723b8b4 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/QueueManager.cs @@ -0,0 +1,340 @@ +/* QueueManager.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.IO;
+ using System.Linq;
+ using System.Windows.Forms;
+ using System.Xml.Serialization;
+
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+
+ using EventArgs = System.EventArgs;
+
+ /// <summary>
+ /// The Queue Manager.
+ /// Thread Safe.
+ /// </summary>
+ public class QueueManager : IQueueManager
+ {
+ /*
+ * TODO
+ * - Rewrite the batch script generator.
+ * - QueueTask, switch everything to use the Task property, which is a model of all settings.
+ */
+
+ #region Private Variables
+
+ /// <summary>
+ /// A Lock object to maintain thread safety
+ /// </summary>
+ static readonly object QueueLock = new object();
+
+ /// <summary>
+ /// The Queue of Job objects
+ /// </summary>
+ private readonly List<QueueTask> queue = new List<QueueTask>();
+
+ /// <summary>
+ /// HandBrakes Queue file with a place holder for an extra string.
+ /// </summary>
+ private readonly string queueFile;
+
+ /// <summary>
+ /// The ID of the job last added
+ /// </summary>
+ private int lastJobId;
+
+ /// <summary>
+ /// The instance Id of this HandBrake instance.
+ /// </summary>
+ private int instanceId;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="QueueManager"/> class.
+ /// </summary>
+ /// <param name="instanceId">
+ /// The instance Id.
+ /// </param>
+ public QueueManager(int instanceId)
+ {
+ this.instanceId = instanceId;
+
+ // If this is the first instance, just use the main queue file, otherwise add the instance id to the filename.
+ this.queueFile = instanceId == 0 ? "hb_queue_recovery.xml" : string.Format("hb_queue_recovery{0}.xml", instanceId);
+ }
+
+ #region Events
+ /// <summary>
+ /// Fires when a job is Added, Removed or Re-Ordered.
+ /// Should be used for triggering an update of the Queue Window.
+ /// </summary>
+ public event EventHandler QueueChanged;
+
+ /// <summary>
+ /// Invoke the Queue Changed Event
+ /// </summary>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void InvokeQueueChanged(EventArgs e)
+ {
+ try
+ {
+ this.BackupQueue(string.Empty);
+ }
+ catch (Exception)
+ {
+ // Do Nothing.
+ }
+
+ EventHandler handler = this.QueueChanged;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets or sets Last Processed Job.
+ /// This is set when the job is poped of the queue by GetNextJobForProcessing();
+ /// </summary>
+ public QueueTask LastProcessedJob { get; set; }
+
+ /// <summary>
+ /// Gets the number of jobs in the queue;
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return this.queue.Count;
+ }
+ }
+
+ /// <summary>
+ /// Gets The current queue.
+ /// </summary>
+ public ReadOnlyCollection<QueueTask> Queue
+ {
+ get
+ {
+ return this.queue.AsReadOnly();
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Add a job to the Queue.
+ /// This method is Thread Safe.
+ /// </summary>
+ /// <param name="job">
+ /// The encode Job object.
+ /// </param>
+ public void Add(QueueTask job)
+ {
+ lock (QueueLock)
+ {
+ // Tag the job with an ID
+ job.Id = lastJobId++;
+ queue.Add(job);
+ InvokeQueueChanged(EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// Remove a job from the Queue.
+ /// This method is Thread Safe
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ public void Remove(QueueTask job)
+ {
+ lock (QueueLock)
+ {
+ queue.Remove(job);
+ InvokeQueueChanged(EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// Get the first job on the queue for processing.
+ /// This also removes the job from the Queue and sets the LastProcessedJob
+ /// </summary>
+ /// <returns>
+ /// An encode Job object.
+ /// </returns>
+ public QueueTask GetNextJobForProcessing()
+ {
+ if (this.queue.Count > 0)
+ {
+ QueueTask job = this.queue[0];
+ this.LastProcessedJob = job;
+ this.Remove(job); // Remove the item which we are about to pass out.
+
+ return job;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Moves an item up one position in the queue.
+ /// </summary>
+ /// <param name="index">The zero-based location of the job in the queue.</param>
+ public void MoveUp(int index)
+ {
+ if (index > 0)
+ {
+ QueueTask item = queue[index];
+
+ queue.RemoveAt(index);
+ queue.Insert((index - 1), item);
+ }
+
+ this.InvokeQueueChanged(EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Moves an item down one position in the queue.
+ /// </summary>
+ /// <param name="index">The zero-based location of the job in the queue.</param>
+ public void MoveDown(int index)
+ {
+ if (index < this.queue.Count - 1)
+ {
+ QueueTask item = this.queue[index];
+
+ this.queue.RemoveAt(index);
+ this.queue.Insert((index + 1), item);
+ }
+
+ this.InvokeQueueChanged(EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Backup any changes to the queue file
+ /// </summary>
+ /// <param name="exportPath">
+ /// If this is not null or empty, this will be used instead of the standard backup location.
+ /// </param>
+ public void BackupQueue(string exportPath)
+ {
+ string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\");
+ string tempPath = !string.IsNullOrEmpty(exportPath) ? exportPath : appDataPath + string.Format(this.queueFile, string.Empty);
+
+ using (FileStream strm = new FileStream(tempPath, FileMode.Create, FileAccess.Write))
+ {
+ XmlSerializer serializer = new XmlSerializer(typeof(List<QueueTask>));
+ serializer.Serialize(strm, queue);
+ strm.Close();
+ strm.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Restore a Queue from file or from the queue backup file.
+ /// </summary>
+ /// <param name="importPath">
+ /// The import path. String.Empty or null will result in the default file being loaded.
+ /// </param>
+ public void RestoreQueue(string importPath)
+ {
+ string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\");
+ string tempPath = !string.IsNullOrEmpty(importPath) ? importPath : (appDataPath + string.Format(this.queueFile, string.Empty));
+
+ if (File.Exists(tempPath))
+ {
+ using (FileStream strm = new FileStream((!string.IsNullOrEmpty(importPath) ? importPath : tempPath), FileMode.Open, FileAccess.Read))
+ {
+ if (strm.Length != 0)
+ {
+ XmlSerializer serializer = new XmlSerializer(typeof(List<QueueTask>));
+
+ List<QueueTask> list = serializer.Deserialize(strm) as List<QueueTask>;
+
+ if (list != null)
+ foreach (QueueTask item in list)
+ this.queue.Add(item);
+
+ this.InvokeQueueChanged(EventArgs.Empty);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Checks the current queue for an existing instance of the specified destination.
+ /// </summary>
+ /// <param name="destination">The destination of the encode.</param>
+ /// <returns>Whether or not the supplied destination is already in the queue.</returns>
+ public bool CheckForDestinationPathDuplicates(string destination)
+ {
+ return this.queue.Any(checkItem => checkItem.Destination.Contains(destination.Replace("\\\\", "\\")));
+ }
+
+ /// <summary>
+ /// Writes the current state of the queue in the form of a batch (.bat) file.
+ /// </summary>
+ /// <param name="file">
+ /// The location of the file to write the batch file to.
+ /// </param>
+ /// <returns>
+ /// The write batch script to file.
+ /// </returns>
+ public bool WriteBatchScriptToFile(string file)
+ {
+ string queries = string.Empty;
+ foreach (QueueTask queueItem in this.queue)
+ {
+ string qItem = queueItem.Query;
+ string fullQuery = '"' + Application.StartupPath + "\\HandBrakeCLI.exe" + '"' + qItem;
+
+ if (queries == string.Empty)
+ queries = queries + fullQuery;
+ else
+ queries = queries + " && " + fullQuery;
+ }
+ string strCmdLine = queries;
+
+ if (file != string.Empty)
+ {
+ try
+ {
+ // Create a StreamWriter and open the file, Write the batch file query to the file and
+ // Close the stream
+ using (StreamWriter line = new StreamWriter(file))
+ {
+ line.WriteLine(strCmdLine);
+ }
+
+ return true;
+ }
+ catch (Exception exc)
+ {
+ throw new Exception("Unable to write to the file. Please make sure that the location has the correct permissions for file writing.", exc);
+ }
+ }
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/win/CS/HandBrake.ApplicationServices/Services/QueueProcessor.cs b/win/CS/HandBrake.ApplicationServices/Services/QueueProcessor.cs new file mode 100644 index 000000000..130e1fc46 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/QueueProcessor.cs @@ -0,0 +1,279 @@ +/* Queue.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.Diagnostics;
+ using System.Windows.Forms;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Functions;
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+
+ /// <summary>
+ /// The HandBrake Queue
+ /// </summary>
+ public class QueueProcessor : IQueueProcessor
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="QueueProcessor"/> class.
+ /// </summary>
+ /// <param name="queueManager">
+ /// The queue manager.
+ /// </param>
+ /// <param name="encodeService">
+ /// The encode Service.
+ /// </param>
+ /// <exception cref="ArgumentNullException">
+ /// </exception>
+ public QueueProcessor(IQueueManager queueManager, IEncode encodeService)
+ {
+ this.QueueManager = queueManager;
+ this.EncodeService = encodeService;
+
+ if (this.QueueManager == null)
+ {
+ throw new ArgumentNullException("queueManager");
+ }
+
+ if (this.QueueManager == null)
+ {
+ throw new ArgumentNullException("queueManager");
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="QueueProcessor"/> class.
+ /// This call also initializes the Encode and QueueManager services
+ /// </summary>
+ /// <param name="instanceId">
+ /// The instance id.
+ /// </param>
+ public QueueProcessor(int instanceId)
+ {
+ this.EncodeService = new Encode();
+ this.QueueManager = new QueueManager(instanceId);
+ }
+
+ #region Events
+
+ /// <summary>
+ /// Queue Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The QueueProgressEventArgs.
+ /// </param>
+ public delegate void QueueProgressStatus(object sender, QueueProgressEventArgs e);
+
+ /// <summary>
+ /// Fires when the Queue has started
+ /// </summary>
+ public event QueueProgressStatus JobProcessingStarted;
+
+ /// <summary>
+ /// Fires when a pause to the encode queue has been requested.
+ /// </summary>
+ public event EventHandler QueuePaused;
+
+ /// <summary>
+ /// Fires when the entire encode queue has completed.
+ /// </summary>
+ public event EventHandler QueueCompleted;
+
+ /// <summary>
+ /// Invoke the JobProcessingStarted event
+ /// </summary>
+ /// <param name="e">
+ /// The QueueProgressEventArgs.
+ /// </param>
+ private void InvokeJobProcessingStarted(QueueProgressEventArgs e)
+ {
+ QueueProgressStatus handler = this.JobProcessingStarted;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Invoke the QueuePaused event
+ /// </summary>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void InvokeQueuePaused(EventArgs e)
+ {
+ EventHandler handler = this.QueuePaused;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Invoke the QueueCompleted event.
+ /// </summary>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void InvokeQueueCompleted(EventArgs e)
+ {
+ this.IsProcessing = false;
+
+ EventHandler handler = this.QueueCompleted;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets a value indicating whether IsProcessing.
+ /// </summary>
+ public bool IsProcessing { get; private set; }
+
+ /// <summary>
+ /// Gets the IEncodeService instance.
+ /// </summary>
+ public IEncode EncodeService { get; private set; }
+
+ /// <summary>
+ /// Gets the IQueueManager instance.
+ /// </summary>
+ public IQueueManager QueueManager { get; private set; }
+
+ #endregion
+
+ /// <summary>
+ /// Starts encoding the first job in the queue and continues encoding until all jobs
+ /// have been encoded.
+ /// </summary>
+ public void Start()
+ {
+ if (IsProcessing)
+ {
+ throw new Exception("Already Processing the Queue");
+ }
+
+ IsProcessing = true;
+ this.EncodeService.EncodeCompleted += this.EncodeServiceEncodeCompleted;
+ this.ProcessNextJob();
+ }
+
+ /// <summary>
+ /// Requests a pause of the encode queue.
+ /// </summary>
+ public void Pause()
+ {
+ this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted;
+ this.InvokeQueuePaused(EventArgs.Empty);
+ this.IsProcessing = false;
+ }
+
+ /// <summary>
+ /// After an encode is complete, move onto the next job.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeCompletedEventArgs.
+ /// </param>
+ private void EncodeServiceEncodeCompleted(object sender, EncodeCompletedEventArgs e)
+ {
+ // Growl
+ if (Init.GrowlEncode)
+ GrowlCommunicator.Notify("Encode Completed",
+ "Put down that cocktail...\nyour Handbrake encode is done.");
+
+ if (!e.Successful)
+ {
+ this.Pause();
+ MessageBox.Show(e.Exception + e.ErrorInformation);
+ }
+
+ // Handling Log Data
+ this.EncodeService.ProcessLogs(this.QueueManager.LastProcessedJob.Destination);
+
+ // Move onto the next job.
+ this.ProcessNextJob();
+ }
+
+ /// <summary>
+ /// Run through all the jobs on the queue.
+ /// </summary>
+ private void ProcessNextJob()
+ {
+ if (this.EncodeService.IsEncoding || !this.IsProcessing)
+ {
+ // We don't want to try start a second encode, so just return out. The event will trigger the next encode automatically.
+ // Also, we don't want to start a new encode if we are paused.
+ return;
+ }
+
+ QueueTask job = this.QueueManager.GetNextJobForProcessing();
+ if (job != null)
+ {
+ this.EncodeService.Start(job, true);
+ this.InvokeJobProcessingStarted(new QueueProgressEventArgs(job));
+ }
+ else
+ {
+ // No more jobs to process, so unsubscribe the event
+ this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted;
+
+ // Fire the event to tell connected services.
+ this.InvokeQueueCompleted(EventArgs.Empty);
+
+ // Run the After encode completeion work
+ Finish();
+ }
+ }
+
+ /// <summary>
+ /// Perform an action after an encode. e.g a shutdown, standby, restart etc.
+ /// </summary>
+ private static void Finish()
+ {
+ // Growl
+ if (Init.GrowlQueue)
+ GrowlCommunicator.Notify("Queue Completed", "Put down that cocktail...\nyour Handbrake queue is done.");
+
+ // Do something whent he encode ends.
+ switch (Init.CompletionOption)
+ {
+ case "Shutdown":
+ Process.Start("Shutdown", "-s -t 60");
+ break;
+ case "Log off":
+ Win32.ExitWindowsEx(0, 0);
+ break;
+ case "Suspend":
+ Application.SetSuspendState(PowerState.Suspend, true, true);
+ break;
+ case "Hibernate":
+ Application.SetSuspendState(PowerState.Hibernate, true, true);
+ break;
+ case "Lock System":
+ Win32.LockWorkStation();
+ break;
+ case "Quit HandBrake":
+ Application.Exit();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/ScanService.cs b/win/CS/HandBrake.ApplicationServices/Services/ScanService.cs new file mode 100644 index 000000000..7064d9358 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/ScanService.cs @@ -0,0 +1,355 @@ +/* Scan.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Text;
+ using System.Threading;
+ using System.Windows.Forms;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Parsing;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+ using HandBrake.ApplicationServices.Utilities;
+
+ /// <summary>
+ /// Scan a Source
+ /// </summary>
+ public class ScanService : IScan
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// A Lock object
+ /// </summary>
+ private static readonly object locker = new object();
+
+ /// <summary>
+ /// The CLI data parser
+ /// </summary>
+ private Parser readData;
+
+ /// <summary>
+ /// The Log Buffer
+ /// </summary>
+ private StringBuilder logBuffer;
+
+ /// <summary>
+ /// The line number thats been read to in the log file
+ /// </summary>
+ private int logFilePosition;
+
+ /// <summary>
+ /// The Process belonging to the CLI
+ /// </summary>
+ private Process hbProc;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ScanService"/> class.
+ /// </summary>
+ public ScanService()
+ {
+ }
+
+ #region Events
+
+ /// <summary>
+ /// Scan has Started
+ /// </summary>
+ public event EventHandler ScanStared;
+
+ /// <summary>
+ /// Scan has completed
+ /// </summary>
+ public event ScanCompletedStatus ScanCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ public event ScanProgessStatus ScanStatusChanged;
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets a value indicating whether IsScanning.
+ /// </summary>
+ public bool IsScanning { get; private set; }
+
+ /// <summary>
+ /// Gets the Souce Data.
+ /// </summary>
+ public Source SouceData { get; private set; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ public string ActivityLog
+ {
+ get
+ {
+ if (IsScanning)
+ return readData.Buffer.ToString();
+
+ if (logBuffer == null)
+ {
+ ResetLogReader(false);
+ ReadLastScanFile();
+ }
+
+ return logBuffer != null ? logBuffer.ToString() : string.Empty;
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Scan a Source Path.
+ /// Title 0: scan all
+ /// </summary>
+ /// <param name="sourcePath">Path to the file to scan</param>
+ /// <param name="title">int title number. 0 for scan all</param>
+ public void Scan(string sourcePath, int title)
+ {
+ Thread t = new Thread(unused => this.ScanSource(sourcePath, title));
+ t.Start();
+ }
+
+ /// <summary>
+ /// Kill the scan
+ /// </summary>
+ public void Stop()
+ {
+ try
+ {
+ // Try to clean things up as best as possible.
+ if (this.readData != null)
+ {
+ this.readData.OnScanProgress -= this.OnScanProgress;
+ }
+
+ if (hbProc != null && !hbProc.HasExited)
+ hbProc.Kill();
+ }
+ catch
+ {
+ // We don't really need to notify the user of any errors here.
+ }
+ }
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Start a scan for a given source path and title
+ /// </summary>
+ /// <param name="sourcePath">Path to the source file</param>
+ /// <param name="title">the title number to look at</param>
+ private void ScanSource(object sourcePath, int title)
+ {
+ try
+ {
+ IsScanning = true;
+ if (this.ScanStared != null)
+ {
+ this.ScanStared(this, new EventArgs());
+ }
+
+ ResetLogReader(true);
+
+ string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
+ "\\HandBrake\\logs";
+ string dvdInfoPath = Path.Combine(
+ logDir,
+ string.Format("last_scan_log{0}.txt", Init.InstanceId == 0 ? string.Empty : Init.InstanceId.ToString()));
+
+ // Make we don't pick up a stale last_encode_log.txt (and that we have rights to the file)
+ if (File.Exists(dvdInfoPath))
+ {
+ File.Delete(dvdInfoPath);
+ }
+
+ string extraArguments = string.Empty;
+ if (Init.DisableDvdNav)
+ {
+ extraArguments = " --no-dvdnav";
+ }
+
+ if (title > 0)
+ {
+ extraArguments += " --scan ";
+ }
+
+ // Quick fix for "F:\\" style paths. Just get rid of the \\ so the CLI doesn't fall over.
+ // Sould probably clean up the escaping of the strings later.
+ string source = sourcePath.ToString().EndsWith("\\") ? sourcePath.ToString() : "\"" + sourcePath + "\"";
+
+ this.hbProc = new Process
+ {
+ StartInfo =
+ {
+ FileName = handbrakeCLIPath,
+ Arguments = String.Format(@" -i ""{0}"" -t{1} {2} -v ", sourcePath, title, extraArguments),
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ }
+ };
+
+ string command = String.Format(@" -i {0} -t{1} {2} -v ", source, title, extraArguments);
+
+ this.hbProc = new Process
+ {
+ StartInfo =
+ {
+ FileName = handbrakeCLIPath,
+ Arguments = command,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ }
+ };
+
+ // Start the Scan
+ this.hbProc.Start();
+
+ this.readData = new Parser(this.hbProc.StandardError.BaseStream);
+ this.readData.OnScanProgress += this.OnScanProgress;
+ this.SouceData = Source.Parse(this.readData);
+
+ // Write the Buffer out to file.
+ using (StreamWriter scanLog = new StreamWriter(dvdInfoPath))
+ {
+ // Only write the log file to disk if it's less than 100MB.
+ if (this.readData.Buffer.Length < 100000000)
+ {
+ scanLog.WriteLine(UtilityService.CreateCliLogHeader(null));
+ scanLog.Write(this.readData.Buffer);
+ logBuffer.AppendLine(this.readData.Buffer.ToString());
+ }
+ else
+ {
+ throw new Exception(
+ "The Log file has not been written to disk as it has grown above the 100MB limit. This indicates there was a problem during the scan process.");
+ }
+ }
+
+ IsScanning = false;
+
+ if (this.ScanCompleted != null)
+ {
+ this.ScanCompleted(this, new ScanCompletedEventArgs(true, null, string.Empty));
+ }
+ }
+ catch (Exception exc)
+ {
+ this.Stop();
+
+ if (this.ScanCompleted != null)
+ this.ScanCompleted(this, new ScanCompletedEventArgs(false, exc, "An Error has occured in ScanService.ScanSource()"));
+ }
+ }
+
+ /// <summary>
+ /// Read the log file
+ /// </summary>
+ private void ReadLastScanFile()
+ {
+ lock (locker)
+ {
+ // last_encode_log.txt is the primary log file. Since .NET can't read this file whilst the CLI is outputing to it (Not even in read only mode),
+ // we'll need to make a copy of it.
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
+ "\\HandBrake\\logs";
+ string logFile = Path.Combine(logDir, string.Format("last_scan_log{0}.txt", Init.InstanceId == 0 ? string.Empty : Init.InstanceId.ToString()));
+ string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId == 0 ? string.Empty : Init.InstanceId.ToString()));
+
+ try
+ {
+ // Make sure the application readable log file does not already exist. FileCopy fill fail if it does.
+ if (File.Exists(logFile2))
+ File.Delete(logFile2);
+
+ // Copy the log file.
+ if (File.Exists(logFile))
+ File.Copy(logFile, logFile2, true);
+ else
+ {
+ ResetLogReader(true);
+ return;
+ }
+
+ // Start the Reader
+ // Only use text which continues on from the last read line
+ StreamReader sr = new StreamReader(logFile2);
+ string line;
+ int i = 1;
+ while ((line = sr.ReadLine()) != null)
+ {
+ if (i > logFilePosition)
+ {
+ logBuffer.AppendLine(line);
+ logFilePosition++;
+ }
+ i++;
+ }
+ sr.Close();
+ sr.Dispose();
+ }
+ catch (Exception exc)
+ {
+ ResetLogReader(true);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Reset the Log Reader
+ /// </summary>
+ /// <param name="addHeader">
+ /// The add Header.
+ /// </param>
+ private void ResetLogReader(bool addHeader)
+ {
+ logFilePosition = 0;
+ logBuffer = new StringBuilder();
+ if (addHeader)
+ logBuffer.AppendLine(UtilityService.CreateCliLogHeader(null));
+ }
+
+ /// <summary>
+ /// Fire an event when the scan process progresses
+ /// </summary>
+ /// <param name="sender">the sender</param>
+ /// <param name="currentTitle">the current title being scanned</param>
+ /// <param name="titleCount">the total number of titles</param>
+ private void OnScanProgress(object sender, int currentTitle, int titleCount)
+ {
+ ScanProgressEventArgs eventArgs = new ScanProgressEventArgs
+ {
+ CurrentTitle = currentTitle,
+ Titles = titleCount
+ };
+
+ if (this.ScanStatusChanged != null)
+ {
+ this.ScanStatusChanged(this, eventArgs);
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrake.ApplicationServices/Services/UpdateService.cs b/win/CS/HandBrake.ApplicationServices/Services/UpdateService.cs new file mode 100644 index 000000000..5e65021e5 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/UpdateService.cs @@ -0,0 +1,97 @@ +namespace HandBrake.ApplicationServices.Services
+{
+ using System;
+ using System.IO;
+ using System.Net;
+ using System.Threading;
+
+ using HandBrake.ApplicationServices.Model.General;
+ using HandBrake.ApplicationServices.Utilities;
+
+ /// <summary>
+ /// The Update Service
+ /// </summary>
+ public class UpdateService
+ {
+ /// <summary>
+ /// Begins checking for an update to HandBrake.
+ /// </summary>
+ /// <param name="callback">
+ /// The method that will be called when the check is finished.
+ /// </param>
+ /// <param name="debug">
+ /// Whether or not to execute this in debug mode.
+ /// </param>
+ /// <param name="url">
+ /// The url.
+ /// </param>
+ /// <param name="currentBuild">
+ /// The current Build.
+ /// </param>
+ /// <param name="skipBuild">
+ /// The skip Build.
+ /// </param>
+ /// <param name="currentVersion">
+ /// The current Version.
+ /// </param>
+ public static void BeginCheckForUpdates(AsyncCallback callback, bool debug, string url, int currentBuild, int skipBuild, string currentVersion)
+ {
+ ThreadPool.QueueUserWorkItem(delegate
+ {
+ try
+ {
+ // Initialize variables
+ WebRequest request = WebRequest.Create(url);
+ WebResponse response = request.GetResponse();
+ AppcastReader reader = new AppcastReader();
+
+ // Get the data, convert it to a string, and parse it into the AppcastReader
+ reader.GetUpdateInfo(new StreamReader(response.GetResponseStream()).ReadToEnd());
+
+ // Further parse the information
+ string build = reader.Build;
+
+ int latest = int.Parse(build);
+ int current = currentBuild;
+ int skip = skipBuild;
+
+ // If the user wanted to skip this version, don't report the update
+ if (latest == skip)
+ {
+ UpdateCheckInformation info = new UpdateCheckInformation { NewVersionAvailable = false };
+ callback(new UpdateCheckResult(debug, info));
+ return;
+ }
+
+ UpdateCheckInformation info2 = new UpdateCheckInformation
+ {
+ NewVersionAvailable = latest > current,
+ DescriptionUrl = reader.DescriptionUrl,
+ DownloadFile = reader.DownloadFile,
+ Build = reader.Build,
+ Version = reader.Version,
+ };
+ callback(new UpdateCheckResult(debug, info2));
+ }
+ catch (Exception exc)
+ {
+ callback(new UpdateCheckResult(debug, new UpdateCheckInformation { Error = exc }));
+ }
+ });
+ }
+
+ /// <summary>
+ /// End Check for Updates
+ /// </summary>
+ /// <param name="result">
+ /// The result.
+ /// </param>
+ /// <returns>
+ /// Update Check information
+ /// </returns>
+ public static UpdateCheckInformation EndCheckForUpdates(IAsyncResult result)
+ {
+ return ((UpdateCheckResult)result).Result;
+ }
+ }
+}
|