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
6 namespace Handbrake.Functions
\r
9 using System.Collections.Generic;
\r
10 using System.Diagnostics;
\r
15 using System.Text.RegularExpressions;
\r
16 using System.Threading;
\r
17 using System.Windows.Forms;
\r
18 using System.Xml.Serialization;
\r
20 using HandBrake.Framework.Services;
\r
21 using HandBrake.Framework.Services.Interfaces;
\r
22 using HandBrake.ApplicationServices.Model;
\r
23 using HandBrake.ApplicationServices.Parsing;
\r
24 using HandBrake.ApplicationServices.Services.Interfaces;
\r
26 using HandBrake.ApplicationServices.Services;
\r
29 /// Useful functions which various screens can use.
\r
31 public static class Main
\r
34 /// The Error Service
\r
36 private static readonly IErrorService errorService = new ErrorService();
\r
39 /// The XML Serializer
\r
41 private static readonly XmlSerializer Ser = new XmlSerializer(typeof(List<Job>));
\r
44 /// Calculate the duration of the selected title and chapters
\r
46 /// <param name="chapterStart">
\r
47 /// The chapter Start.
\r
49 /// <param name="chapterEnd">
\r
50 /// The chapter End.
\r
52 /// <param name="selectedTitle">
\r
53 /// The selected Title.
\r
56 /// The calculated duration.
\r
58 public static TimeSpan CalculateDuration(int chapterStart, int chapterEnd, Title selectedTitle)
\r
60 TimeSpan duration = TimeSpan.FromSeconds(0.0);
\r
63 if (chapterStart != 0 && chapterEnd != 0 && chapterEnd <= selectedTitle.Chapters.Count)
\r
65 for (int i = chapterStart; i <= chapterEnd; i++)
\r
66 duration += selectedTitle.Chapters[i - 1].Duration;
\r
73 /// Set's up the DataGridView on the Chapters tab (frmMain)
\r
75 /// <param name="dataChpt">
\r
76 /// The DataGridView Control
\r
78 /// <param name="chapterEnd">
\r
79 /// The chapter End.
\r
82 /// The chapter naming.
\r
84 public static DataGridView ChapterNaming(DataGridView dataChpt, string chapterEnd)
\r
86 int i = 0, finish = 0;
\r
88 if (chapterEnd != "Auto")
\r
89 int.TryParse(chapterEnd, out finish);
\r
93 int n = dataChpt.Rows.Add();
\r
94 dataChpt.Rows[n].Cells[0].Value = i + 1;
\r
95 dataChpt.Rows[n].Cells[1].Value = "Chapter " + (i + 1);
\r
96 dataChpt.Rows[n].Cells[0].ValueType = typeof(int);
\r
97 dataChpt.Rows[n].Cells[1].ValueType = typeof(string);
\r
105 /// Import a CSV file which contains Chapter Names
\r
107 /// <param name="dataChpt">
\r
108 /// The DataGridView Control
\r
110 /// <param name="filename">
\r
111 /// The filepath and name
\r
113 /// <returns>A Populated DataGridView</returns>
\r
114 public static DataGridView ImportChapterNames(DataGridView dataChpt, string filename)
\r
116 IDictionary<int, string> chapterMap = new Dictionary<int, string>();
\r
119 StreamReader sr = new StreamReader(filename);
\r
120 string csv = sr.ReadLine();
\r
121 while (csv != null)
\r
123 if (csv.Trim() != string.Empty)
\r
125 csv = csv.Replace("\\,", "<!comma!>");
\r
126 string[] contents = csv.Split(',');
\r
128 int.TryParse(contents[0], out chapter);
\r
129 chapterMap.Add(chapter, contents[1].Replace("<!comma!>", ","));
\r
131 csv = sr.ReadLine();
\r
139 foreach (DataGridViewRow item in dataChpt.Rows)
\r
142 chapterMap.TryGetValue((int)item.Cells[0].Value, out name);
\r
143 item.Cells[1].Value = name ?? "Chapter " + item.Cells[0].Value;
\r
150 /// Create a CSV file with the data from the Main Window Chapters tab
\r
152 /// <param name="mainWindow">Main Window</param>
\r
153 /// <param name="filePathName">Path to save the csv file</param>
\r
154 /// <returns>True if successful </returns>
\r
155 public static bool SaveChapterMarkersToCsv(frmMain mainWindow, string filePathName)
\r
159 string csv = string.Empty;
\r
161 foreach (DataGridViewRow row in mainWindow.data_chpt.Rows)
\r
163 csv += row.Cells[0].Value.ToString();
\r
165 csv += row.Cells[1].Value.ToString().Replace(",", "\\,");
\r
166 csv += Environment.NewLine;
\r
168 StreamWriter file = new StreamWriter(filePathName);
\r
174 catch (Exception exc)
\r
176 ShowExceptiowWindow("Unable to save Chapter Makrers file! \nChapter marker names will NOT be saved in your encode", exc.ToString());
\r
182 /// Function which generates the filename and path automatically based on
\r
183 /// the Source Name, DVD title and DVD Chapters
\r
185 /// <param name="mainWindow">
\r
186 /// The main Window.
\r
189 /// The Generated FileName
\r
191 public static string AutoName(frmMain mainWindow)
\r
193 string autoNamePath = string.Empty;
\r
194 if (mainWindow.drp_dvdtitle.Text != "Automatic")
\r
196 // Get the Source Name and remove any invalid characters
\r
197 string sourceName = Path.GetInvalidFileNameChars().Aggregate(mainWindow.SourceName, (current, character) => current.Replace(character.ToString(), string.Empty));
\r
199 if (Properties.Settings.Default.AutoNameRemoveUnderscore)
\r
200 sourceName = sourceName.Replace("_", " ");
\r
202 if (Properties.Settings.Default.AutoNameTitleCase)
\r
203 sourceName = TitleCase(sourceName);
\r
205 // Get the Selected Title Number
\r
206 string[] titlesplit = mainWindow.drp_dvdtitle.Text.Split(' ');
\r
207 string dvdTitle = titlesplit[0].Replace("Automatic", string.Empty);
\r
209 // Get the Chapter Start and Chapter End Numbers
\r
210 string chapterStart = mainWindow.drop_chapterStart.Text.Replace("Auto", string.Empty);
\r
211 string chapterFinish = mainWindow.drop_chapterFinish.Text.Replace("Auto", string.Empty);
\r
212 string combinedChapterTag = chapterStart;
\r
213 if (chapterFinish != chapterStart && chapterFinish != string.Empty)
\r
214 combinedChapterTag = chapterStart + "-" + chapterFinish;
\r
216 // Get the destination filename.
\r
217 string destinationFilename;
\r
218 if (Properties.Settings.Default.autoNameFormat != string.Empty)
\r
220 destinationFilename = Properties.Settings.Default.autoNameFormat;
\r
221 destinationFilename = destinationFilename.Replace("{source}", sourceName)
\r
222 .Replace("{title}", dvdTitle)
\r
223 .Replace("{chapters}", combinedChapterTag);
\r
226 destinationFilename = sourceName + "_T" + dvdTitle + "_C" + combinedChapterTag;
\r
228 // Add the appropriate file extension
\r
229 if (mainWindow.drop_format.SelectedIndex == 0)
\r
231 destinationFilename += Properties.Settings.Default.useM4v || mainWindow.Check_ChapterMarkers.Checked ||
\r
232 mainWindow.AudioSettings.RequiresM4V() || mainWindow.Subtitles.RequiresM4V()
\r
236 else if (mainWindow.drop_format.SelectedIndex == 1)
\r
237 destinationFilename += ".mkv";
\r
239 // Now work out the path where the file will be stored.
\r
240 // First case: If the destination box doesn't already contain a path, make one.
\r
241 if (!mainWindow.text_destination.Text.Contains(Path.DirectorySeparatorChar.ToString()))
\r
243 // If there is an auto name path, use it...
\r
244 if (Properties.Settings.Default.autoNamePath.Trim() == "{source_path}" && !string.IsNullOrEmpty(mainWindow.sourcePath))
\r
246 autoNamePath = Path.Combine(Path.GetDirectoryName(mainWindow.sourcePath), destinationFilename);
\r
247 if (autoNamePath == mainWindow.sourcePath)
\r
249 // Append out_ to files that already exist or is the source file
\r
250 autoNamePath = Path.Combine(Path.GetDirectoryName(mainWindow.sourcePath), "output_" + destinationFilename);
\r
253 else if (Properties.Settings.Default.autoNamePath.Trim() != string.Empty && Properties.Settings.Default.autoNamePath.Trim() != "Click 'Browse' to set the default location")
\r
255 autoNamePath = Path.Combine(Properties.Settings.Default.autoNamePath, destinationFilename);
\r
257 else // ...otherwise, output to the source directory
\r
258 autoNamePath = null;
\r
260 else // Otherwise, use the path that is already there.
\r
262 // Use the path and change the file extension to match the previous destination
\r
263 autoNamePath = Path.Combine(Path.GetDirectoryName(mainWindow.text_destination.Text), destinationFilename);
\r
265 if (Path.HasExtension(mainWindow.text_destination.Text))
\r
266 autoNamePath = Path.ChangeExtension(autoNamePath,
\r
267 Path.GetExtension(mainWindow.text_destination.Text));
\r
271 return autoNamePath;
\r
275 /// Get's HandBrakes version data from the CLI.
\r
277 public static void SetCliVersionData()
\r
281 // 0 = SVN Build / Version
\r
283 DateTime lastModified = File.GetLastWriteTime("HandBrakeCLI.exe");
\r
285 if (Properties.Settings.Default.hb_build != 0 && Properties.Settings.Default.cliLastModified == lastModified)
\r
290 Properties.Settings.Default.cliLastModified = lastModified;
\r
292 Process cliProcess = new Process();
\r
293 ProcessStartInfo handBrakeCli = new ProcessStartInfo("HandBrakeCLI.exe", " -u -v0")
\r
295 UseShellExecute = false,
\r
296 RedirectStandardError = true,
\r
297 RedirectStandardOutput = true,
\r
298 CreateNoWindow = true
\r
300 cliProcess.StartInfo = handBrakeCli;
\r
304 cliProcess.Start();
\r
306 // Retrieve standard output and report back to parent thread until the process is complete
\r
307 TextReader stdOutput = cliProcess.StandardError;
\r
309 while (!cliProcess.HasExited)
\r
311 line = stdOutput.ReadLine() ?? string.Empty;
\r
312 Match m = Regex.Match(line, @"HandBrake ([svnM0-9.]*) \(([0-9]*)\)");
\r
313 Match platform = Regex.Match(line, @"- ([A-Za-z0-9\s ]*) -");
\r
317 string version = m.Groups[1].Success ? m.Groups[1].Value : string.Empty;
\r
318 string build = m.Groups[2].Success ? m.Groups[2].Value : string.Empty;
\r
321 int.TryParse(build, out buildValue);
\r
323 Properties.Settings.Default.hb_build = buildValue;
\r
324 Properties.Settings.Default.hb_version = version;
\r
327 if (platform.Success)
\r
329 Properties.Settings.Default.hb_platform = platform.Value.Replace("-", string.Empty).Trim();
\r
332 if (cliProcess.TotalProcessorTime.Seconds > 10) // Don't wait longer than 10 seconds.
\r
334 Process cli = Process.GetProcessById(cliProcess.Id);
\r
335 if (!cli.HasExited)
\r
342 Properties.Settings.Default.Save();
\r
344 catch (Exception e)
\r
346 Properties.Settings.Default.hb_build = 0;
\r
347 Properties.Settings.Default.Save();
\r
349 ShowExceptiowWindow("Unable to retrieve version information from the CLI.", e.ToString());
\r
354 /// Check if the queue recovery file contains records.
\r
355 /// If it does, it means the last queue did not complete before HandBrake closed.
\r
356 /// So, return a boolean if true.
\r
359 /// True if there is a queue to recover.
\r
361 public static List<string> CheckQueueRecovery()
\r
365 string tempPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\");
\r
366 List<string> queueFiles = new List<string>();
\r
368 DirectoryInfo info = new DirectoryInfo(tempPath);
\r
369 FileInfo[] logFiles = info.GetFiles("*.xml");
\r
370 foreach (FileInfo file in logFiles)
\r
372 if (!file.Name.Contains("hb_queue_recovery"))
\r
375 using (FileStream strm = new FileStream(Path.Combine(file.DirectoryName, file.Name), FileMode.Open, FileAccess.Read))
\r
377 List<Job> list = Ser.Deserialize(strm) as List<Job>;
\r
380 if (list.Count != 0)
\r
382 queueFiles.Add(file.Name);
\r
392 return new List<string>(); // Keep quiet about the error.
\r
397 /// Recover a queue from file.
\r
399 /// <param name="encodeQueue">
\r
400 /// The encode Queue.
\r
402 public static void RecoverQueue(IQueue encodeQueue)
\r
404 DialogResult result = DialogResult.None;
\r
405 List<string> queueFiles = CheckQueueRecovery();
\r
406 if (queueFiles.Count == 1)
\r
408 result = MessageBox.Show(
\r
409 "HandBrake has detected unfinished items on the queue from the last time the application was launched. Would you like to recover these?",
\r
410 "Queue Recovery Possible", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
\r
412 else if (queueFiles.Count > 1)
\r
414 result = MessageBox.Show(
\r
415 "HandBrake has detected multiple unfinished queue files. These will be from multiple instances of HandBrake running. Would you like to recover all unfinished jobs?",
\r
416 "Queue Recovery Possible", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
\r
419 if (result == DialogResult.Yes)
\r
421 foreach (string file in queueFiles)
\r
423 encodeQueue.LoadQueueFromFile(file); // Start Recovery
\r
428 if (IsMultiInstance) return; // Don't tamper with the files if we are multi instance
\r
430 string tempPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"HandBrake\");
\r
431 foreach (string file in queueFiles)
\r
433 if (File.Exists(Path.Combine(tempPath, file)))
\r
434 File.Delete(Path.Combine(tempPath, file));
\r
440 /// Gets a value indicating whether HandBrake is running in multi instance mode
\r
442 /// <returns>True if the UI has another instance running</returns>
\r
443 public static bool IsMultiInstance
\r
447 return Process.GetProcessesByName("HandBrake").Length > 0 ? true : false;
\r
452 /// Clear all the encode log files.
\r
454 public static void ClearLogs()
\r
456 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
\r
457 if (Directory.Exists(logDir))
\r
459 DirectoryInfo info = new DirectoryInfo(logDir);
\r
460 FileInfo[] logFiles = info.GetFiles("*.txt");
\r
461 foreach (FileInfo file in logFiles)
\r
463 if (!file.Name.Contains("last_scan_log") && !file.Name.Contains("last_encode_log"))
\r
464 File.Delete(file.FullName);
\r
470 /// Clear old log files x days in the past
\r
472 public static void ClearOldLogs()
\r
474 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
\r
475 if (Directory.Exists(logDir))
\r
477 DirectoryInfo info = new DirectoryInfo(logDir);
\r
478 FileInfo[] logFiles = info.GetFiles("*.txt");
\r
480 foreach (FileInfo file in logFiles)
\r
482 if (file.LastWriteTime < DateTime.Now.AddDays(-30))
\r
484 if (!file.Name.Contains("last_scan_log.txt") && !file.Name.Contains("last_encode_log.txt"))
\r
485 File.Delete(file.FullName);
\r
492 /// Map languages and their iso639_2 value into a IDictionary
\r
494 /// <returns>A Dictionary containing the language and iso code</returns>
\r
495 public static IDictionary<string, string> MapLanguages()
\r
497 IDictionary<string, string> languageMap = new Dictionary<string, string>
\r
501 {"Abkhazian", "abk"},
\r
502 {"Afrikaans", "afr"},
\r
504 {"Albanian", "sqi"},
\r
505 {"Amharic", "amh"},
\r
506 {"Arabic", "ara"},
\r
507 {"Aragonese", "arg"},
\r
508 {"Armenian", "hye"},
\r
509 {"Assamese", "asm"},
\r
510 {"Avaric", "ava"},
\r
511 {"Avestan", "ave"},
\r
512 {"Aymara", "aym"},
\r
513 {"Azerbaijani", "aze"},
\r
514 {"Bashkir", "bak"},
\r
515 {"Bambara", "bam"},
\r
516 {"Basque", "eus"},
\r
517 {"Belarusian", "bel"},
\r
518 {"Bengali", "ben"},
\r
519 {"Bihari", "bih"},
\r
520 {"Bislama", "bis"},
\r
521 {"Bosnian", "bos"},
\r
522 {"Breton", "bre"},
\r
523 {"Bulgarian", "bul"},
\r
524 {"Burmese", "mya"},
\r
525 {"Catalan", "cat"},
\r
526 {"Chamorro", "cha"},
\r
527 {"Chechen", "che"},
\r
528 {"Chinese", "zho"},
\r
529 {"Church Slavic", "chu"},
\r
530 {"Chuvash", "chv"},
\r
531 {"Cornish", "cor"},
\r
532 {"Corsican", "cos"},
\r
536 {"Divehi", "div"},
\r
537 {"Nederlands", "nld"},
\r
538 {"Dzongkha", "dzo"},
\r
539 {"English", "eng"},
\r
540 {"Esperanto", "epo"},
\r
541 {"Estonian", "est"},
\r
543 {"Faroese", "fao"},
\r
544 {"Fijian", "fij"},
\r
546 {"Francais", "fra"},
\r
547 {"Western Frisian", "fry"},
\r
549 {"Georgian", "kat"},
\r
550 {"Deutsch", "deu"},
\r
551 {"Gaelic (Scots)", "gla"},
\r
553 {"Galician", "glg"},
\r
555 {"Greek Modern", "ell"},
\r
556 {"Guarani", "grn"},
\r
557 {"Gujarati", "guj"},
\r
558 {"Haitian", "hat"},
\r
560 {"Hebrew", "heb"},
\r
561 {"Herero", "her"},
\r
563 {"Hiri Motu", "hmo"},
\r
564 {"Magyar", "hun"},
\r
566 {"Islenska", "isl"},
\r
568 {"Sichuan Yi", "iii"},
\r
569 {"Inuktitut", "iku"},
\r
570 {"Interlingue", "ile"},
\r
571 {"Interlingua", "ina"},
\r
572 {"Indonesian", "ind"},
\r
573 {"Inupiaq", "ipk"},
\r
574 {"Italiano", "ita"},
\r
575 {"Javanese", "jav"},
\r
576 {"Japanese", "jpn"},
\r
577 {"Kalaallisut", "kal"},
\r
578 {"Kannada", "kan"},
\r
579 {"Kashmiri", "kas"},
\r
580 {"Kanuri", "kau"},
\r
581 {"Kazakh", "kaz"},
\r
582 {"Central Khmer", "khm"},
\r
583 {"Kikuyu", "kik"},
\r
584 {"Kinyarwanda", "kin"},
\r
585 {"Kirghiz", "kir"},
\r
588 {"Korean", "kor"},
\r
589 {"Kuanyama", "kua"},
\r
590 {"Kurdish", "kur"},
\r
593 {"Latvian", "lav"},
\r
594 {"Limburgan", "lim"},
\r
595 {"Lingala", "lin"},
\r
596 {"Lithuanian", "lit"},
\r
597 {"Luxembourgish", "ltz"},
\r
598 {"Luba-Katanga", "lub"},
\r
600 {"Macedonian", "mkd"},
\r
601 {"Marshallese", "mah"},
\r
602 {"Malayalam", "mal"},
\r
604 {"Marathi", "mar"},
\r
606 {"Malagasy", "mlg"},
\r
607 {"Maltese", "mlt"},
\r
608 {"Moldavian", "mol"},
\r
609 {"Mongolian", "mon"},
\r
611 {"Navajo", "nav"},
\r
612 {"Ndebele, South", "nbl"},
\r
613 {"Ndebele, North", "nde"},
\r
614 {"Ndonga", "ndo"},
\r
615 {"Nepali", "nep"},
\r
616 {"Norwegian Nynorsk", "nno"},
\r
617 {"Norwegian Bokmål", "nob"},
\r
619 {"Chichewa; Nyanja", "nya"},
\r
620 {"Occitan", "oci"},
\r
621 {"Ojibwa", "oji"},
\r
624 {"Ossetian", "oss"},
\r
625 {"Panjabi", "pan"},
\r
626 {"Persian", "fas"},
\r
628 {"Polish", "pol"},
\r
629 {"Portugues", "por"},
\r
630 {"Pushto", "pus"},
\r
631 {"Quechua", "que"},
\r
632 {"Romansh", "roh"},
\r
633 {"Romanian", "ron"},
\r
635 {"Russian", "rus"},
\r
637 {"Sanskrit", "san"},
\r
638 {"Serbian", "srp"},
\r
639 {"Hrvatski", "hrv"},
\r
640 {"Sinhala", "sin"},
\r
641 {"Slovak", "slk"},
\r
642 {"Slovenian", "slv"},
\r
643 {"Northern Sami", "sme"},
\r
644 {"Samoan", "smo"},
\r
646 {"Sindhi", "snd"},
\r
647 {"Somali", "som"},
\r
648 {"Sotho Southern", "sot"},
\r
649 {"Espanol", "spa"},
\r
650 {"Sardinian", "srd"},
\r
652 {"Sundanese", "sun"},
\r
653 {"Swahili", "swa"},
\r
654 {"Svenska", "swe"},
\r
655 {"Tahitian", "tah"},
\r
658 {"Telugu", "tel"},
\r
660 {"Tagalog", "tgl"},
\r
662 {"Tibetan", "bod"},
\r
663 {"Tigrinya", "tir"},
\r
665 {"Tswana", "tsn"},
\r
666 {"Tsonga", "tso"},
\r
667 {"Turkmen", "tuk"},
\r
668 {"Turkish", "tur"},
\r
670 {"Uighur", "uig"},
\r
671 {"Ukrainian", "ukr"},
\r
675 {"Vietnamese", "vie"},
\r
676 {"Volapük", "vol"},
\r
678 {"Walloon", "wln"},
\r
681 {"Yiddish", "yid"},
\r
682 {"Yoruba", "yor"},
\r
683 {"Zhuang", "zha"},
\r
686 return languageMap;
\r
690 /// Get a list of available DVD drives which are ready and contain DVD content.
\r
692 /// <returns>A List of Drives with their details</returns>
\r
693 public static List<DriveInformation> GetDrives()
\r
695 List<DriveInformation> drives = new List<DriveInformation>();
\r
696 DriveInfo[] theCollectionOfDrives = DriveInfo.GetDrives();
\r
698 foreach (DriveInfo curDrive in theCollectionOfDrives)
\r
700 if (curDrive.DriveType == DriveType.CDRom && curDrive.IsReady &&
\r
701 File.Exists(curDrive.RootDirectory + "VIDEO_TS\\VIDEO_TS.IFO"))
\r
703 drives.Add(new DriveInformation
\r
706 VolumeLabel = curDrive.VolumeLabel,
\r
707 RootDirectory = curDrive.RootDirectory + "VIDEO_TS"
\r
716 /// Change a string to Title Case/
\r
718 /// <param name="input">
\r
722 /// A string in title case.
\r
724 public static string TitleCase(string input)
\r
726 string[] tokens = input.Split(' ');
\r
727 StringBuilder sb = new StringBuilder(input.Length);
\r
728 foreach (string s in tokens)
\r
730 if (!string.IsNullOrEmpty(s))
\r
732 sb.Append(s[0].ToString().ToUpper());
\r
733 sb.Append(s.Substring(1).ToLower());
\r
738 return sb.ToString().Trim();
\r
742 /// Show the Exception Window
\r
744 /// <param name="shortError">
\r
745 /// The short error.
\r
747 /// <param name="longError">
\r
748 /// The long error.
\r
750 public static void ShowExceptiowWindow(string shortError, string longError)
\r
752 errorService.ShowError(shortError, longError);
\r
756 /// Get The Source from the CLI Query
\r
758 /// <param name="query">Full CLI Query</param>
\r
759 /// <returns>The Source Path</returns>
\r
760 public static string GetSourceFromQuery(string query)
\r
762 int startIndex = query.IndexOf("-i \"");
\r
763 if (startIndex != -1)
\r
765 string input = query.Substring(startIndex).Replace("-i \"", string.Empty).Trim();
\r
767 int closeIndex = input.IndexOf('"');
\r
769 return closeIndex == -1 ? "Unknown" : input.Substring(0, closeIndex);
\r
776 /// Get the Destination from the CLI Query
\r
778 /// <param name="query">Full CLI Query</param>
\r
779 /// <returns>The Destination path</returns>
\r
780 public static string GetDestinationFromQuery(string query)
\r
782 int startIndex = query.IndexOf("-o \"");
\r
783 if (startIndex != -1)
\r
785 string output = query.Substring(startIndex).Replace("-o \"", string.Empty).Trim();
\r
787 int closeIndex = output.IndexOf('"');
\r
789 return closeIndex == -1 ? "Unknown" : output.Substring(0, closeIndex);
\r