/* Queue.cs $
This file is part of the HandBrake source code.
Homepage: .
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.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;
using HandBrake.ApplicationServices.Functions;
using HandBrake.ApplicationServices.Model;
using HandBrake.ApplicationServices.Services.Interfaces;
///
/// The HandBrake Queue
///
public class Queue : Encode, IQueue
{
///
/// The Queue Job List
///
private readonly List queue = new List();
///
/// An XML Serializer
///
private static XmlSerializer serializer;
///
/// The Next Job ID
///
private int nextJobId;
#region Events
///
/// Fires when the Queue has started
///
public event EventHandler QueueStarted;
///
/// Fires when a job is Added, Removed or Re-Ordered.
/// Should be used for triggering an update of the Queue Window.
///
public event EventHandler QueueListChanged;
///
/// Fires when a pause to the encode queue has been requested.
///
public event EventHandler QueuePauseRequested;
///
/// Fires when the entire encode queue has completed.
///
public event EventHandler QueueCompleted;
#endregion
#region Properties
///
/// Gets or sets the last encode that was processed.
///
///
public QueueTask LastEncode { get; set; }
///
/// Gets a value indicating whether Request Pause
///
public bool Paused { get; private set; }
///
/// Gets the current state of the encode queue.
///
public ReadOnlyCollection CurrentQueue
{
get { return this.queue.AsReadOnly(); }
}
///
/// Gets the number of items in the queue.
///
public int Count
{
get { return this.queue.Count; }
}
#endregion
#region Queue
///
/// Gets and removes the next job in the queue.
///
/// The job that was removed from the queue.
private QueueTask GetNextJob()
{
QueueTask job = this.queue[0];
this.LastEncode = job;
this.Remove(0); // Remove the item which we are about to pass out.
this.SaveQueue();
return job;
}
///
/// Adds an item to the queue.
///
///
/// The query that will be passed to the HandBrake CLI.
///
///
/// The title.
///
///
/// The location of the source video.
///
///
/// The location where the encoded video will be.
///
///
/// Custom job
///
public void Add(string query, int title, string source, string destination, bool customJob)
{
QueueTask newJob = new QueueTask
{
Id = this.nextJobId++,
Title = title,
Query = query,
Source = source,
Destination = destination,
CustomQuery = customJob
};
this.queue.Add(newJob);
this.SaveQueue();
if (this.QueueListChanged != null)
this.QueueListChanged(this, new EventArgs());
}
///
/// Removes an item from the queue.
///
/// The zero-based location of the job in the queue.
public void Remove(int index)
{
this.queue.RemoveAt(index);
this.SaveQueue();
if (this.QueueListChanged != null)
this.QueueListChanged(this, new EventArgs());
}
///
/// Retrieve a job from the queue
///
/// the job id
/// A job for the given index or blank job object
public QueueTask GetJob(int index)
{
if (this.queue.Count >= (index + 1))
return this.queue[index];
return new QueueTask();
}
///
/// 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)
{
QueueTask item = queue[index];
queue.RemoveAt(index);
queue.Insert((index - 1), item);
}
this.SaveQueue(); // Update the queue recovery file
if (this.QueueListChanged != null)
this.QueueListChanged(this, new EventArgs());
}
///
/// 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 < this.queue.Count - 1)
{
QueueTask item = this.queue[index];
this.queue.RemoveAt(index);
this.queue.Insert((index + 1), item);
}
this.SaveQueue(); // Update the queue recovery file
if (this.QueueListChanged != null)
this.QueueListChanged(this, new EventArgs());
}
///
/// Save any updates to the main queue file.
///
public void SaveQueue()
{
string file = Init.InstanceId == 0
? "hb_queue_recovery.xml"
: string.Format("hb_queue_recovery{0}.xml", Init.InstanceId);
this.WriteQueueStateToFile(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\");
string tempPath = file.Contains("hb_queue_recovery") ? appDataPath + file : 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)
{
return;
}
}
///
/// 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.
///
///
/// The write batch script to file.
///
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;
}
///
/// 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\");
string tempPath = file.Contains("hb_queue_recovery") ? appDataPath + file : 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 (QueueTask item in list)
this.queue.Add(item);
if (!file.Contains("hb_queue_recovery"))
this.SaveQueue();
if (this.QueueListChanged != null)
this.QueueListChanged(this, new EventArgs());
}
}
}
}
///
/// 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)
{
return this.queue.Any(checkItem => checkItem.Destination.Contains(destination.Replace("\\\\", "\\")));
}
#endregion
#region Encoding
///
/// Starts encoding the first job in the queue and continues encoding until all jobs
/// have been encoded.
///
public void Start()
{
if (this.Count != 0)
{
if (this.Paused)
this.Paused = false;
else
{
this.Paused = false;
try
{
Thread theQueue = new Thread(this.StartQueue) { IsBackground = true };
theQueue.Start();
if (this.QueueStarted != null)
this.QueueStarted(this, new EventArgs());
}
catch (Exception exc)
{
throw new Exception("Unable to Start Queue", exc);
}
}
}
}
///
/// Requests a pause of the encode queue.
///
public void Pause()
{
this.Paused = true;
if (this.QueuePauseRequested != null)
this.QueuePauseRequested(this, new EventArgs());
}
///
/// Run through all the jobs on the queue.
///
/// Object State
private void StartQueue(object state)
{
// Run through each item on the queue
while (this.Count != 0)
{
QueueTask encJob = this.GetNextJob();
this.SaveQueue(); // Update the queue recovery file
Start(encJob, true);
if (HbProcess == null)
{
return;
}
HbProcess.WaitForExit();
this.CopyLog(this.LastEncode.Destination);
HbProcess.Close();
HbProcess.Dispose();
// Growl
if (Init.GrowlEncode)
GrowlCommunicator.Notify("Encode Completed",
"Put down that cocktail...\nyour Handbrake encode is done.");
while (this.Paused) // Need to find a better way of doing this.
{
Thread.Sleep(2000);
}
}
this.LastEncode = new QueueTask();
if (this.QueueCompleted != null)
this.QueueCompleted(this, new EventArgs());
// After the encode is done, we may want to shutdown, suspend etc.
Finish();
}
///
/// Perform an action after an encode. e.g a shutdown, standby, restart etc.
///
private 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;
}
}
#endregion
}
}