/* EncodeAndQueueHandler.cs $
This file is part of the HandBrake source code.
Homepage: .
It may be used under the terms of the GNU General Public License. */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;
using Handbrake.Functions;
namespace Handbrake.EncodeQueue
{
public class EncodeAndQueueHandler
{
private static XmlSerializer serializer;
private List queue = new List();
private int nextJobId;
#region Event Handlers
///
/// Fires when an encode job has been started.
///
public event EventHandler NewJobStarted;
///
/// Fires when a pause to the encode queue has been requested.
///
public event EventHandler QueuePauseRequested;
///
/// Fires when an encode job has been completed.
///
public event EventHandler CurrentJobCompleted;
///
/// Fires when the entire encode queue has completed.
///
public event EventHandler QueueCompleted;
#endregion
#region Queue
///
/// Gets and removes the next job in the queue.
///
/// The job that was removed from the queue.
private Job GetNextJob()
{
Job job = queue[0];
LastEncode = job;
RemoveJob(0); // Remove the item which we are about to pass out.
WriteQueueStateToFile("hb_queue_recovery.xml");
return job;
}
///
/// Gets the current state of the encode queue.
///
public ReadOnlyCollection CurrentQueue
{
get { return queue.AsReadOnly(); }
}
///
/// Gets the number of items in the queue.
///
public int Count
{
get { return queue.Count; }
}
///
/// Adds an item to the queue.
///
/// The query that will be passed to the HandBrake CLI.
/// The location of the source video.
/// The location where the encoded video will be.
///
public void AddJob(string query, string source, string destination, bool customJob)
{
Job newJob = new Job { Id = nextJobId++, Query = query, Source = source, Destination = destination, CustomQuery = customJob };
queue.Add(newJob);
WriteQueueStateToFile("hb_queue_recovery.xml");
}
///
/// Removes an item from the queue.
///
/// The zero-based location of the job in the queue.
public void RemoveJob(int index)
{
queue.RemoveAt(index);
WriteQueueStateToFile("hb_queue_recovery.xml");
}
///
/// Moves an item up one position in the queue.
///
/// The zero-based location of the job in the queue.
public void MoveUp(int index)
{
if (index > 0)
{
Job item = queue[index];
queue.RemoveAt(index);
queue.Insert((index - 1), item);
}
WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file
}
///
/// Moves an item down one position in the queue.
///
/// The zero-based location of the job in the queue.
public void MoveDown(int index)
{
if (index < queue.Count - 1)
{
Job item = queue[index];
queue.RemoveAt(index);
queue.Insert((index + 1), item);
}
WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file
}
///
/// Writes the current state of the queue to a file.
///
/// The location of the file to write the queue to.
public void WriteQueueStateToFile(string file)
{
string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\hb_queue_recovery.xml");
string tempPath = file == "hb_queue_recovery.xml" ? appDataPath : file;
try
{
using (FileStream strm = new FileStream(tempPath, FileMode.Create, FileAccess.Write))
{
if (serializer == null)
serializer = new XmlSerializer(typeof(List));
serializer.Serialize(strm, queue);
strm.Close();
strm.Dispose();
}
}
catch (Exception)
{
// Any Errors will be out of diskspace/permissions problems.
// Don't report them as they'll annoy the user.
}
}
///
/// Writes the current state of the queue in the form of a batch (.bat) file.
///
/// The location of the file to write the batch file to.
public void WriteBatchScriptToFile(string file)
{
string queries = "";
foreach (Job queue_item in queue)
{
string q_item = queue_item.Query;
string fullQuery = '"' + Application.StartupPath + "\\HandBrakeCLI.exe" + '"' + q_item;
if (queries == string.Empty)
queries = queries + fullQuery;
else
queries = queries + " && " + fullQuery;
}
string strCmdLine = queries;
if (file != "")
{
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);
}
MessageBox.Show("Your batch script has been sucessfully saved.", "Status", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
catch (Exception)
{
MessageBox.Show("Unable to write to the file. Please make sure that the location has the correct permissions for file writing.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
}
///
/// Reads a serialized XML file that represents a queue of encoding jobs.
///
/// The location of the file to read the queue from.
public void LoadQueueFromFile(string file)
{
string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\hb_queue_recovery.xml");
string tempPath = file == "hb_queue_recovery.xml" ? appDataPath : file;
if (File.Exists(tempPath))
{
using (FileStream strm = new FileStream(tempPath, FileMode.Open, FileAccess.Read))
{
if (strm.Length != 0)
{
if (serializer == null)
serializer = new XmlSerializer(typeof(List));
List list = serializer.Deserialize(strm) as List;
if (list != null)
foreach (Job item in list)
queue.Add(item);
if (file != "hb_queue_recovery.xml")
WriteQueueStateToFile("hb_queue_recovery.xml");
}
}
}
}
///
/// Checks the current queue for an existing instance of the specified destination.
///
/// The destination of the encode.
/// Whether or not the supplied destination is already in the queue.
public bool CheckForDestinationDuplicate(string destination)
{
foreach (Job checkItem in queue)
{
if (checkItem.Destination.Contains(destination.Replace("\\\\", "\\")))
return true;
}
return false;
}
#endregion
#region Encoding
///
/// Gets the last encode that was processed.
///
///
public Job LastEncode { get; set; }
///
/// Request Pause
///
public Boolean PauseRequested { get; private set; }
///
/// Starts encoding the first job in the queue and continues encoding until all jobs
/// have been encoded.
///
public void StartEncodeQueue()
{
if (this.Count != 0)
{
if (PauseRequested)
PauseRequested = false;
else
{
PauseRequested = false;
try
{
Thread theQueue = new Thread(startProcess) { IsBackground = true };
theQueue.Start();
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
}
}
}
///
/// Requests a pause of the encode queue.
///
public void RequestPause()
{
PauseRequested = true;
if (QueuePauseRequested != null)
QueuePauseRequested(this, new EventArgs());
}
///
/// Stops the current job.
///
public void EndEncodeJob()
{
CloseCLI();
}
private void startProcess(object state)
{
// Run through each item on the queue
while (this.Count != 0)
{
Job encJob = GetNextJob();
string query = encJob.Query;
WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file
RunCli(query);
if (NewJobStarted != null)
NewJobStarted(this, new EventArgs());
hbProcess.WaitForExit();
AddCLIQueryToLog(encJob);
CopyLog(LastEncode.Destination);
hbProcess.Close();
hbProcess.Dispose();
isEncoding = false;
//Growl
if (Properties.Settings.Default.growlEncode)
GrowlCommunicator.Notify("Encode Completed", "Put down that cocktail...\nyour Handbrake encode is done.");
if (CurrentJobCompleted != null)
CurrentJobCompleted(this, new EventArgs());
while (PauseRequested) // Need to find a better way of doing this.
{
Thread.Sleep(5000);
}
}
LastEncode = new Job();
if (QueueCompleted != null)
QueueCompleted(this, new EventArgs());
// After the encode is done, we may want to shutdown, suspend etc.
AfterEncodeAction();
}
#endregion
#region CLI and Log Handling
public Process hbProcess { get; set; }
public int processID { get; set; }
public IntPtr processHandle { get; set; }
public String currentQuery { get; set; }
public Boolean isEncoding { get; set; }
///
/// Execute a HandBrakeCLI process.
///
/// The CLI Query
public void RunCli(string query)
{
try
{
isEncoding = true;
string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs", "last_encode_log.txt");
string strCmdLine = String.Format(@" /C """"{0}"" {1} 2>""{2}"" """, handbrakeCLIPath, query, logPath);
ProcessStartInfo cliStart = new ProcessStartInfo("CMD.exe", strCmdLine);
if (Properties.Settings.Default.enocdeStatusInGui)
{
cliStart.RedirectStandardOutput = true;
cliStart.UseShellExecute = false;
}
if (Properties.Settings.Default.cli_minimized)
cliStart.WindowStyle = ProcessWindowStyle.Minimized;
Process[] before = Process.GetProcesses(); // Get a list of running processes before starting.
hbProcess = Process.Start(cliStart);
processID = Main.getCliProcess(before);
currentQuery = query;
if (hbProcess != null)
processHandle = hbProcess.MainWindowHandle; // Set the process Handle
// Set the process Priority
Process hbCliProcess = null;
if (processID != -1)
hbCliProcess = Process.GetProcessById(processID);
if (hbCliProcess != null)
switch (Properties.Settings.Default.processPriority)
{
case "Realtime":
hbCliProcess.PriorityClass = ProcessPriorityClass.RealTime;
break;
case "High":
hbCliProcess.PriorityClass = ProcessPriorityClass.High;
break;
case "Above Normal":
hbCliProcess.PriorityClass = ProcessPriorityClass.AboveNormal;
break;
case "Normal":
hbCliProcess.PriorityClass = ProcessPriorityClass.Normal;
break;
case "Low":
hbCliProcess.PriorityClass = ProcessPriorityClass.Idle;
break;
default:
hbCliProcess.PriorityClass = ProcessPriorityClass.BelowNormal;
break;
}
}
catch (Exception exc)
{
MessageBox.Show("It would appear that HandBrakeCLI has not started correctly. You should take a look at the Activity log as it may indicate the reason why.\n\n Detailed Error Information: error occured in runCli()\n\n" + exc, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// Kill the CLI process
///
private void CloseCLI()
{
hbProcess.Kill();
isEncoding = false;
}
///
/// Perform an action after an encode. e.g a shutdown, standby, restart etc.
///
private void AfterEncodeAction()
{
isEncoding = false;
currentQuery = String.Empty;
//Growl
if (Properties.Settings.Default.growlQueue)
GrowlCommunicator.Notify("Queue Completed", "Put down that cocktail...\nyour Handbrake queue is done.");
// Do something whent he encode ends.
switch (Properties.Settings.Default.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;
}
}
///
/// Append the CLI query to the start of the log file.
///
///
private static void AddCLIQueryToLog(Job encJob)
{
string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
string logPath = Path.Combine(logDir, "last_encode_log.txt");
StreamReader reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));
String log = reader.ReadToEnd();
reader.Close();
StreamWriter writer = new StreamWriter(File.Create(logPath));
writer.Write("### CLI Query: " + encJob.Query + "\n\n");
writer.Write("### User Query: " + encJob.CustomQuery + "\n\n");
writer.Write("#########################################\n\n");
writer.WriteLine(log);
writer.Flush();
writer.Close();
}
///
/// Save a copy of the log to the users desired location or a default location
/// if this feature is enabled in options.
///
///
private static void CopyLog(string destination)
{
try
{
string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");
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 (Properties.Settings.Default.saveLogWithVideo)
File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));
// Save a copy of the log file to a user specified location
if (Directory.Exists(Properties.Settings.Default.saveLogPath))
if (Properties.Settings.Default.saveLogPath != String.Empty && Properties.Settings.Default.saveLogToSpecifiedPath)
File.Copy(tempLogFile, Path.Combine(Properties.Settings.Default.saveLogPath, encodeLogFile));
}
catch (Exception exc)
{
MessageBox.Show("Something went a bit wrong trying to copy your log file.\nError Information:\n\n" + exc, "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
}
}