/* Main.cs $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ namespace Handbrake.Functions { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Windows.Forms; using System.Xml.Serialization; using HandBrake.ApplicationServices; using HandBrake.ApplicationServices.Exceptions; using HandBrake.ApplicationServices.Extensions; using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Parsing; using HandBrake.ApplicationServices.Services.Interfaces; using HandBrake.ApplicationServices.Utilities; using Handbrake.ToolWindows; /// /// Useful functions which various screens can use. /// public static class Main { /// /// The XML Serializer /// private static readonly XmlSerializer Ser = new XmlSerializer(typeof(List)); /// /// The User Setting Service /// private static readonly IUserSettingService UserSettingService = ServiceManager.UserSettingService; /// /// Set's up the DataGridView on the Chapters tab (frmMain) /// /// /// The currently selected title object. /// This will be used to get chapter names if they exist. /// /// /// The DataGridView Control /// /// /// The chapter End. /// /// /// The chapter naming. /// public static DataGridView ChapterNaming(Title title, DataGridView dataChpt, string chapterEnd) { int i = 0, finish = 0; if (chapterEnd != "Auto") int.TryParse(chapterEnd, out finish); while (i < finish) { string chapterName = string.Empty; if (title != null) { if (title.Chapters.Count <= i && title.Chapters[i] != null) { chapterName = title.Chapters[i].ChapterName; } } int n = dataChpt.Rows.Add(); dataChpt.Rows[n].Cells[0].Value = i + 1; dataChpt.Rows[n].Cells[1].Value = string.IsNullOrEmpty(chapterName) ? "Chapter " + (i + 1) : chapterName; dataChpt.Rows[n].Cells[0].ValueType = typeof(int); dataChpt.Rows[n].Cells[1].ValueType = typeof(string); i++; } return dataChpt; } /// /// Import a CSV file which contains Chapter Names /// /// /// The DataGridView Control /// /// /// The filepath and name /// /// A Populated DataGridView public static DataGridView ImportChapterNames(DataGridView dataChpt, string filename) { IDictionary chapterMap = new Dictionary(); try { using (StreamReader sr = new StreamReader(filename)) { string csv = sr.ReadLine(); while (csv != null) { if (csv.Trim() != string.Empty) { csv = csv.Replace("\\,", ""); string[] contents = csv.Split(','); int chapter; int.TryParse(contents[0], out chapter); chapterMap.Add(chapter, contents[1].Replace("", ",")); } csv = sr.ReadLine(); } } } catch (Exception) { return null; } foreach (DataGridViewRow item in dataChpt.Rows) { string name; chapterMap.TryGetValue((int)item.Cells[0].Value, out name); item.Cells[1].Value = name ?? "Chapter " + item.Cells[0].Value; } return dataChpt; } /// /// Create a CSV file with the data from the Main Window Chapters tab /// /// Main Window /// Path to save the csv file /// True if successful public static void SaveChapterMarkersToCsv(frmMain mainWindow, string filePathName) { try { string csv = string.Empty; foreach (DataGridViewRow row in mainWindow.data_chpt.Rows) { csv += row.Cells[0].Value.ToString(); csv += ","; csv += row.Cells[1].Value.ToString().Replace(",", "\\,"); csv += Environment.NewLine; } StreamWriter file = new StreamWriter(filePathName); file.Write(csv); file.Close(); file.Dispose(); } catch (Exception exc) { throw new GeneralApplicationException("Unable to save the chapter information to csv.", "The file may already be in use by another application.", exc); } } /// /// Function which generates the filename and path automatically based on /// the Source Name, DVD title and DVD Chapters /// /// /// The main Window. /// /// /// The Generated FileName /// public static string AutoName(frmMain mainWindow) { string autoNamePath = string.Empty; if (mainWindow.drp_dvdtitle.Text != "Automatic") { // Get the Source Name and remove any invalid characters string sourceName = Path.GetInvalidFileNameChars().Aggregate(mainWindow.SourceName, (current, character) => current.Replace(character.ToString(), string.Empty)); sourceName = Path.GetFileNameWithoutExtension(sourceName); // Remove Underscores if (UserSettingService.GetUserSetting(UserSettingConstants.AutoNameRemoveUnderscore)) sourceName = sourceName.Replace("_", " "); // Switch to "Title Case" if (UserSettingService.GetUserSetting(UserSettingConstants.AutoNameTitleCase)) sourceName = sourceName.ToTitleCase(); // Get the Selected Title Number string[] titlesplit = mainWindow.drp_dvdtitle.Text.Split(' '); string dvdTitle = titlesplit[0].Replace("Automatic", string.Empty); // Get the Chapter Start and Chapter End Numbers string chapterStart = mainWindow.drop_chapterStart.Text.Replace("Auto", string.Empty); string chapterFinish = mainWindow.drop_chapterFinish.Text.Replace("Auto", string.Empty); string combinedChapterTag = chapterStart; if (chapterFinish != chapterStart && chapterFinish != string.Empty) combinedChapterTag = chapterStart + "-" + chapterFinish; /* * File Name */ string destinationFilename; if (UserSettingService.GetUserSetting(UserSettingConstants.AutoNameFormat) != string.Empty) { destinationFilename = UserSettingService.GetUserSetting(UserSettingConstants.AutoNameFormat); destinationFilename = destinationFilename.Replace("{source}", sourceName) .Replace("{title}", dvdTitle) .Replace("{chapters}", combinedChapterTag) .Replace("{date}", DateTime.Now.Date.ToShortDateString().Replace('/', '-')); } else destinationFilename = sourceName + "_T" + dvdTitle + "_C" + combinedChapterTag; /* * File Extension */ if (mainWindow.drop_format.SelectedIndex == 0) { switch (UserSettingService.GetUserSetting(UserSettingConstants.UseM4v)) { case 0: // Automatic destinationFilename += mainWindow.Check_ChapterMarkers.Checked || mainWindow.AudioSettings.RequiresM4V() || mainWindow.Subtitles.RequiresM4V() ? ".m4v" : ".mp4"; break; case 1: // Always MP4 destinationFilename += ".mp4"; break; case 2: // Always M4V destinationFilename += ".m4v"; break; } } else if (mainWindow.drop_format.SelectedIndex == 1) destinationFilename += ".mkv"; /* * File Destination Path */ // If there is an auto name path, use it... if (UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath).Trim().StartsWith("{source_path}") && !string.IsNullOrEmpty(mainWindow.sourcePath)) { string savedPath = UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath).Trim().Replace("{source_path}\\", string.Empty).Replace("{source_path}", string.Empty); string directory = Directory.Exists(mainWindow.sourcePath) ? mainWindow.sourcePath : Path.GetDirectoryName(mainWindow.sourcePath); string requestedPath = Path.Combine(directory, savedPath); autoNamePath = Path.Combine(requestedPath, destinationFilename); if (autoNamePath == mainWindow.sourcePath) { // Append out_ to files that already exist or is the source file autoNamePath = Path.Combine(Path.GetDirectoryName(mainWindow.sourcePath), "output_" + destinationFilename); } } else if (UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath).Contains("{source_folder_name}") && !string.IsNullOrEmpty(mainWindow.sourcePath)) { // Second Case: We have a Path, with "{source_folder}" in it, therefore we need to replace it with the folder name from the source. string path = Path.GetDirectoryName(mainWindow.sourcePath); if (!string.IsNullOrEmpty(path)) { string[] filesArray = path.Split(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); string sourceFolder = filesArray[filesArray.Length - 1]; autoNamePath = Path.Combine(UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath).Replace("{source_folder_name}", sourceFolder), destinationFilename); } } else if (!mainWindow.text_destination.Text.Contains(Path.DirectorySeparatorChar.ToString())) { // Third case: If the destination box doesn't already contain a path, make one. if (UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath).Trim() != string.Empty && UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath).Trim() != "Click 'Browse' to set the default location") { autoNamePath = Path.Combine(UserSettingService.GetUserSetting(UserSettingConstants.AutoNamePath), destinationFilename); } else // ...otherwise, output to the source directory autoNamePath = null; } else // Otherwise, use the path that is already there. { // Use the path and change the file extension to match the previous destination autoNamePath = Path.Combine(Path.GetDirectoryName(mainWindow.text_destination.Text), destinationFilename); } } return autoNamePath; } /// /// Get's HandBrakes version data from the CLI. /// public static void SetCliVersionData() { string line; // 0 = SVN Build / Version // 1 = Build Date // Get the SHA1 Hash of HandBrakeCLI byte[] hash; using (Stream stream = File.OpenRead(Path.Combine(Application.StartupPath, "HandBrakeCLI.exe"))) { hash = SHA1.Create().ComputeHash(stream); } string base64Hash = Convert.ToBase64String(hash); // Compare the hash with the last known hash. If it's the same, return. if (UserSettingService.GetUserSetting(ASUserSettingConstants.HandBrakeExeHash) == base64Hash) { return; } // It's not the same, so start the CLI to get it's version data. Process cliProcess = new Process(); ProcessStartInfo handBrakeCli = new ProcessStartInfo("HandBrakeCLI.exe", " -u -v0") { UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, CreateNoWindow = true }; cliProcess.StartInfo = handBrakeCli; try { cliProcess.Start(); // Retrieve standard output and report back to parent thread until the process is complete TextReader stdOutput = cliProcess.StandardError; while (!cliProcess.HasExited) { line = stdOutput.ReadLine() ?? string.Empty; Match m = Regex.Match(line, @"HandBrake ([svnM0-9.]*) \(([0-9]*)\)"); Match platform = Regex.Match(line, @"- ([A-Za-z0-9\s ]*) -"); if (m.Success) { string version = m.Groups[1].Success ? m.Groups[1].Value : string.Empty; string build = m.Groups[2].Success ? m.Groups[2].Value : string.Empty; int buildValue; int.TryParse(build, out buildValue); UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakeBuild, buildValue); UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakeVersion, version); } if (platform.Success) { UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakePlatform, platform.Value.Replace("-", string.Empty).Trim()); } if (cliProcess.TotalProcessorTime.Seconds > 10) // Don't wait longer than 10 seconds. { Process cli = Process.GetProcessById(cliProcess.Id); if (!cli.HasExited) { cli.Kill(); } } } UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakeExeHash, base64Hash); } catch (Exception e) { UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakeBuild, string.Empty); UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakePlatform, string.Empty); UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakeVersion, string.Empty); UserSettingService.SetUserSetting(ASUserSettingConstants.HandBrakeExeHash, string.Empty); ExceptionWindow window = new ExceptionWindow(); window.Setup("Unable to Initialise HandBrake \nThis error is unrecoverable. Maybe try restarting.", e.ToString()); window.ShowDialog(); Application.Exit(); } } /// /// Check if the queue recovery file contains records. /// If it does, it means the last queue did not complete before HandBrake closed. /// So, return a boolean if true. /// /// /// True if there is a queue to recover. /// public static List CheckQueueRecovery() { try { string tempPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\"); List queueFiles = new List(); List removeFiles = new List(); DirectoryInfo info = new DirectoryInfo(tempPath); IEnumerable logFiles = info.GetFiles("*.xml").Where(f => f.Name.StartsWith("hb_queue_recovery")); foreach (FileInfo file in logFiles) { using (FileStream strm = new FileStream(file.FullName, FileMode.Open, FileAccess.Read)) { List list = Ser.Deserialize(strm) as List; if (list != null && list.Count == 0) { removeFiles.Add(file.FullName); } if (list != null && list.Count != 0) { List tasks = list.Where(l => l.Status != QueueItemStatus.Completed).ToList(); if (tasks.Count != 0) { queueFiles.Add(file.Name); } } } } // Cleanup old/unused queue files for now. if (!GeneralUtilities.IsMultiInstance) { foreach (string file in removeFiles) { File.Delete(file); } } return queueFiles; } catch (Exception exc) { return new List(); // Keep quiet about the error. } } /// /// Recover a queue from file. /// /// /// The encode Queue. /// public static void RecoverQueue(IQueueProcessor encodeQueue) { string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\"); DialogResult result = DialogResult.None; List queueFiles = CheckQueueRecovery(); if (queueFiles.Count == 1) { result = MessageBox.Show( "HandBrake has detected unfinished items on the queue from the last time the application was launched. Would you like to recover these?", "Queue Recovery Possible", MessageBoxButtons.YesNo, MessageBoxIcon.Question); } else if (queueFiles.Count > 1) { result = MessageBox.Show( "HandBrake has detected multiple unfinished queue files. These will be from multiple instances of HandBrake running. Would you like to recover all unfinished jobs?", "Queue Recovery Possible", MessageBoxButtons.YesNo, MessageBoxIcon.Question); } if (result == DialogResult.Yes) { foreach (string file in queueFiles) { encodeQueue.QueueManager.RestoreQueue(appDataPath + file); // Start Recovery } } else { if (GeneralUtilities.IsMultiInstance) return; // Don't tamper with the files if we are multi instance foreach (string file in queueFiles) { if (File.Exists(Path.Combine(appDataPath, file))) File.Delete(Path.Combine(appDataPath, file)); } } } /// /// Get The Source from the CLI Query /// /// Full CLI Query /// The Source Path public static string GetSourceFromQuery(string query) { int startIndex = query.IndexOf("-i \""); if (startIndex != -1) { string input = query.Substring(startIndex).Replace("-i \"", string.Empty).Trim(); int closeIndex = input.IndexOf('"'); return closeIndex == -1 ? "Unknown" : input.Substring(0, closeIndex); } return "Unknown"; } /// /// Get the Destination from the CLI Query /// /// Full CLI Query /// The Destination path public static string GetDestinationFromQuery(string query) { int startIndex = query.IndexOf("-o \""); if (startIndex != -1) { string output = query.Substring(startIndex).Replace("-o \"", string.Empty).Trim(); int closeIndex = output.IndexOf('"'); return closeIndex == -1 ? "Unknown" : output.Substring(0, closeIndex); } return "Unknown"; } } }