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