OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / win / C# / Services / Encode.cs
1 /*  Encode.cs $\r
2     This file is part of the HandBrake source code.\r
3     Homepage: <http://handbrake.fr/>.\r
4     It may be used under the terms of the GNU General Public License. */\r
5 \r
6 namespace Handbrake.Services\r
7 {\r
8     using System;\r
9     using System.Diagnostics;\r
10     using System.IO;\r
11     using System.Text;\r
12     using System.Threading;\r
13     using System.Windows.Forms;\r
14     using Functions;\r
15     using Model;\r
16     using Properties;\r
17     using Timer = System.Threading.Timer;\r
18 \r
19     /// <summary>\r
20     /// Class which handles the CLI\r
21     /// </summary>\r
22     public class Encode\r
23     {\r
24         /// <summary>\r
25         /// An Encode Job\r
26         /// </summary>\r
27         private Job job;\r
28 \r
29         /// <summary>\r
30         /// The Log Buffer\r
31         /// </summary>\r
32         private StringBuilder logBuffer;\r
33 \r
34         /// <summary>\r
35         /// The line number thats been read to in the log file\r
36         /// </summary>\r
37         private int logFilePosition;\r
38 \r
39         /// <summary>\r
40         /// A Timer for this window\r
41         /// </summary>\r
42         private Timer windowTimer;\r
43 \r
44         /// <summary>\r
45         /// Fires when a new CLI Job starts\r
46         /// </summary>\r
47         public event EventHandler EncodeStarted;\r
48 \r
49         /// <summary>\r
50         /// Fires when a CLI job finishes.\r
51         /// </summary>\r
52         public event EventHandler EncodeEnded;\r
53 \r
54         /// <summary>\r
55         /// Gets or sets The HB Process\r
56         /// </summary>\r
57         public Process HbProcess { get; set; }\r
58 \r
59         /// <summary>\r
60         /// Gets or sets The Process Handle\r
61         /// </summary>\r
62         public IntPtr ProcessHandle { get; set; }\r
63 \r
64         /// <summary>\r
65         /// Gets or sets a value indicating whether HandBrakeCLI.exe is running\r
66         /// </summary>\r
67         public bool IsEncoding { get; set; }\r
68 \r
69         /// <summary>\r
70         /// Gets or sets the Process ID\r
71         /// </summary>\r
72         public int ProcessID { get; set; }\r
73 \r
74         /// <summary>\r
75         /// Gets ActivityLog.\r
76         /// </summary>\r
77         public string ActivityLog\r
78         {\r
79             get\r
80             {\r
81                 if (logBuffer != null)\r
82                     return logBuffer.ToString();\r
83 \r
84                 return string.Empty;\r
85             }\r
86         }\r
87 \r
88         /// <summary>\r
89         /// Create a preview sample video\r
90         /// </summary>\r
91         /// <param name="query">\r
92         /// The CLI Query\r
93         /// </param>\r
94         public void CreatePreviewSample(string query)\r
95         {\r
96             this.Run(new Job {Query = query});\r
97         }\r
98 \r
99         /// <summary>\r
100         /// Kill the CLI process\r
101         /// </summary>\r
102         public void Stop()\r
103         {\r
104             if (this.HbProcess != null)\r
105                 this.HbProcess.Kill();\r
106 \r
107             Process[] list = Process.GetProcessesByName("HandBrakeCLI");\r
108             foreach (Process process in list)\r
109                 process.Kill();\r
110 \r
111             if (this.EncodeEnded != null)\r
112                 this.EncodeEnded(this, new EventArgs());\r
113 \r
114             IsEncoding = false;\r
115         }\r
116 \r
117         /// <summary>\r
118         /// Attempt to Safely kill a DirectRun() CLI\r
119         /// NOTE: This will not work with a MinGW CLI\r
120         /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html\r
121         /// </summary>\r
122         public void SafelyClose()\r
123         {\r
124             if ((int)this.ProcessHandle == 0)\r
125                 return;\r
126 \r
127             // Allow the CLI to exit cleanly\r
128             Win32.SetForegroundWindow((int)this.ProcessHandle);\r
129             SendKeys.Send("^C");\r
130             SendKeys.Flush();\r
131 \r
132             // HbProcess.StandardInput.AutoFlush = true;\r
133             // HbProcess.StandardInput.WriteLine("^C");\r
134 \r
135             IsEncoding = false;\r
136         }\r
137 \r
138         /// <summary>\r
139         /// Execute a HandBrakeCLI process.\r
140         /// </summary>\r
141         /// <param name="encJob">\r
142         /// The enc Job.\r
143         /// </param>\r
144         protected void Run(Job encJob)\r
145         {\r
146             this.job = encJob;\r
147             try\r
148             {\r
149                 IsEncoding = true;\r
150 \r
151                 ResetLogReader();\r
152 \r
153                 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");\r
154                 string logPath =\r
155                     Path.Combine(\r
156                         Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs",\r
157                         "last_encode_log.txt");\r
158                 string strCmdLine = String.Format(@" /C """"{0}"" {1} 2>""{2}"" """, handbrakeCLIPath, encJob.Query, logPath);\r
159                 var cliStart = new ProcessStartInfo("CMD.exe", strCmdLine);\r
160 \r
161                 if (Settings.Default.enocdeStatusInGui)\r
162                 {\r
163                     cliStart.RedirectStandardOutput = true;\r
164                     cliStart.UseShellExecute = false;\r
165                     if (!Settings.Default.showCliForInGuiEncodeStatus)\r
166                         cliStart.CreateNoWindow = true;\r
167                 }\r
168                 if (Settings.Default.cli_minimized)\r
169                     cliStart.WindowStyle = ProcessWindowStyle.Minimized;\r
170 \r
171                 Process[] before = Process.GetProcesses(); // Get a list of running processes before starting.\r
172                 this.HbProcess = Process.Start(cliStart);\r
173                 this.ProcessID = Main.GetCliProcess(before);\r
174 \r
175                 // Fire the Encode Started Event\r
176                 if (this.EncodeStarted != null)\r
177                     this.EncodeStarted(this, new EventArgs());\r
178 \r
179                 if (this.HbProcess != null)\r
180                     this.ProcessHandle = this.HbProcess.MainWindowHandle; // Set the process Handle\r
181 \r
182                 // Start the Log Monitor\r
183                 windowTimer = new Timer(new TimerCallback(ReadFile), null, 1000, 1000);\r
184 \r
185                 // Set the process Priority\r
186                 Process hbCliProcess = null;\r
187                 if (this.ProcessID != -1)\r
188                     hbCliProcess = Process.GetProcessById(this.ProcessID);\r
189 \r
190                 if (hbCliProcess != null)\r
191                     switch (Settings.Default.processPriority)\r
192                     {\r
193                         case "Realtime":\r
194                             hbCliProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
195                             break;\r
196                         case "High":\r
197                             hbCliProcess.PriorityClass = ProcessPriorityClass.High;\r
198                             break;\r
199                         case "Above Normal":\r
200                             hbCliProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
201                             break;\r
202                         case "Normal":\r
203                             hbCliProcess.PriorityClass = ProcessPriorityClass.Normal;\r
204                             break;\r
205                         case "Low":\r
206                             hbCliProcess.PriorityClass = ProcessPriorityClass.Idle;\r
207                             break;\r
208                         default:\r
209                             hbCliProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
210                             break;\r
211                     }\r
212             }\r
213             catch (Exception exc)\r
214             {\r
215                 MessageBox.Show(\r
216                     "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\nDetailed Error Information: error occured in runCli()\n\n" +\r
217                     exc,\r
218                     "Error",\r
219                     MessageBoxButtons.OK,\r
220                     MessageBoxIcon.Error);\r
221             }\r
222         }\r
223 \r
224         /// <summary>\r
225         /// Function to run the CLI directly rather than via CMD\r
226         /// TODO: Code to handle the Log data has yet to be written.\r
227         /// TODO: Code to handle the % / ETA info has to be written.\r
228         /// </summary>\r
229         /// <param name="query">\r
230         /// The query.\r
231         /// </param>\r
232         protected void DirectRun(string query)\r
233         {\r
234             try\r
235             {\r
236                 if (this.EncodeStarted != null)\r
237                     this.EncodeStarted(this, new EventArgs());\r
238 \r
239                 IsEncoding = true;\r
240                 ResetLogReader();\r
241 \r
242                 // Setup the job\r
243                 string handbrakeCLIPath = Path.Combine(Environment.CurrentDirectory, "HandBrakeCLI.exe");\r
244                 HbProcess = new Process\r
245                                  {\r
246                                      StartInfo =\r
247                                          {\r
248                                              FileName = handbrakeCLIPath,\r
249                                              Arguments = query,\r
250                                              UseShellExecute = false,\r
251                                              RedirectStandardOutput = true,\r
252                                              RedirectStandardError = true,\r
253                                              RedirectStandardInput = true,\r
254                                              CreateNoWindow = false,\r
255                                              WindowStyle = ProcessWindowStyle.Minimized\r
256                                          }\r
257                                  };\r
258 \r
259                 // Setup event handlers for rediected data\r
260                 HbProcess.ErrorDataReceived += new DataReceivedEventHandler(HbProcErrorDataReceived);\r
261                 HbProcess.OutputDataReceived += new DataReceivedEventHandler(HbProcOutputDataReceived);\r
262 \r
263                 // Start the process\r
264                 HbProcess.Start();\r
265 \r
266                 // Setup the asynchronous reading of stdin and stderr\r
267                 HbProcess.BeginErrorReadLine();\r
268                 HbProcess.BeginOutputReadLine();\r
269 \r
270                 // Set the Process Priority);\r
271                 switch (Settings.Default.processPriority)\r
272                 {\r
273                     case "Realtime":\r
274                         HbProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
275                         break;\r
276                     case "High":\r
277                         HbProcess.PriorityClass = ProcessPriorityClass.High;\r
278                         break;\r
279                     case "Above Normal":\r
280                         HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
281                         break;\r
282                     case "Normal":\r
283                         HbProcess.PriorityClass = ProcessPriorityClass.Normal;\r
284                         break;\r
285                     case "Low":\r
286                         HbProcess.PriorityClass = ProcessPriorityClass.Idle;\r
287                         break;\r
288                     default:\r
289                         HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
290                         break;\r
291                 }\r
292 \r
293                 // Set the class items\r
294                 this.ProcessID = HbProcess.Id;\r
295                 this.ProcessHandle = HbProcess.Handle;\r
296             }\r
297             catch (Exception exc)\r
298             {\r
299                 Console.WriteLine(exc);\r
300             }\r
301         }\r
302 \r
303         /// <summary>\r
304         /// Perform an action after an encode. e.g a shutdown, standby, restart etc.\r
305         /// </summary>\r
306         protected void Finish()\r
307         {\r
308             if (this.EncodeEnded != null)\r
309                 this.EncodeEnded(this, new EventArgs());\r
310 \r
311             IsEncoding = false;\r
312 \r
313             if (!IsEncoding)\r
314             {\r
315                 windowTimer.Dispose();\r
316                 ReadFile(null);\r
317             }\r
318 \r
319             // Growl\r
320             if (Settings.Default.growlQueue)\r
321                 GrowlCommunicator.Notify("Queue Completed", "Put down that cocktail...\nyour Handbrake queue is done.");\r
322 \r
323             // Do something whent he encode ends.\r
324             switch (Settings.Default.CompletionOption)\r
325             {\r
326                 case "Shutdown":\r
327                     Process.Start("Shutdown", "-s -t 60");\r
328                     break;\r
329                 case "Log Off":\r
330                     Win32.ExitWindowsEx(0, 0);\r
331                     break;\r
332                 case "Suspend":\r
333                     Application.SetSuspendState(PowerState.Suspend, true, true);\r
334                     break;\r
335                 case "Hibernate":\r
336                     Application.SetSuspendState(PowerState.Hibernate, true, true);\r
337                     break;\r
338                 case "Lock System":\r
339                     Win32.LockWorkStation();\r
340                     break;\r
341                 case "Quit HandBrake":\r
342                     Application.Exit();\r
343                     break;\r
344                 default:\r
345                     break;\r
346             }\r
347         }\r
348 \r
349         /// <summary>\r
350         /// Add the CLI Query to the Log File.\r
351         /// </summary>\r
352         /// <param name="encJob">\r
353         /// The Encode Job Object\r
354         /// </param>\r
355         protected void AddCLIQueryToLog(Job encJob)\r
356         {\r
357             try\r
358             {\r
359                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
360                 string logPath = Path.Combine(logDir, "last_encode_log.txt");\r
361 \r
362                 var reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));\r
363                 string log = reader.ReadToEnd();\r
364                 reader.Close();\r
365 \r
366                 var writer = new StreamWriter(File.Create(logPath));\r
367 \r
368                 writer.WriteLine("### CLI Query: " + encJob.Query);\r
369                 writer.WriteLine("### User Query: " + encJob.CustomQuery);\r
370                 writer.WriteLine("#########################################");\r
371                 writer.WriteLine(log);\r
372                 writer.Flush();\r
373                 writer.Close();\r
374             }\r
375             catch (Exception)\r
376             {\r
377                 return;\r
378             }\r
379         }\r
380 \r
381         /// <summary>\r
382         /// Save a copy of the log to the users desired location or a default location\r
383         /// if this feature is enabled in options.\r
384         /// </summary>\r
385         /// <param name="destination">\r
386         /// The Destination File Path\r
387         /// </param>\r
388         protected void CopyLog(string destination)\r
389         {\r
390             try\r
391             {\r
392                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
393                                 "\\HandBrake\\logs";\r
394                 string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");\r
395 \r
396                 string encodeDestinationPath = Path.GetDirectoryName(destination);\r
397                 string destinationFile = Path.GetFileName(destination);\r
398                 string encodeLogFile = destinationFile + " " +\r
399                                        DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";\r
400 \r
401                 // Make sure the log directory exists.\r
402                 if (!Directory.Exists(logDir))\r
403                     Directory.CreateDirectory(logDir);\r
404 \r
405                 // Copy the Log to HandBrakes log folder in the users applciation data folder.\r
406                 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));\r
407 \r
408                 // Save a copy of the log file in the same location as the enocde.\r
409                 if (Settings.Default.saveLogWithVideo)\r
410                     File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));\r
411 \r
412                 // Save a copy of the log file to a user specified location\r
413                 if (Directory.Exists(Settings.Default.saveLogPath))\r
414                     if (Settings.Default.saveLogPath != String.Empty && Settings.Default.saveLogToSpecifiedPath)\r
415                         File.Copy(tempLogFile, Path.Combine(Settings.Default.saveLogPath, encodeLogFile));\r
416             }\r
417             catch (Exception exc)\r
418             {\r
419                 MessageBox.Show(\r
420                     "Something went a bit wrong trying to copy your log file.\nError Information:\n\n" + exc,\r
421                     "Error",\r
422                     MessageBoxButtons.OK,\r
423                     MessageBoxIcon.Error);\r
424             }\r
425         }\r
426 \r
427         /// <summary>\r
428         /// Read the log file\r
429         /// </summary>\r
430         /// <param name="n">\r
431         /// The object.\r
432         /// </param>\r
433         private void ReadFile(object n)\r
434         {\r
435             lock (logBuffer)\r
436             {\r
437                 // last_encode_log.txt is the primary log file. Since .NET can't read this file whilst the CLI is outputing to it (Not even in read only mode),\r
438                 // we'll need to make a copy of it.\r
439                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
440                 string logFile = Path.Combine(logDir, "last_encode_log.txt");\r
441                 string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");\r
442 \r
443                 try\r
444                 {\r
445                     // Make sure the application readable log file does not already exist. FileCopy fill fail if it does.\r
446                     if (File.Exists(logFile2))\r
447                         File.Delete(logFile2);\r
448 \r
449                     // Copy the log file.\r
450                     if (File.Exists(logFile))\r
451                         File.Copy(logFile, logFile2, true);\r
452                     else\r
453                     {\r
454                         ResetLogReader();\r
455                         return;\r
456                     }\r
457 \r
458                     // Put the Query and User Generated Query Flag on the log.\r
459                     if (logFilePosition == 0)\r
460                     {\r
461                         logBuffer.AppendLine("### CLI Query: " + job.Query);\r
462                         logBuffer.AppendLine("### User Query: " + job.CustomQuery);\r
463                         logBuffer.AppendLine("#########################################");\r
464                     }\r
465 \r
466                     // Start the Reader\r
467                     // Only use text which continues on from the last read line\r
468                     StreamReader sr = new StreamReader(logFile2);\r
469                     string line;\r
470                     int i = 1;\r
471                     while ((line = sr.ReadLine()) != null)\r
472                     {\r
473                         if (i > logFilePosition)\r
474                         {\r
475                             logBuffer.AppendLine(line);\r
476                             logFilePosition++;\r
477                         }\r
478                         i++;\r
479                     }\r
480                     sr.Close();\r
481                     sr.Dispose();\r
482                 }\r
483                 catch (Exception)\r
484                 {\r
485                     ResetLogReader();\r
486                 }\r
487             }\r
488         }\r
489 \r
490         /// <summary>\r
491         /// Reset the Log Reader\r
492         /// </summary>\r
493         private void ResetLogReader()\r
494         {\r
495             logFilePosition = 0;\r
496             logBuffer = new StringBuilder();\r
497         }\r
498 \r
499         /// <summary>\r
500         /// Recieve the Standard Error information and process it\r
501         /// </summary>\r
502         /// <param name="sender">\r
503         /// The Sender Object\r
504         /// </param>\r
505         /// <param name="e">\r
506         /// DataReceived EventArgs\r
507         /// </param>\r
508         private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)\r
509         {\r
510             if (!String.IsNullOrEmpty(e.Data))\r
511             {\r
512                 lock (logBuffer)\r
513                     logBuffer.AppendLine(e.Data);\r
514             }\r
515         }\r
516 \r
517         /// <summary>\r
518         /// Standard Input Data Recieved from the CLI\r
519         /// </summary>\r
520         /// <param name="sender">\r
521         /// The Sender Object\r
522         /// </param>\r
523         /// <param name="e">\r
524         /// DataReceived EventArgs\r
525         /// </param>\r
526         private void HbProcOutputDataReceived(object sender, DataReceivedEventArgs e)\r
527         {\r
528             if (!String.IsNullOrEmpty(e.Data))\r
529             {\r
530                 lock (logBuffer)\r
531                     logBuffer.AppendLine(e.Data);\r
532             }\r
533         }\r
534     }\r
535 }