/* 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.Properties; 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 Job 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 Job GetNextJob() { Job job = this.queue[0]; this.LastEncode = job; this.Remove(0); // Remove the item which we are about to pass out. this.WriteQueueStateToFile("hb_queue_recovery.xml"); 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) { Job newJob = new Job { Id = this.nextJobId++, Title = title, Query = query, Source = source, Destination = destination, CustomQuery = customJob }; this.queue.Add(newJob); this.WriteQueueStateToFile("hb_queue_recovery.xml"); 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.WriteQueueStateToFile("hb_queue_recovery.xml"); 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 Job GetJob(int index) { if (this.queue.Count >= (index + 1)) return this.queue[index]; return new Job(); } /// /// 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 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) { Job item = this.queue[index]; this.queue.RemoveAt(index); this.queue.Insert((index + 1), item); } this.WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file if (this.QueueListChanged != null) this.QueueListChanged(this, new EventArgs()); } /// /// 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) { 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. public bool WriteBatchScriptToFile(string file) { string queries = string.Empty; foreach (Job 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) { Main.ShowExceptiowWindow("Unable to write to the file. Please make sure that the location has the correct permissions for file writing.", exc.ToString()); } } 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\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) this.queue.Add(item); if (file != "hb_queue_recovery.xml") this.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) { 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) { Main.ShowExceptiowWindow("Unable to Start Queue", exc.ToString()); } } } } /// /// 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) { Job encJob = this.GetNextJob(); this.WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file Run(encJob, false); if (HbProcess == null) { return; } HbProcess.WaitForExit(); AddCLIQueryToLog(encJob); this.CopyLog(this.LastEncode.Destination); HbProcess.Close(); HbProcess.Dispose(); // Growl if (Properties.Settings.Default.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 Job(); 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 (Settings.Default.growlQueue) GrowlCommunicator.Notify("Queue Completed", "Put down that cocktail...\nyour Handbrake queue is done."); // Do something whent he encode ends. switch (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; } } #endregion } }