summaryrefslogtreecommitdiffstats
path: root/win/CS/HandBrake.ApplicationServices/Parsing
diff options
context:
space:
mode:
authorsr55 <[email protected]>2011-03-13 16:44:49 +0000
committersr55 <[email protected]>2011-03-13 16:44:49 +0000
commitb6a5d4eba610711d15ed99dc5f2e9e126ce06086 (patch)
treee72eb5be161c3ac9b01bbc3b3efcd4ab8f91e06c /win/CS/HandBrake.ApplicationServices/Parsing
parent38f64c238720fe0524f99b380fbb1a8c795a7586 (diff)
Rename Direction C# to CS
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3846 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'win/CS/HandBrake.ApplicationServices/Parsing')
-rw-r--r--win/CS/HandBrake.ApplicationServices/Parsing/AudioTrack.cs169
-rw-r--r--win/CS/HandBrake.ApplicationServices/Parsing/Chapter.cs126
-rw-r--r--win/CS/HandBrake.ApplicationServices/Parsing/Parser.cs163
-rw-r--r--win/CS/HandBrake.ApplicationServices/Parsing/Source.cs53
-rw-r--r--win/CS/HandBrake.ApplicationServices/Parsing/Subtitle.cs171
-rw-r--r--win/CS/HandBrake.ApplicationServices/Parsing/Title.cs234
6 files changed, 916 insertions, 0 deletions
diff --git a/win/CS/HandBrake.ApplicationServices/Parsing/AudioTrack.cs b/win/CS/HandBrake.ApplicationServices/Parsing/AudioTrack.cs
new file mode 100644
index 000000000..0597f4d1b
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Parsing/AudioTrack.cs
@@ -0,0 +1,169 @@
+/* AudioTrack.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Parsing
+{
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Text.RegularExpressions;
+
+ /// <summary>
+ /// An object represending an AudioTrack associated with a Title, in a DVD
+ /// </summary>
+ public class AudioTrack
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AudioTrack"/> class.
+ /// </summary>
+ public AudioTrack()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AudioTrack"/> class.
+ /// </summary>
+ /// <param name="trackNumber">
+ /// The track number.
+ /// </param>
+ /// <param name="language">
+ /// The language.
+ /// </param>
+ /// <param name="languageCode">
+ /// The language code.
+ /// </param>
+ /// <param name="description">
+ /// The description.
+ /// </param>
+ /// <param name="format">
+ /// The format.
+ /// </param>
+ /// <param name="sampleRate">
+ /// The sample rate.
+ /// </param>
+ /// <param name="bitrate">
+ /// The bitrate.
+ /// </param>
+ public AudioTrack(int trackNumber, string language, string languageCode, string description, string format, int sampleRate, int bitrate)
+ {
+ this.TrackNumber = trackNumber;
+ this.Language = language;
+ this.LanguageCode = languageCode;
+ this.Description = description;
+ this.Format = format;
+ this.SampleRate = sampleRate;
+ this.Bitrate = bitrate;
+ }
+
+ /// <summary>
+ /// Gets or sets The track number of this Audio Track
+ /// </summary>
+ public int TrackNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets The language (if detected) of this Audio Track
+ /// </summary>
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets LanguageCode.
+ /// </summary>
+ public string LanguageCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets Description.
+ /// </summary>
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets The primary format of this Audio Track
+ /// </summary>
+ public string Format { get; set; }
+
+ /// <summary>
+ /// Gets or sets The frequency (in MHz) of this Audio Track
+ /// </summary>
+ public int SampleRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets The bitrate (in kbps) of this Audio Track
+ /// </summary>
+ public int Bitrate { get; set; }
+
+ /// <summary>
+ /// Parse the CLI input to an Audio Track object
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// An Audio Track obkect
+ /// </returns>
+ public static AudioTrack Parse(StringReader output)
+ {
+ string audioTrack = output.ReadLine();
+ Match m = Regex.Match(audioTrack, @"^ \+ ([0-9]*), ([A-Za-z0-9,\s]*) \((.*)\) \((.*)\)");
+ Match track = Regex.Match(audioTrack, @"^ \+ ([0-9]*), ([A-Za-z0-9,\s]*) \((.*)\)"); // ID and Language
+ Match iso639_2 = Regex.Match(audioTrack, @"iso639-2: ([a-zA-Z]*)\)");
+ Match samplerate = Regex.Match(audioTrack, @"([0-9]*)Hz");
+ Match bitrate = Regex.Match(audioTrack, @"([0-9]*)bps");
+
+ string subformat = m.Groups[4].Value.Trim().Contains("iso639") ? null : m.Groups[4].Value;
+ string samplerateVal = samplerate.Success ? samplerate.Groups[0].Value.Replace("Hz", string.Empty).Trim() : "0";
+ string bitrateVal = bitrate.Success ? bitrate.Groups[0].Value.Replace("bps", string.Empty).Trim() : "0";
+
+ if (track.Success)
+ {
+ var thisTrack = new AudioTrack
+ {
+ TrackNumber = int.Parse(track.Groups[1].Value.Trim()),
+ Language = track.Groups[2].Value,
+ Format = m.Groups[3].Value,
+ Description = subformat,
+ SampleRate = int.Parse(samplerateVal),
+ Bitrate = int.Parse(bitrateVal),
+ LanguageCode = iso639_2.Value.Replace("iso639-2: ", string.Empty).Replace(")", string.Empty)
+ };
+ return thisTrack;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Pase a list of audio tracks
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// An array of audio tracks
+ /// </returns>
+ public static AudioTrack[] ParseList(StringReader output)
+ {
+ var tracks = new List<AudioTrack>();
+ while (true)
+ {
+ AudioTrack thisTrack = Parse(output);
+ if (thisTrack != null)
+ tracks.Add(thisTrack);
+ else
+ break;
+ }
+ return tracks.ToArray();
+ }
+
+ /// <summary>
+ /// Override of the ToString method to make this object easier to use in the UI
+ /// </summary>
+ /// <returns>A string formatted as: {track #} {language} ({format}) ({sub-format})</returns>
+ public override string ToString()
+ {
+ if (Description == null)
+ return string.Format("{0} {1} ({2})", TrackNumber, Language, Format);
+
+ return string.Format("{0} {1} ({2}) ({3})", TrackNumber, Language, Format, Description);
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Parsing/Chapter.cs b/win/CS/HandBrake.ApplicationServices/Parsing/Chapter.cs
new file mode 100644
index 000000000..4ec76fa1c
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Parsing/Chapter.cs
@@ -0,0 +1,126 @@
+/* Chapter.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Parsing
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Text.RegularExpressions;
+
+ /// <summary>
+ /// An object representing a Chapter aosciated with a Title, in a DVD
+ /// </summary>
+ public class Chapter
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Chapter"/> class.
+ /// </summary>
+ public Chapter()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Chapter"/> class.
+ /// </summary>
+ /// <param name="number">
+ /// The number.
+ /// </param>
+ /// <param name="Name">
+ /// The name.
+ /// </param>
+ /// <param name="duration">
+ /// The duration.
+ /// </param>
+ public Chapter(int number, string Name, TimeSpan duration)
+ {
+ this.ChapterName = Name;
+ this.ChapterNumber = number;
+ this.Duration = duration;
+ }
+
+ /// <summary>
+ /// Gets or sets The number of this Chapter, in regards to it's parent Title
+ /// </summary>
+ public int ChapterNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets ChapterName.
+ /// </summary>
+ public string ChapterName { get; set; }
+
+ /// <summary>
+ /// Gets or sets The length in time this Chapter spans
+ /// </summary>
+ public TimeSpan Duration { get; set; }
+
+ /// <summary>
+ /// Parse a CLI string to a Chapter object
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// A chapter Object
+ /// </returns>
+ public static Chapter Parse(StringReader output)
+ {
+ // TODO add support for reading chapter names to the regex.
+ Match m = Regex.Match(
+ output.ReadLine(),
+ @"^ \+ ([0-9]*): cells ([0-9]*)->([0-9]*), ([0-9]*) blocks, duration ([0-9]{2}:[0-9]{2}:[0-9]{2})");
+ if (m.Success)
+ {
+ var thisChapter = new Chapter
+ {
+ ChapterNumber = int.Parse(m.Groups[1].Value.Trim()),
+ ChapterName = string.Empty,
+ Duration = TimeSpan.Parse(m.Groups[5].Value)
+ };
+ return thisChapter;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Prase a list of strings / chatpers
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// An array of chapter objects
+ /// </returns>
+ public static Chapter[] ParseList(StringReader output)
+ {
+ var chapters = new List<Chapter>();
+
+ // this is to read the " + chapters:" line from the buffer
+ // so we can start reading the chapters themselvs
+ output.ReadLine();
+
+ while (true)
+ {
+ // Start of the chapter list for this Title
+ Chapter thisChapter = Parse(output);
+
+ if (thisChapter != null)
+ chapters.Add(thisChapter);
+ else
+ break;
+ }
+ return chapters.ToArray();
+ }
+
+ /// <summary>
+ /// Override of the ToString method to make this object easier to use in the UI
+ /// </summary>
+ /// <returns>A string formatted as: {chapter #}</returns>
+ public override string ToString()
+ {
+ return ChapterNumber.ToString();
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Parsing/Parser.cs b/win/CS/HandBrake.ApplicationServices/Parsing/Parser.cs
new file mode 100644
index 000000000..e46afea28
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Parsing/Parser.cs
@@ -0,0 +1,163 @@
+/* Parser.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Parsing
+{
+ using System;
+ using System.Globalization;
+ using System.IO;
+ using System.Text;
+ using System.Text.RegularExpressions;
+
+ /// <summary>
+ /// A delegate to handle custom events regarding data being parsed from the buffer
+ /// </summary>
+ /// <param name="sender">The object which raised this delegate</param>
+ /// <param name="data">The data parsed from the stream</param>
+ public delegate void DataReadEventHandler(object sender, string data);
+
+ /// <summary>
+ /// A delegate to handle events regarding progress during DVD scanning
+ /// </summary>
+ /// <param name="sender">The object who's raising the event</param>
+ /// <param name="currentTitle">The title number currently being processed</param>
+ /// <param name="titleCount">The total number of titiles to be processed</param>
+ public delegate void ScanProgressEventHandler(object sender, int currentTitle, int titleCount);
+
+ /// <summary>
+ /// A delegate to handle encode progress updates // EXPERIMENTAL
+ /// </summary>
+ /// <param name="sender">The object which raised the event</param>
+ /// <param name="currentTask">The current task being processed from the queue</param>
+ /// <param name="taskCount">The total number of tasks in queue</param>
+ /// <param name="percentComplete">The percentage this task is complete</param>
+ /// <param name="currentFps">The current encoding fps</param>
+ /// <param name="averageFps">The average encoding fps for this task</param>
+ /// <param name="timeRemaining">The estimated time remaining for this task to complete</param>
+ public delegate void EncodeProgressEventHandler(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float averageFps, string timeRemaining);
+
+ /// <summary>
+ /// A simple wrapper around a StreamReader to keep track of the entire output from a cli process
+ /// </summary>
+ public class Parser : StreamReader
+ {
+ /// <summary>
+ /// The Buffer StringBuilder
+ /// </summary>
+ private readonly StringBuilder buffer = new StringBuilder(string.Empty);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Parser"/> class.
+ /// Default constructor for this object
+ /// </summary>
+ /// <param name="baseStream">
+ /// The stream to parse from
+ /// </param>
+ public Parser(Stream baseStream) : base(baseStream)
+ {
+ }
+
+ /// <summary>
+ /// Raised upon a new line being read from stdout/stderr
+ /// </summary>
+ public event DataReadEventHandler OnReadLine;
+
+ /// <summary>
+ /// Raised upon the entire stdout/stderr stream being read in a single call
+ /// </summary>
+ public event DataReadEventHandler OnReadToEnd;
+
+ /// <summary>
+ /// Raised upon the catching of a "Scanning title # of #..." in the stream
+ /// </summary>
+ public event ScanProgressEventHandler OnScanProgress;
+
+ /// <summary>
+ /// Raised upon the catching of a "Scanning title # of #..." in the stream
+ /// </summary>
+ public event EncodeProgressEventHandler OnEncodeProgress;
+
+ /// <summary>
+ /// Gets the buffer of data that came from the CLI standard input/error
+ /// </summary>
+ public StringBuilder Buffer
+ {
+ get { return buffer; }
+ }
+
+ /// <summary>
+ /// Read a line from standard in/err
+ /// </summary>
+ /// <returns>
+ /// The read line
+ /// </returns>
+ public override string ReadLine()
+ {
+ string tmp = base.ReadLine();
+
+ buffer.Append(tmp + Environment.NewLine);
+
+ Match m = null;
+ if (tmp.Contains("Scanning title"))
+ m = Regex.Match(tmp, "^Scanning title ([0-9]*) of ([0-9]*)");
+
+ if (OnReadLine != null)
+ OnReadLine(this, tmp);
+
+ if (m != null)
+ if (m.Success && OnScanProgress != null)
+ OnScanProgress(this, int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value));
+
+ return tmp;
+ }
+
+ /// <summary>
+ /// Read to the end of the input stream
+ /// </summary>
+ /// <returns>
+ /// A string of the input data
+ /// </returns>
+ public override string ReadToEnd()
+ {
+ string tmp = base.ReadToEnd();
+
+ buffer.Append(tmp + Environment.NewLine);
+ if (OnReadToEnd != null)
+ OnReadToEnd(this, tmp);
+
+ return tmp;
+ }
+
+ /// <summary>
+ /// Pase the CLI status output (from standard output)
+ /// </summary>
+ public void ReadEncodeStatus()
+ {
+ CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
+ string tmp = base.ReadLine();
+
+ Match m = Regex.Match(tmp, @"^Encoding: task ([0-9]*) of ([0-9]*), ([0-9]*\.[0-9]*) %( \(([0-9]*\.[0-9]*) fps, avg ([0-9]*\.[0-9]*) fps, ETA ([0-9]{2})h([0-9]{2})m([0-9]{2})s\))?");
+ if (m.Success && OnEncodeProgress != null)
+ {
+ int currentTask = int.Parse(m.Groups[1].Value);
+ int totalTasks = int.Parse(m.Groups[2].Value);
+ float percent = float.Parse(m.Groups[3].Value, culture);
+ float currentFps = m.Groups[5].Value == string.Empty ? 0.0F : float.Parse(m.Groups[5].Value, culture);
+ float avgFps = m.Groups[6].Value == string.Empty ? 0.0F : float.Parse(m.Groups[6].Value, culture);
+ string remaining = string.Empty;
+ if (m.Groups[7].Value != string.Empty)
+ {
+ remaining = m.Groups[7].Value + ":" + m.Groups[8].Value + ":" + m.Groups[9].Value;
+ }
+ if (string.IsNullOrEmpty(remaining))
+ {
+ remaining = "Calculating ...";
+ }
+
+ OnEncodeProgress(this, currentTask, totalTasks, percent, currentFps, avgFps, remaining);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Parsing/Source.cs b/win/CS/HandBrake.ApplicationServices/Parsing/Source.cs
new file mode 100644
index 000000000..0ed9c0033
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Parsing/Source.cs
@@ -0,0 +1,53 @@
+/* Source.cs $ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Parsing
+{
+ using System.Collections.Generic;
+ using System.IO;
+
+ /// <summary>
+ /// An object representing a scanned DVD
+ /// </summary>
+ public class Source
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Source"/> class.
+ /// Default constructor for this object
+ /// </summary>
+ public Source()
+ {
+ Titles = new List<Title>();
+ }
+
+ /// <summary>
+ /// Gets or sets Titles. A list of titles from the source
+ /// </summary>
+ public List<Title> Titles { get; set; }
+
+ /// <summary>
+ /// Parse the StreamReader output into a List of Titles
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// A DVD object which contains a list of title inforamtion
+ /// </returns>
+ public static Source Parse(StreamReader output)
+ {
+ var thisDVD = new Source();
+
+ while (!output.EndOfStream)
+ {
+ if ((char) output.Peek() == '+')
+ thisDVD.Titles.AddRange(Title.ParseList(output.ReadToEnd()));
+ else
+ output.ReadLine();
+ }
+
+ return thisDVD;
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Parsing/Subtitle.cs b/win/CS/HandBrake.ApplicationServices/Parsing/Subtitle.cs
new file mode 100644
index 000000000..1ac827037
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Parsing/Subtitle.cs
@@ -0,0 +1,171 @@
+/* Subtitle.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Parsing
+{
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Text.RegularExpressions;
+
+ using HandBrake.ApplicationServices.Functions;
+ using HandBrake.ApplicationServices.Model.Encoding;
+
+ /// <summary>
+ /// An object that represents a subtitle associated with a Title, in a DVD
+ /// </summary>
+ public class Subtitle
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Subtitle"/> class.
+ /// </summary>
+ public Subtitle()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Subtitle"/> class.
+ /// </summary>
+ /// <param name="trackNumber">
+ /// The track number.
+ /// </param>
+ /// <param name="language">
+ /// The language.
+ /// </param>
+ /// <param name="languageCode">
+ /// The language code.
+ /// </param>
+ /// <param name="subtitleType">
+ /// The subtitle type.
+ /// </param>
+ public Subtitle(int trackNumber, string language, string languageCode, SubtitleType subtitleType)
+ {
+ this.TrackNumber = trackNumber;
+ this.Language = language;
+ this.LanguageCode = languageCode;
+ this.SubtitleType = subtitleType;
+ }
+
+
+ /// <summary>
+ /// Gets or sets the track number of this Subtitle
+ /// </summary>
+ public int TrackNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the The language (if detected) of this Subtitle
+ /// </summary>
+ public string Language { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Langauage Code
+ /// </summary>
+ public string LanguageCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Subtitle Type
+ /// </summary>
+ public SubtitleType SubtitleType { get; set; }
+
+ /// <summary>
+ /// Gets Subtitle Type
+ /// </summary>
+ public string TypeString
+ {
+ get
+ {
+ return EnumHelper.GetDescription(this.SubtitleType);
+ }
+ }
+
+ /// <summary>
+ /// Parse the input strings related to subtitles
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// A Subitle object
+ /// </returns>
+ public static Subtitle Parse(StringReader output)
+ {
+ string curLine = output.ReadLine();
+
+ // + 1, English (iso639-2: eng) (Text)(SSA)
+ // + 1, English (iso639-2: eng) (Text)(UTF-8)
+ Match m = Regex.Match(curLine, @"^ \+ ([0-9]*), ([A-Za-z, ]*) \((.*)\) \(([a-zA-Z]*)\)\(([a-zA-Z0-9\-]*)\)");
+
+ if (m.Success && !curLine.Contains("HandBrake has exited."))
+ {
+ var thisSubtitle = new Subtitle
+ {
+ TrackNumber = int.Parse(m.Groups[1].Value.Trim()),
+ Language = m.Groups[2].Value,
+ LanguageCode = m.Groups[3].Value,
+ };
+
+ switch (m.Groups[5].Value)
+ {
+ case "VOBSUB":
+ thisSubtitle.SubtitleType = SubtitleType.VobSub;
+ break;
+ case "SRT":
+ thisSubtitle.SubtitleType = SubtitleType.SRT;
+ break;
+ case "CC":
+ thisSubtitle.SubtitleType = SubtitleType.CC;
+ break;
+ case "UTF-8":
+ thisSubtitle.SubtitleType = SubtitleType.UTF8Sub;
+ break;
+ case "TX3G":
+ thisSubtitle.SubtitleType = SubtitleType.TX3G;
+ break;
+ case "SSA":
+ thisSubtitle.SubtitleType = SubtitleType.SSA;
+ break;
+ default:
+ thisSubtitle.SubtitleType = SubtitleType.Unknown;
+ break;
+ }
+
+ return thisSubtitle;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Parse a list of Subtitle tracks from an input string.
+ /// </summary>
+ /// <param name="output">
+ /// The output.
+ /// </param>
+ /// <returns>
+ /// An array of Subtitle objects
+ /// </returns>
+ public static IEnumerable<Subtitle> ParseList(StringReader output)
+ {
+ var subtitles = new List<Subtitle>();
+ while ((char)output.Peek() != '+')
+ {
+ Subtitle thisSubtitle = Parse(output);
+
+ if (thisSubtitle != null)
+ subtitles.Add(thisSubtitle);
+ else
+ break;
+ }
+ return subtitles.ToArray();
+ }
+
+ /// <summary>
+ /// Override of the ToString method to make this object easier to use in the UI
+ /// </summary>
+ /// <returns>A string formatted as: {track #} {language}</returns>
+ public override string ToString()
+ {
+ return string.Format("{0} {1} ({2})", TrackNumber, Language, TypeString);
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Parsing/Title.cs b/win/CS/HandBrake.ApplicationServices/Parsing/Title.cs
new file mode 100644
index 000000000..3a9e6a56a
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Parsing/Title.cs
@@ -0,0 +1,234 @@
+/* Title.cs $
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr>.
+ It may be used under the terms of the GNU General Public License. */
+
+namespace HandBrake.ApplicationServices.Parsing
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.Globalization;
+ using System.IO;
+ using System.Text.RegularExpressions;
+
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Model.Encoding;
+
+ /// <summary>
+ /// An object that represents a single Title of a DVD
+ /// </summary>
+ public class Title
+ {
+ /// <summary>
+ /// The Culture Info
+ /// </summary>
+ private static readonly CultureInfo Culture = new CultureInfo("en-US", false);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Title"/> class.
+ /// </summary>
+ public Title()
+ {
+ this.AudioTracks = new List<AudioTrack>();
+ this.Chapters = new List<Chapter>();
+ this.Subtitles = new List<Subtitle>();
+ }
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets a Collection of chapters in this Title
+ /// </summary>
+ public List<Chapter> Chapters { get; set; }
+
+ /// <summary>
+ /// Gets or sets a Collection of audio tracks associated with this Title
+ /// </summary>
+ public List<AudioTrack> AudioTracks { get; set; }
+
+ /// <summary>
+ /// Gets or sets a Collection of subtitles associated with this Title
+ /// </summary>
+ public List<Subtitle> Subtitles { get; set; }
+
+ /// <summary>
+ /// Gets or sets The track number of this Title
+ /// </summary>
+ public int TitleNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the length in time of this Title
+ /// </summary>
+ public TimeSpan Duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the resolution (width/height) of this Title
+ /// </summary>
+ public Size Resolution { get; set; }
+
+ /// <summary>
+ /// Gets or sets the aspect ratio of this Title
+ /// </summary>
+ public double AspectRatio { get; set; }
+
+ /// <summary>
+ /// Gets or sets AngleCount.
+ /// </summary>
+ public int AngleCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets Par Value
+ /// </summary>
+ public Size ParVal { get; set; }
+
+ /// <summary>
+ /// Gets or sets the automatically detected crop region for this Title.
+ /// This is an int array with 4 items in it as so:
+ /// 0: T
+ /// 1: B
+ /// 2: L
+ /// 3: R
+ /// </summary>
+ public Cropping AutoCropDimensions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the FPS of the source.
+ /// </summary>
+ public double Fps { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this is a MainTitle.
+ /// </summary>
+ public bool MainTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Source Name
+ /// </summary>
+ public string SourceName { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Parse the Title Information
+ /// </summary>
+ /// <param name="output">A StringReader of output data</param>
+ /// <returns>A Title Object</returns>
+ public static Title Parse(StringReader output)
+ {
+ var thisTitle = new Title();
+ string nextLine = output.ReadLine();
+
+ // Get the Title Number
+ Match m = Regex.Match(nextLine, @"^\+ title ([0-9]*):");
+ if (m.Success)
+ thisTitle.TitleNumber = int.Parse(m.Groups[1].Value.Trim());
+ nextLine = output.ReadLine();
+
+ // Detect if this is the main feature
+ m = Regex.Match(nextLine, @" \+ Main Feature");
+ if (m.Success)
+ {
+ thisTitle.MainTitle = true;
+ nextLine = output.ReadLine();
+ }
+
+ // Get the stream name for file import
+ m = Regex.Match(nextLine, @"^ \+ stream:");
+ if (m.Success)
+ {
+ thisTitle.SourceName = nextLine.Replace("+ stream:", string.Empty).Trim();
+ nextLine = output.ReadLine();
+ }
+
+ // Jump over the VTS and blocks line
+ m = Regex.Match(nextLine, @"^ \+ vts:");
+ if (nextLine.Contains("blocks") || nextLine.Contains("+ vts "))
+ {
+ nextLine = output.ReadLine();
+ }
+
+ // Multi-Angle Support if LibDvdNav is enabled
+ if (!Init.DisableDvdNav)
+ {
+ m = Regex.Match(nextLine, @" \+ angle\(s\) ([0-9])");
+ if (m.Success)
+ {
+ string angleList = m.Value.Replace("+ angle(s) ", string.Empty).Trim();
+ int angleCount;
+ int.TryParse(angleList, out angleCount);
+
+ thisTitle.AngleCount = angleCount;
+ nextLine = output.ReadLine();
+ }
+ }
+
+ // Get duration for this title
+ m = Regex.Match(nextLine, @"^ \+ duration: ([0-9]{2}:[0-9]{2}:[0-9]{2})");
+ if (m.Success)
+ thisTitle.Duration = TimeSpan.Parse(m.Groups[1].Value);
+
+ // Get resolution, aspect ratio and FPS for this title
+ m = Regex.Match(output.ReadLine(), @"^ \+ size: ([0-9]*)x([0-9]*), pixel aspect: ([0-9]*)/([0-9]*), display aspect: ([0-9]*\.[0-9]*), ([0-9]*\.[0-9]*) fps");
+ if (m.Success)
+ {
+ thisTitle.Resolution = new Size(int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value));
+ thisTitle.ParVal = new Size(int.Parse(m.Groups[3].Value), int.Parse(m.Groups[4].Value));
+ thisTitle.AspectRatio = float.Parse(m.Groups[5].Value, Culture);
+ thisTitle.Fps = float.Parse(m.Groups[6].Value, Culture);
+ }
+
+ // Get autocrop region for this title
+ m = Regex.Match(output.ReadLine(), @"^ \+ autocrop: ([0-9]*)/([0-9]*)/([0-9]*)/([0-9]*)");
+ if (m.Success)
+ {
+ thisTitle.AutoCropDimensions = new Cropping
+ {
+ Top = int.Parse(m.Groups[1].Value),
+ Bottom = int.Parse(m.Groups[2].Value),
+ Left = int.Parse(m.Groups[3].Value),
+ Right = int.Parse(m.Groups[4].Value)
+ };
+ }
+
+ thisTitle.Chapters.AddRange(Chapter.ParseList(output));
+
+ thisTitle.AudioTracks.AddRange(AudioTrack.ParseList(output));
+
+ thisTitle.Subtitles.AddRange(Subtitle.ParseList(output));
+
+ return thisTitle;
+ }
+
+ /// <summary>
+ /// Return a list of parsed titles
+ /// </summary>
+ /// <param name="output">The Output</param>
+ /// <returns>A List of titles</returns>
+ public static Title[] ParseList(string output)
+ {
+ var titles = new List<Title>();
+ var sr = new StringReader(output);
+
+ while (sr.Peek() == '+' || sr.Peek() == ' ')
+ {
+ // If the the character is a space, then chances are the line
+ if (sr.Peek() == ' ') // If the character is a space, then chances are it's the combing detected line.
+ sr.ReadLine(); // Skip over it
+ else
+ titles.Add(Parse(sr));
+ }
+
+ return titles.ToArray();
+ }
+
+ /// <summary>
+ /// Override of the ToString method to provide an easy way to use this object in the UI
+ /// </summary>
+ /// <returns>A string representing this track in the format: {title #} (00:00:00)</returns>
+ public override string ToString()
+ {
+ return string.Format("{0} ({1:00}:{2:00}:{3:00})", TitleNumber, Duration.Hours, Duration.Minutes, Duration.Seconds);
+ }
+ }
+} \ No newline at end of file