/* Title.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.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; /// /// An object that represents a single Title of a DVD /// public class Title { /// /// The Culture Info /// private static readonly CultureInfo Culture = new CultureInfo("en-US", false); /// /// Initializes a new instance of the class. /// public Title() { AudioTracks = new List(); Chapters = new List(); Subtitles = new List(); } #region Properties /// /// Gets or sets a Collection of chapters in this Title /// public List Chapters { get; set; } /// /// Gets or sets a Collection of audio tracks associated with this Title /// public List AudioTracks { get; set; } /// /// Gets or sets a Collection of subtitles associated with this Title /// public List Subtitles { get; set; } /// /// Gets or sets The track number of this Title /// public int TitleNumber { get; set; } /// /// Gets or sets the length in time of this Title /// public TimeSpan Duration { get; set; } /// /// Gets or sets the resolution (width/height) of this Title /// public Size Resolution { get; set; } /// /// Gets or sets the aspect ratio of this Title /// public double AspectRatio { get; set; } /// /// Gets or sets AngleCount. /// public int AngleCount { get; set; } /// /// Gets or sets Par Value /// public Size ParVal { get; set; } /// /// 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 /// public Cropping AutoCropDimensions { get; set; } /// /// Gets or sets the FPS of the source. /// public double Fps { get; set; } /// /// Gets or sets a value indicating whether this is a MainTitle. /// public bool MainTitle { get; set; } /// /// Gets or sets the Source Name /// public string SourceName { get; set; } #endregion /// /// Parse the Title Information /// /// A stingreader of output data /// A Title 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; } /// /// Return a list of parsed titles /// /// The Output /// A List of titles public static Title[] ParseList(string output) { var titles = new List(); 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); } } }