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