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