OSDN Git Service

a6da85026ce1681628741a243d0ad53919dcf420
[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 }, true);\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         /// <param name="RequireStandardOuput">\r
152         /// Set to True to show no window and force standard output redirect\r
153         /// </param>\r
154         protected void Run(Job encJob, bool RequireStandardOuput)\r
155         {\r
156             this.job = encJob;\r
157             try\r
158             {\r
159                 ResetLogReader();\r
160                 IsEncoding = true;\r
161 \r
162                 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");\r
163                 string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs", "last_encode_log.txt");\r
164                 string strCmdLine = String.Format(@" /C """"{0}"" {1} 2>""{2}"" """, handbrakeCLIPath, encJob.Query, logPath);\r
165                 var cliStart = new ProcessStartInfo("CMD.exe", strCmdLine);\r
166 \r
167                 if (Settings.Default.enocdeStatusInGui || RequireStandardOuput)\r
168                 {\r
169                     cliStart.RedirectStandardOutput = true;\r
170                     cliStart.UseShellExecute = false;\r
171                     if (!Settings.Default.showCliForInGuiEncodeStatus || RequireStandardOuput)\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                 HbProcess = Process.Start(cliStart);\r
179                 this.processID = Main.GetCliProcess(before);\r
180 \r
181                 if (HbProcess != null)\r
182                     this.processHandle = HbProcess.MainWindowHandle; // Set the process Handle\r
183       \r
184                 // Start the Log Monitor\r
185                 windowTimer = new Timer(new TimerCallback(ReadFile), null, 1000, 1000);\r
186 \r
187                 // Set the process Priority\r
188                 Process hbCliProcess = null;\r
189                 if (this.processID != -1)\r
190                 {\r
191                     hbCliProcess = Process.GetProcessById(this.processID);\r
192                     hbCliProcess.EnableRaisingEvents = true;\r
193                     hbCliProcess.Exited += new EventHandler(HbProcess_Exited);\r
194                 }\r
195 \r
196                 if (hbCliProcess != null)\r
197                     switch (Settings.Default.processPriority)\r
198                     {\r
199                         case "Realtime":\r
200                             hbCliProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
201                             break;\r
202                         case "High":\r
203                             hbCliProcess.PriorityClass = ProcessPriorityClass.High;\r
204                             break;\r
205                         case "Above Normal":\r
206                             hbCliProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
207                             break;\r
208                         case "Normal":\r
209                             hbCliProcess.PriorityClass = ProcessPriorityClass.Normal;\r
210                             break;\r
211                         case "Low":\r
212                             hbCliProcess.PriorityClass = ProcessPriorityClass.Idle;\r
213                             break;\r
214                         default:\r
215                             hbCliProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
216                             break;\r
217                     }\r
218 \r
219                 // Fire the Encode Started Event\r
220                 if (this.EncodeStarted != null)\r
221                     this.EncodeStarted(this, new EventArgs());\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 (!IsEncoding)\r
334             {\r
335                 windowTimer.Dispose();\r
336                 ReadFile(null);\r
337             }\r
338 \r
339             if (this.EncodeEnded != null)\r
340                 this.EncodeEnded(this, new EventArgs());\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 && job.Query != null)\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 }