OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / win / C# / EncodeQueue / EncodeAndQueueHandler.cs
1 /*  EncodeAndQueueHandler.cs $\r
2         \r
3            This file is part of the HandBrake source code.\r
4            Homepage: <http://handbrake.fr/>.\r
5            It may be used under the terms of the GNU General Public License. */\r
6 \r
7 using System;\r
8 using System.Collections.Generic;\r
9 using System.Collections.ObjectModel;\r
10 using System.Diagnostics;\r
11 using System.IO;\r
12 using System.Threading;\r
13 using System.Windows.Forms;\r
14 using System.Xml.Serialization;\r
15 using Handbrake.Functions;\r
16 \r
17 namespace Handbrake.EncodeQueue\r
18 {\r
19     public class EncodeAndQueueHandler\r
20     {\r
21         private static XmlSerializer serializer;\r
22         private List<Job> queue = new List<Job>();\r
23         private int nextJobId;\r
24 \r
25         #region Event Handlers\r
26         /// <summary>\r
27         /// Fires when an encode job has been started.\r
28         /// </summary>\r
29         public event EventHandler NewJobStarted;\r
30 \r
31         /// <summary>\r
32         /// Fires when a pause to the encode queue has been requested.\r
33         /// </summary>\r
34         public event EventHandler QueuePauseRequested;\r
35 \r
36         /// <summary>\r
37         /// Fires when an encode job has been completed.\r
38         /// </summary>\r
39         public event EventHandler CurrentJobCompleted;\r
40 \r
41         /// <summary>\r
42         /// Fires when the entire encode queue has completed.\r
43         /// </summary>\r
44         public event EventHandler QueueCompleted;\r
45         #endregion\r
46 \r
47         #region Queue\r
48         /// <summary>\r
49         /// Gets and removes the next job in the queue.\r
50         /// </summary>\r
51         /// <returns>The job that was removed from the queue.</returns>\r
52         private Job GetNextJob()\r
53         {\r
54             Job job = queue[0];\r
55             LastEncode = job;\r
56             RemoveJob(0); // Remove the item which we are about to pass out.\r
57 \r
58             WriteQueueStateToFile("hb_queue_recovery.xml");\r
59 \r
60             return job;\r
61         }\r
62 \r
63         /// <summary>\r
64         /// Gets the current state of the encode queue.\r
65         /// </summary>\r
66         public ReadOnlyCollection<Job> CurrentQueue\r
67         {\r
68             get { return queue.AsReadOnly(); }\r
69         }\r
70 \r
71         /// <summary>\r
72         /// Gets the number of items in the queue.\r
73         /// </summary>\r
74         public int Count\r
75         {\r
76             get { return queue.Count; }\r
77         }\r
78 \r
79         /// <summary>\r
80         /// Adds an item to the queue.\r
81         /// </summary>\r
82         /// <param name="query">The query that will be passed to the HandBrake CLI.</param>\r
83         /// <param name="source">The location of the source video.</param>\r
84         /// <param name="destination">The location where the encoded video will be.</param>\r
85         /// <param name="customJob"></param>\r
86         public void AddJob(string query, string source, string destination, bool customJob)\r
87         {\r
88             Job newJob = new Job { Id = nextJobId++, Query = query, Source = source, Destination = destination, CustomQuery = customJob };\r
89 \r
90             queue.Add(newJob);\r
91             WriteQueueStateToFile("hb_queue_recovery.xml");\r
92         }\r
93 \r
94         /// <summary>\r
95         /// Removes an item from the queue.\r
96         /// </summary>\r
97         /// <param name="index">The zero-based location of the job in the queue.</param>\r
98         public void RemoveJob(int index)\r
99         {\r
100             queue.RemoveAt(index);\r
101             WriteQueueStateToFile("hb_queue_recovery.xml");\r
102         }\r
103 \r
104         /// <summary>\r
105         /// Moves an item up one position in the queue.\r
106         /// </summary>\r
107         /// <param name="index">The zero-based location of the job in the queue.</param>\r
108         public void MoveUp(int index)\r
109         {\r
110             if (index > 0)\r
111             {\r
112                 Job item = queue[index];\r
113 \r
114                 queue.RemoveAt(index);\r
115                 queue.Insert((index - 1), item);\r
116             }\r
117 \r
118             WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file\r
119         }\r
120 \r
121         /// <summary>\r
122         /// Moves an item down one position in the queue.\r
123         /// </summary>\r
124         /// <param name="index">The zero-based location of the job in the queue.</param>\r
125         public void MoveDown(int index)\r
126         {\r
127             if (index < queue.Count - 1)\r
128             {\r
129                 Job item = queue[index];\r
130 \r
131                 queue.RemoveAt(index);\r
132                 queue.Insert((index + 1), item);\r
133             }\r
134 \r
135             WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file\r
136         }\r
137 \r
138         /// <summary>\r
139         /// Writes the current state of the queue to a file.\r
140         /// </summary>\r
141         /// <param name="file">The location of the file to write the queue to.</param>\r
142         public void WriteQueueStateToFile(string file)\r
143         {\r
144             string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\hb_queue_recovery.xml");\r
145             string tempPath = file == "hb_queue_recovery.xml" ? appDataPath : file;\r
146 \r
147             try\r
148             {\r
149                 using (FileStream strm = new FileStream(tempPath, FileMode.Create, FileAccess.Write))\r
150                 {\r
151                     if (serializer == null)\r
152                         serializer = new XmlSerializer(typeof(List<Job>));\r
153                     serializer.Serialize(strm, queue);\r
154                     strm.Close();\r
155                     strm.Dispose();\r
156                 }\r
157             }\r
158             catch (Exception)\r
159             {\r
160                 // Any Errors will be out of diskspace/permissions problems. \r
161                 // Don't report them as they'll annoy the user.\r
162             }\r
163         }\r
164 \r
165         /// <summary>\r
166         /// Writes the current state of the queue in the form of a batch (.bat) file.\r
167         /// </summary>\r
168         /// <param name="file">The location of the file to write the batch file to.</param>\r
169         public void WriteBatchScriptToFile(string file)\r
170         {\r
171             string queries = "";\r
172             foreach (Job queue_item in queue)\r
173             {\r
174                 string q_item = queue_item.Query;\r
175                 string fullQuery = '"' + Application.StartupPath + "\\HandBrakeCLI.exe" + '"' + q_item;\r
176 \r
177                 if (queries == string.Empty)\r
178                     queries = queries + fullQuery;\r
179                 else\r
180                     queries = queries + " && " + fullQuery;\r
181             }\r
182             string strCmdLine = queries;\r
183 \r
184             if (file != "")\r
185             {\r
186                 try\r
187                 {\r
188                     // Create a StreamWriter and open the file, Write the batch file query to the file and \r
189                     // Close the stream\r
190                     using (StreamWriter line = new StreamWriter(file))\r
191                     {\r
192                         line.WriteLine(strCmdLine);\r
193                     }\r
194 \r
195                     MessageBox.Show("Your batch script has been sucessfully saved.", "Status", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);\r
196                 }\r
197                 catch (Exception)\r
198                 {\r
199                     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);\r
200                 }\r
201 \r
202             }\r
203         }\r
204 \r
205         /// <summary>\r
206         /// Reads a serialized XML file that represents a queue of encoding jobs.\r
207         /// </summary>\r
208         /// <param name="file">The location of the file to read the queue from.</param>\r
209         public void LoadQueueFromFile(string file)\r
210         {\r
211             string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\hb_queue_recovery.xml");\r
212             string tempPath = file == "hb_queue_recovery.xml" ? appDataPath : file;\r
213 \r
214             if (File.Exists(tempPath))\r
215             {\r
216                 using (FileStream strm = new FileStream(tempPath, FileMode.Open, FileAccess.Read))\r
217                 {\r
218                     if (strm.Length != 0)\r
219                     {\r
220                         if (serializer == null)\r
221                             serializer = new XmlSerializer(typeof(List<Job>));\r
222 \r
223                         List<Job> list = serializer.Deserialize(strm) as List<Job>;\r
224 \r
225                         if (list != null)\r
226                             foreach (Job item in list)\r
227                                 queue.Add(item);\r
228 \r
229                         if (file != "hb_queue_recovery.xml")\r
230                             WriteQueueStateToFile("hb_queue_recovery.xml");\r
231                     }\r
232                 }\r
233             }\r
234         }\r
235 \r
236         /// <summary>\r
237         /// Checks the current queue for an existing instance of the specified destination.\r
238         /// </summary>\r
239         /// <param name="destination">The destination of the encode.</param>\r
240         /// <returns>Whether or not the supplied destination is already in the queue.</returns>\r
241         public bool CheckForDestinationDuplicate(string destination)\r
242         {\r
243             foreach (Job checkItem in queue)\r
244             {\r
245                 if (checkItem.Destination.Contains(destination.Replace("\\\\", "\\")))\r
246                     return true;\r
247             }\r
248 \r
249             return false;\r
250         }\r
251 \r
252         #endregion\r
253 \r
254         #region Encoding\r
255 \r
256         /// <summary>\r
257         /// Gets the last encode that was processed.\r
258         /// </summary>\r
259         /// <returns></returns> \r
260         public Job LastEncode { get; set; }\r
261 \r
262         /// <summary>\r
263         /// Request Pause\r
264         /// </summary>\r
265         public Boolean PauseRequested { get; private set; }\r
266 \r
267         /// <summary>\r
268         /// Starts encoding the first job in the queue and continues encoding until all jobs\r
269         /// have been encoded.\r
270         /// </summary>\r
271         public void StartEncodeQueue()\r
272         {\r
273             if (this.Count != 0)\r
274             {\r
275                 if (PauseRequested)\r
276                     PauseRequested = false;\r
277                 else\r
278                 {\r
279                     PauseRequested = false;\r
280                     try\r
281                     {\r
282                         Thread theQueue = new Thread(startProcess) { IsBackground = true };\r
283                         theQueue.Start();\r
284                     }\r
285                     catch (Exception exc)\r
286                     {\r
287                         MessageBox.Show(exc.ToString());\r
288                     }\r
289                 }\r
290             }\r
291         }\r
292 \r
293         /// <summary>\r
294         /// Requests a pause of the encode queue.\r
295         /// </summary>\r
296         public void RequestPause()\r
297         {\r
298             PauseRequested = true;\r
299 \r
300             if (QueuePauseRequested != null)\r
301                 QueuePauseRequested(this, new EventArgs());\r
302         }\r
303 \r
304         /// <summary>\r
305         /// Stops the current job.\r
306         /// </summary>\r
307         public void EndEncodeJob()\r
308         {\r
309             CloseCLI();\r
310         }\r
311 \r
312         private void startProcess(object state)\r
313         {\r
314             // Run through each item on the queue\r
315             while (this.Count != 0)\r
316             {\r
317                 Job encJob = GetNextJob();\r
318                 string query = encJob.Query;\r
319                 WriteQueueStateToFile("hb_queue_recovery.xml"); // Update the queue recovery file\r
320 \r
321                 RunCli(query);\r
322 \r
323                 if (NewJobStarted != null)\r
324                     NewJobStarted(this, new EventArgs());\r
325 \r
326                 hbProcess.WaitForExit();\r
327 \r
328                 AddCLIQueryToLog(encJob);\r
329                 CopyLog(LastEncode.Destination);\r
330 \r
331                 hbProcess.Close();\r
332                 hbProcess.Dispose();\r
333 \r
334                 isEncoding = false;\r
335 \r
336                 //Growl\r
337                 if (Properties.Settings.Default.growlEncode)\r
338                     GrowlCommunicator.Notify("Encode Completed", "Put down that cocktail...\nyour Handbrake encode is done.");\r
339 \r
340                 if (CurrentJobCompleted != null)\r
341                     CurrentJobCompleted(this, new EventArgs());\r
342 \r
343                 while (PauseRequested) // Need to find a better way of doing this.\r
344                 {\r
345                     Thread.Sleep(5000);\r
346                 }\r
347             }\r
348             LastEncode = new Job();\r
349 \r
350             if (QueueCompleted != null)\r
351                 QueueCompleted(this, new EventArgs());\r
352 \r
353             // After the encode is done, we may want to shutdown, suspend etc.\r
354             AfterEncodeAction();\r
355         }\r
356 \r
357         #endregion\r
358 \r
359         #region CLI and Log Handling\r
360         public Process hbProcess { get; set; }\r
361         public int processID { get; set; }\r
362         public IntPtr processHandle { get; set; }\r
363         public String currentQuery { get; set; }\r
364         public Boolean isEncoding { get; set; }\r
365 \r
366         /// <summary>\r
367         /// Execute a HandBrakeCLI process.\r
368         /// </summary>\r
369         /// <param name="query">The CLI Query</param>\r
370         public void RunCli(string query)\r
371         {\r
372             try\r
373             {\r
374                 isEncoding = true;\r
375 \r
376                 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");\r
377                 string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs", "last_encode_log.txt");\r
378                 string strCmdLine = String.Format(@" /C """"{0}"" {1} 2>""{2}"" """, handbrakeCLIPath, query, logPath);\r
379                 ProcessStartInfo cliStart = new ProcessStartInfo("CMD.exe", strCmdLine);\r
380 \r
381                 if (Properties.Settings.Default.enocdeStatusInGui)\r
382                 {\r
383                     cliStart.RedirectStandardOutput = true;\r
384                     cliStart.UseShellExecute = false;\r
385                 }\r
386                 if (Properties.Settings.Default.cli_minimized)\r
387                     cliStart.WindowStyle = ProcessWindowStyle.Minimized;\r
388 \r
389                 Process[] before = Process.GetProcesses(); // Get a list of running processes before starting.\r
390                 hbProcess = Process.Start(cliStart);\r
391                 processID = Main.GetCliProcess(before);\r
392                 currentQuery = query;\r
393                 if (hbProcess != null)\r
394                     processHandle = hbProcess.MainWindowHandle; // Set the process Handle\r
395 \r
396                 // Set the process Priority\r
397                 Process hbCliProcess = null;\r
398                 if (processID != -1)\r
399                     hbCliProcess = Process.GetProcessById(processID);\r
400 \r
401                 if (hbCliProcess != null)\r
402                     switch (Properties.Settings.Default.processPriority)\r
403                     {\r
404                         case "Realtime":\r
405                             hbCliProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
406                             break;\r
407                         case "High":\r
408                             hbCliProcess.PriorityClass = ProcessPriorityClass.High;\r
409                             break;\r
410                         case "Above Normal":\r
411                             hbCliProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
412                             break;\r
413                         case "Normal":\r
414                             hbCliProcess.PriorityClass = ProcessPriorityClass.Normal;\r
415                             break;\r
416                         case "Low":\r
417                             hbCliProcess.PriorityClass = ProcessPriorityClass.Idle;\r
418                             break;\r
419                         default:\r
420                             hbCliProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
421                             break;\r
422                     }\r
423             }\r
424             catch (Exception exc)\r
425             {\r
426                 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);\r
427             }\r
428         }\r
429 \r
430         /// <summary>\r
431         /// Kill the CLI process\r
432         /// </summary>\r
433         private void CloseCLI()\r
434         {\r
435             hbProcess.Kill();\r
436             isEncoding = false;\r
437         }\r
438 \r
439         /// <summary>\r
440         /// Perform an action after an encode. e.g a shutdown, standby, restart etc.\r
441         /// </summary>\r
442         private void AfterEncodeAction()\r
443         {\r
444             isEncoding = false;\r
445             currentQuery = String.Empty;\r
446 \r
447             //Growl\r
448             if (Properties.Settings.Default.growlQueue)\r
449                 GrowlCommunicator.Notify("Queue Completed", "Put down that cocktail...\nyour Handbrake queue is done.");\r
450 \r
451             // Do something whent he encode ends.\r
452             switch (Properties.Settings.Default.CompletionOption)\r
453             {\r
454                 case "Shutdown":\r
455                     Process.Start("Shutdown", "-s -t 60");\r
456                     break;\r
457                 case "Log Off":\r
458                     Win32.ExitWindowsEx(0, 0);\r
459                     break;\r
460                 case "Suspend":\r
461                     Application.SetSuspendState(PowerState.Suspend, true, true);\r
462                     break;\r
463                 case "Hibernate":\r
464                     Application.SetSuspendState(PowerState.Hibernate, true, true);\r
465                     break;\r
466                 case "Lock System":\r
467                     Win32.LockWorkStation();\r
468                     break;\r
469                 case "Quit HandBrake":\r
470                     Application.Exit();\r
471                     break;\r
472                 default:\r
473                     break;\r
474             }\r
475         }\r
476 \r
477         /// <summar>\r
478         /// Append the CLI query to the start of the log file.\r
479         /// </summary>\r
480         /// <param name="query"></param>\r
481         private static void AddCLIQueryToLog(Job encJob)\r
482         {\r
483             string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
484             string logPath = Path.Combine(logDir, "last_encode_log.txt");\r
485 \r
486             StreamReader reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));\r
487             String log = reader.ReadToEnd();\r
488             reader.Close();\r
489 \r
490             StreamWriter writer = new StreamWriter(File.Create(logPath));\r
491 \r
492             writer.Write("### CLI Query: " + encJob.Query + "\n\n");\r
493             writer.Write("### User Query: " + encJob.CustomQuery + "\n\n");\r
494             writer.Write("#########################################\n\n");\r
495             writer.WriteLine(log);\r
496             writer.Flush();\r
497             writer.Close();\r
498         }\r
499 \r
500         /// <summary>\r
501         /// Save a copy of the log to the users desired location or a default location\r
502         /// if this feature is enabled in options.\r
503         /// </summary>\r
504         /// <param name="destination"></param>\r
505         private static void CopyLog(string destination)\r
506         {\r
507             try\r
508             {\r
509                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
510                 string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");\r
511 \r
512                 string encodeDestinationPath = Path.GetDirectoryName(destination);\r
513                 String destinationFile = Path.GetFileName(destination);\r
514                 string encodeLogFile = destinationFile + " " + DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";\r
515 \r
516                 // Make sure the log directory exists.\r
517                 if (!Directory.Exists(logDir))\r
518                     Directory.CreateDirectory(logDir);\r
519 \r
520                 // Copy the Log to HandBrakes log folder in the users applciation data folder.\r
521                 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));\r
522 \r
523                 // Save a copy of the log file in the same location as the enocde.\r
524                 if (Properties.Settings.Default.saveLogWithVideo)\r
525                     File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));\r
526 \r
527                 // Save a copy of the log file to a user specified location\r
528                 if (Directory.Exists(Properties.Settings.Default.saveLogPath))\r
529                     if (Properties.Settings.Default.saveLogPath != String.Empty && Properties.Settings.Default.saveLogToSpecifiedPath)\r
530                         File.Copy(tempLogFile, Path.Combine(Properties.Settings.Default.saveLogPath, encodeLogFile));\r
531             }\r
532             catch (Exception exc)\r
533             {\r
534                 MessageBox.Show("Something went a bit wrong trying to copy your log file.\nError Information:\n\n" + exc, "Error",\r
535                                 MessageBoxButtons.OK, MessageBoxIcon.Error);\r
536             }\r
537         }\r
538         #endregion\r
539     }\r
540 }