/* 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 } }