summaryrefslogtreecommitdiffstats
path: root/win/CS/HandBrakeWPF/Services/Scan
diff options
context:
space:
mode:
authorScott <[email protected]>2015-09-26 20:58:05 +0100
committerScott <[email protected]>2015-09-26 21:30:31 +0100
commite703a7961f12a3e02c475754862a1f4a57a04646 (patch)
treebc0a611446ab624082b27ebcc22980f250a05838 /win/CS/HandBrakeWPF/Services/Scan
parentefcddfdf4fc67f59bf09154a0c8d2d20ba61c895 (diff)
AppServices tidyup
Moving the UI modelling and services to the GUI Project.
Diffstat (limited to 'win/CS/HandBrakeWPF/Services/Scan')
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanCompletedEventArgs.cs70
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanProgressEventArgs.cs39
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/Interfaces/IScan.cs113
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/LibScan.cs600
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/Model/Audio.cs181
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/Model/Chapter.cs70
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/Model/Source.cs57
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/Model/Subtitle.cs207
-rw-r--r--win/CS/HandBrakeWPF/Services/Scan/Model/Title.cs159
9 files changed, 1496 insertions, 0 deletions
diff --git a/win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanCompletedEventArgs.cs b/win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanCompletedEventArgs.cs
new file mode 100644
index 000000000..75520fef6
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanCompletedEventArgs.cs
@@ -0,0 +1,70 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="ScanCompletedEventArgs.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Scan Progress Event Args
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.EventArgs
+{
+ using System;
+
+ using HandBrakeWPF.Services.Scan.Model;
+
+ /// <summary>
+ /// Scan Progress Event Args
+ /// </summary>
+ public class ScanCompletedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ScanCompletedEventArgs"/> class.
+ /// </summary>
+ /// <param name="cancelled">
+ /// Whether the scan was cancelled.
+ /// </param>
+ /// <param name="exception">
+ /// The exception.
+ /// </param>
+ /// <param name="errorInformation">
+ /// The error information.
+ /// </param>
+ /// <param name="scannedSource">
+ /// The scanned Source.
+ /// </param>
+ public ScanCompletedEventArgs(bool cancelled, Exception exception, string errorInformation, Source scannedSource)
+ {
+ this.Successful = !cancelled && exception == null && string.IsNullOrEmpty(errorInformation) && scannedSource != null && scannedSource.Titles != null && scannedSource.Titles.Count > 0;
+ this.Cancelled = cancelled;
+ this.Exception = exception;
+ this.ErrorInformation = errorInformation;
+ this.ScannedSource = scannedSource;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether Successful.
+ /// </summary>
+ public bool Successful { get; private set; }
+
+ /// <summary>
+ /// Gets a value indicating whether Cancelled.
+ /// </summary>
+ public bool Cancelled { get; private set; }
+
+ /// <summary>
+ /// Gets the Exception.
+ /// </summary>
+ public Exception Exception { get; private set; }
+
+ /// <summary>
+ /// Gets ErrorInformation.
+ /// </summary>
+ public string ErrorInformation { get; private set; }
+
+ /// <summary>
+ /// Gets the scanned source.
+ /// </summary>
+ public Source ScannedSource { get; private set; }
+ }
+}
diff --git a/win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanProgressEventArgs.cs b/win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanProgressEventArgs.cs
new file mode 100644
index 000000000..8d815b746
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/EventArgs/ScanProgressEventArgs.cs
@@ -0,0 +1,39 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="ScanProgressEventArgs.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Scan Progress Event Args
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.EventArgs
+{
+ using System;
+ using System.Runtime.Serialization;
+
+ /// <summary>
+ /// Scan Progress Event Args
+ /// </summary>
+ [DataContract]
+ public class ScanProgressEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the title currently being scanned.
+ /// </summary>
+ [DataMember]
+ public int CurrentTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the total number of Titles.
+ /// </summary>
+ [DataMember]
+ public int Titles { get; set; }
+
+ /// <summary>
+ /// Gets or sets the percentage.
+ /// </summary>
+ [DataMember]
+ public decimal Percentage { get; set; }
+ }
+}
diff --git a/win/CS/HandBrakeWPF/Services/Scan/Interfaces/IScan.cs b/win/CS/HandBrakeWPF/Services/Scan/Interfaces/IScan.cs
new file mode 100644
index 000000000..1c0d3a851
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/Interfaces/IScan.cs
@@ -0,0 +1,113 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="IScan.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Encode Progess Status
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.Interfaces
+{
+ using System;
+ using System.Windows.Media.Imaging;
+
+ using HandBrake.ApplicationServices.Model;
+
+ using HandBrakeWPF.Services.Encode.Model;
+ using HandBrakeWPF.Services.Scan.EventArgs;
+ using HandBrakeWPF.Services.Scan.Model;
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public delegate void ScanProgessStatus(object sender, ScanProgressEventArgs e);
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The ScanCompletedEventArgs.
+ /// </param>
+ public delegate void ScanCompletedStatus(object sender, ScanCompletedEventArgs e);
+
+ /// <summary>
+ /// The IScan Interface
+ /// </summary>
+ public interface IScan
+ {
+ /// <summary>
+ /// Scan has Started
+ /// </summary>
+ event EventHandler ScanStarted;
+
+ /// <summary>
+ /// Scan has completed
+ /// </summary>
+ event ScanCompletedStatus ScanCompleted;
+
+ /// <summary>
+ /// Scan process has changed to a new title
+ /// </summary>
+ event ScanProgessStatus ScanStatusChanged;
+
+ /// <summary>
+ /// Gets a value indicating whether IsScanning.
+ /// </summary>
+ bool IsScanning { get; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ string ActivityLog { get; }
+
+ /// <summary>
+ /// Scan a Source Path.
+ /// Title 0: scan all
+ /// </summary>
+ /// <param name="sourcePath">
+ /// Path to the file to scan
+ /// </param>
+ /// <param name="title">
+ /// int title number. 0 for scan all
+ /// </param>
+ /// <param name="postAction">
+ /// The post Action.
+ /// </param>
+ /// <param name="configuration">
+ /// The configuraiton.
+ /// </param>
+ void Scan(string sourcePath, int title, Action<bool, Source> postAction, HBConfiguration configuration);
+
+ /// <summary>
+ /// Get a Preview image for the current job and preview number.
+ /// </summary>
+ /// <param name="task">
+ /// The task.
+ /// </param>
+ /// <param name="preview">
+ /// The preview.
+ /// </param>
+ /// <param name="configuration">
+ /// The configuration.
+ /// </param>
+ /// <returns>
+ /// The <see cref="BitmapImage"/>.
+ /// </returns>
+ BitmapImage GetPreview(EncodeTask task, int preview, HBConfiguration configuration);
+
+ /// <summary>
+ /// Kill the scan
+ /// </summary>
+ void Stop();
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs b/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs
new file mode 100644
index 000000000..2aae331d6
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/LibScan.cs
@@ -0,0 +1,600 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="LibScan.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Scan a Source
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Text;
+ using System.Windows.Media.Imaging;
+
+ using HandBrake.ApplicationServices.Interop;
+ using HandBrake.ApplicationServices.Interop.EventArgs;
+ using HandBrake.ApplicationServices.Interop.HbLib;
+ using HandBrake.ApplicationServices.Interop.Interfaces;
+ using HandBrake.ApplicationServices.Interop.Json.Scan;
+ using HandBrake.ApplicationServices.Interop.Model;
+ using HandBrake.ApplicationServices.Interop.Model.Preview;
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Utilities;
+
+ using HandBrakeWPF.Services.Encode.Model;
+ using HandBrakeWPF.Services.Encode.Model.Models;
+ using HandBrakeWPF.Services.Scan.EventArgs;
+ using HandBrakeWPF.Services.Scan.Interfaces;
+ using HandBrakeWPF.Services.Scan.Model;
+
+ using Chapter = HandBrakeWPF.Services.Scan.Model.Chapter;
+ using ScanProgressEventArgs = HandBrake.ApplicationServices.Interop.EventArgs.ScanProgressEventArgs;
+ using Subtitle = HandBrakeWPF.Services.Scan.Model.Subtitle;
+ using Title = HandBrakeWPF.Services.Scan.Model.Title;
+
+ /// <summary>
+ /// Scan a Source
+ /// </summary>
+ public class LibScan : IScan
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// Lock for the log file
+ /// </summary>
+ static readonly object LogLock = new object();
+
+ /// <summary>
+ /// Log data from HandBrakeInstance
+ /// </summary>
+ private readonly StringBuilder logging;
+
+ /// <summary>
+ /// The Log File Header
+ /// </summary>
+ private readonly StringBuilder header;
+
+ /// <summary>
+ /// The Current source scan path.
+ /// </summary>
+ private string currentSourceScanPath;
+
+ /// <summary>
+ /// The log dir.
+ /// </summary>
+ private static string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
+
+ /// <summary>
+ /// The dvd info path.
+ /// </summary>
+ private string dvdInfoPath = Path.Combine(logDir, string.Format("last_scan_log{0}.txt", GeneralUtilities.ProcessId));
+
+ /// <summary>
+ /// The scan log.
+ /// </summary>
+ private StreamWriter scanLog;
+
+ /// <summary>
+ /// LibHB Instance
+ /// </summary>
+ private IHandBrakeInstance instance;
+
+ /// <summary>
+ /// The post scan operation.
+ /// </summary>
+ private Action<bool, Source> postScanOperation;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibScan"/> class.
+ /// </summary>
+ public LibScan()
+ {
+ this.logging = new StringBuilder();
+ this.header = GeneralUtilities.CreateLogHeader();
+ this.IsScanning = false;
+ }
+
+ #region Events
+
+ /// <summary>
+ /// Scan has Started
+ /// </summary>
+ public event EventHandler ScanStarted;
+
+ /// <summary>
+ /// Scan has completed
+ /// </summary>
+ public event ScanCompletedStatus ScanCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ public event ScanProgessStatus ScanStatusChanged;
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets a value indicating whether IsScanning.
+ /// </summary>
+ public bool IsScanning { get; private set; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ public string ActivityLog
+ {
+ get
+ {
+ string noLog = "There is no log information to display." + Environment.NewLine + Environment.NewLine
+ + "This window will only display logging information after you have scanned a source." + Environment.NewLine
+ + Environment.NewLine + "You can find previous log files in the log directory or by clicking the 'Open Log Directory' button above.";
+
+ return string.IsNullOrEmpty(this.logging.ToString()) ? noLog : this.header + this.logging.ToString();
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Scan a Source Path.
+ /// Title 0: scan all
+ /// </summary>
+ /// <param name="sourcePath">
+ /// Path to the file to scan
+ /// </param>
+ /// <param name="title">
+ /// int title number. 0 for scan all
+ /// </param>
+ /// <param name="postAction">
+ /// The post Action.
+ /// </param>
+ /// <param name="configuraiton">
+ /// The configuraiton.
+ /// </param>
+ public void Scan(string sourcePath, int title, Action<bool, Source> postAction, HBConfiguration configuraiton)
+ {
+ // Try to cleanup any previous scan instances.
+ if (this.instance != null)
+ {
+ try
+ {
+ lock (LogLock)
+ {
+ this.scanLog.Close();
+ this.scanLog.Dispose();
+ this.scanLog = null;
+ }
+ this.instance.Dispose();
+ }
+ catch (Exception)
+ {
+ // Do Nothing
+ }
+ }
+
+ // Handle the post scan operation.
+ this.postScanOperation = postAction;
+
+ // Clear down the logging
+ this.logging.Clear();
+
+ try
+ {
+ // Make we don't pick up a stale last_scan_log_xyz.txt (and that we have rights to the file)
+ if (File.Exists(this.dvdInfoPath))
+ {
+ File.Delete(this.dvdInfoPath);
+ }
+ }
+ catch (Exception exc)
+ {
+ Debug.WriteLine(exc);
+ }
+
+ if (!Directory.Exists(Path.GetDirectoryName(this.dvdInfoPath)))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(this.dvdInfoPath));
+ }
+
+ // Create a new scan log.
+ this.scanLog = new StreamWriter(this.dvdInfoPath);
+
+ // Create a new HandBrake Instance.
+ HandBrakeUtils.MessageLogged += this.HandBrakeInstanceMessageLogged;
+ HandBrakeUtils.ErrorLogged += this.HandBrakeInstanceErrorLogged;
+ this.instance = HandBrakeInstanceManager.GetScanInstance(configuraiton.Verbosity);
+ this.instance.ScanProgress += this.InstanceScanProgress;
+ this.instance.ScanCompleted += this.InstanceScanCompleted;
+
+ // Start the scan on a back
+ this.ScanSource(sourcePath, title, configuraiton.PreviewScanCount, configuraiton);
+ }
+
+ /// <summary>
+ /// Kill the scan
+ /// </summary>
+ public void Stop()
+ {
+ try
+ {
+ this.ServiceLogMessage("Stopping Scan.");
+ this.IsScanning = false;
+ this.instance.StopScan();
+
+ lock (LogLock)
+ {
+ if (this.scanLog != null)
+ {
+ this.scanLog.Close();
+ this.scanLog.Dispose();
+ this.scanLog = null;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // Do Nothing.
+ }
+ }
+
+ /// <summary>
+ /// Get a Preview image for the current job and preview number.
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ /// <param name="preview">
+ /// The preview.
+ /// </param>
+ /// <param name="configuraiton">
+ /// The configuraiton.
+ /// </param>
+ /// <returns>
+ /// The <see cref="BitmapImage"/>.
+ /// </returns>
+ public BitmapImage GetPreview(EncodeTask job, int preview, HBConfiguration configuraiton)
+ {
+ if (this.instance == null)
+ {
+ return null;
+ }
+
+ BitmapImage bitmapImage = null;
+ try
+ {
+ PreviewSettings settings = new PreviewSettings
+ {
+ Cropping = new Cropping(job.Cropping),
+ MaxWidth = job.MaxWidth ?? 0,
+ MaxHeight = job.MaxHeight ?? 0,
+ KeepDisplayAspect = job.KeepDisplayAspect,
+ TitleNumber = job.Title,
+ Anamorphic = job.Anamorphic,
+ Modulus = job.Modulus,
+ Width = job.Width ?? 0,
+ Height = job.Height ?? 0,
+ PixelAspectX = job.PixelAspectX,
+ PixelAspectY = job.PixelAspectY
+ };
+
+ bitmapImage = this.instance.GetPreview(settings, preview);
+ }
+ catch (AccessViolationException e)
+ {
+ Console.WriteLine(e);
+ }
+
+ return bitmapImage;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Start a scan for a given source path and title
+ /// </summary>
+ /// <param name="sourcePath">
+ /// Path to the source file
+ /// </param>
+ /// <param name="title">
+ /// the title number to look at
+ /// </param>
+ /// <param name="previewCount">
+ /// The preview Count.
+ /// </param>
+ /// <param name="configuraiton">
+ /// The configuraiton.
+ /// </param>
+ private void ScanSource(object sourcePath, int title, int previewCount, HBConfiguration configuraiton)
+ {
+ try
+ {
+ this.logging.Clear();
+
+ string source = sourcePath.ToString().EndsWith("\\") ? string.Format("\"{0}\\\\\"", sourcePath.ToString().TrimEnd('\\'))
+ : "\"" + sourcePath + "\"";
+ this.currentSourceScanPath = source;
+
+ this.IsScanning = true;
+
+ TimeSpan minDuration = TimeSpan.FromSeconds(configuraiton.MinScanDuration);
+
+ HandBrakeUtils.SetDvdNav(!configuraiton.IsDvdNavDisabled);
+
+ this.ServiceLogMessage("Starting Scan ...");
+ this.instance.StartScan(sourcePath.ToString(), previewCount, minDuration, title != 0 ? title : 0);
+
+ if (this.ScanStarted != null)
+ this.ScanStarted(this, System.EventArgs.Empty);
+ }
+ catch (Exception exc)
+ {
+ this.ServiceLogMessage("Scan Failed ..." + Environment.NewLine + exc);
+ this.Stop();
+
+ if (this.ScanCompleted != null)
+ this.ScanCompleted(this, new ScanCompletedEventArgs(false, exc, "An Error has occured in ScanService.ScanSource()", null));
+ }
+ }
+
+ #endregion
+
+ #region HandBrakeInstance Event Handlers
+ /// <summary>
+ /// Scan Completed Event Handler
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void InstanceScanCompleted(object sender, System.EventArgs e)
+ {
+ this.ServiceLogMessage("Scan Finished ...");
+
+ // Write the log file out before we start processing incase we crash.
+ try
+ {
+ if (this.scanLog != null)
+ {
+ this.scanLog.Flush();
+ }
+ }
+ catch (Exception exc)
+ {
+ Debug.WriteLine(exc);
+ }
+
+ HandBrakeUtils.MessageLogged -= this.HandBrakeInstanceMessageLogged;
+ HandBrakeUtils.ErrorLogged -= this.HandBrakeInstanceErrorLogged;
+
+ // TODO -> Might be a better place to fix this.
+ string path = this.currentSourceScanPath;
+ if (this.currentSourceScanPath.Contains("\""))
+ {
+ path = this.currentSourceScanPath.Trim('\"');
+ }
+
+ // Process into internal structures.
+ Source sourceData = null;
+ if (this.instance != null && this.instance.Titles != null)
+ {
+ sourceData = new Source { Titles = ConvertTitles(this.instance.Titles), ScanPath = path };
+ }
+
+ this.IsScanning = false;
+
+ if (this.postScanOperation != null)
+ {
+ try
+ {
+ this.postScanOperation(true, sourceData);
+ }
+ catch (Exception exc)
+ {
+ Debug.WriteLine(exc);
+ }
+
+ this.postScanOperation = null; // Reset
+ }
+ else
+ {
+ if (this.ScanCompleted != null) this.ScanCompleted(this, new ScanCompletedEventArgs(false, null, string.Empty, sourceData));
+ }
+ }
+
+ /// <summary>
+ /// Scan Progress Event Handler
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void InstanceScanProgress(object sender, ScanProgressEventArgs e)
+ {
+ if (this.ScanStatusChanged != null)
+ {
+ HandBrakeWPF.Services.Scan.EventArgs.ScanProgressEventArgs eventArgs =
+ new HandBrakeWPF.Services.Scan.EventArgs.ScanProgressEventArgs
+ {
+ CurrentTitle = e.CurrentTitle,
+ Titles = e.Titles,
+ Percentage = Math.Round((decimal)e.Progress * 100, 0)
+ };
+
+ this.ScanStatusChanged(this, eventArgs);
+ }
+ }
+
+ /// <summary>
+ /// Log a message
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The MessageLoggedEventArgs.
+ /// </param>
+ private void HandBrakeInstanceErrorLogged(object sender, MessageLoggedEventArgs e)
+ {
+ this.LogMessage(e.Message);
+ }
+
+ /// <summary>
+ /// Log a message
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The MessageLoggedEventArgs.
+ /// </param>
+ private void HandBrakeInstanceMessageLogged(object sender, MessageLoggedEventArgs e)
+ {
+ this.LogMessage(e.Message);
+ }
+
+ /// <summary>
+ /// Convert Interop Title objects to App Services Title object
+ /// </summary>
+ /// <param name="titles">
+ /// The titles.
+ /// </param>
+ /// <returns>
+ /// The convert titles.
+ /// </returns>
+ internal static List<Title> ConvertTitles(JsonScanObject titles)
+ {
+ List<Title> titleList = new List<Title>();
+ foreach (SourceTitle title in titles.TitleList)
+ {
+ Title converted = new Title
+ {
+ TitleNumber = title.Index,
+ Duration = new TimeSpan(0, title.Duration.Hours, title.Duration.Minutes, title.Duration.Seconds),
+ Resolution = new Size(title.Geometry.Width, title.Geometry.Height),
+ AngleCount = title.AngleCount,
+ ParVal = new Size(title.Geometry.PAR.Num, title.Geometry.PAR.Den),
+ AutoCropDimensions = new Cropping
+ {
+ Top = title.Crop[0],
+ Bottom = title.Crop[1],
+ Left = title.Crop[2],
+ Right = title.Crop[3]
+ },
+ Fps = ((double)title.FrameRate.Num) / title.FrameRate.Den,
+ SourceName = title.Path,
+ MainTitle = titles.MainFeature == title.Index,
+ Playlist = title.Type == 1 ? string.Format(" {0:d5}.MPLS", title.Playlist).Trim() : null,
+ FramerateNumerator = title.FrameRate.Num,
+ FramerateDenominator = title.FrameRate.Den
+ };
+
+ int currentTrack = 1;
+ foreach (SourceChapter chapter in title.ChapterList)
+ {
+ string chapterName = !string.IsNullOrEmpty(chapter.Name) ? chapter.Name : string.Empty;
+ converted.Chapters.Add(new Chapter(currentTrack, chapterName, new TimeSpan(chapter.Duration.Hours, chapter.Duration.Minutes, chapter.Duration.Seconds)));
+ currentTrack++;
+ }
+
+ int currentAudioTrack = 1;
+ foreach (SourceAudioTrack track in title.AudioList)
+ {
+ converted.AudioTracks.Add(new Audio(currentAudioTrack, track.Language, track.LanguageCode, track.Description, string.Empty, track.SampleRate, track.BitRate));
+ currentAudioTrack++;
+ }
+
+ int currentSubtitleTrack = 1;
+ foreach (SourceSubtitleTrack track in title.SubtitleList)
+ {
+ SubtitleType convertedType = new SubtitleType();
+
+ switch (track.Source)
+ {
+ case 0:
+ convertedType = SubtitleType.VobSub;
+ break;
+ case 4:
+ convertedType = SubtitleType.UTF8Sub;
+ break;
+ case 5:
+ convertedType = SubtitleType.TX3G;
+ break;
+ case 6:
+ convertedType = SubtitleType.SSA;
+ break;
+ case 1:
+ convertedType = SubtitleType.SRT;
+ break;
+ case 2:
+ convertedType = SubtitleType.CC;
+ break;
+ case 3:
+ convertedType = SubtitleType.CC;
+ break;
+ case 7:
+ convertedType = SubtitleType.PGS;
+ break;
+ }
+
+ bool canBurn = HBFunctions.hb_subtitle_can_burn(track.Source) > 0;
+ bool canSetForcedOnly = HBFunctions.hb_subtitle_can_force(track.Source) > 0;
+
+ converted.Subtitles.Add(new Subtitle(track.Source, currentSubtitleTrack, track.Language, track.LanguageCode, convertedType, canBurn, canSetForcedOnly));
+ currentSubtitleTrack++;
+ }
+
+ titleList.Add(converted);
+ }
+
+ return titleList;
+ }
+
+ /// <summary>
+ /// The log message.
+ /// </summary>
+ /// <param name="message">
+ /// The message.
+ /// </param>
+ private void LogMessage(string message)
+ {
+ lock (LogLock)
+ {
+ if (this.scanLog != null)
+ {
+ this.scanLog.WriteLine(message);
+ }
+
+ this.logging.AppendLine(message);
+ }
+ }
+
+ /// <summary>
+ /// The service log message.
+ /// </summary>
+ /// <param name="message">
+ /// The message.
+ /// </param>
+ protected void ServiceLogMessage(string message)
+ {
+ this.LogMessage(string.Format("# {0}", message));
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Services/Scan/Model/Audio.cs b/win/CS/HandBrakeWPF/Services/Scan/Model/Audio.cs
new file mode 100644
index 000000000..1f0a2af98
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/Model/Audio.cs
@@ -0,0 +1,181 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="Audio.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// An object represending an AudioTrack associated with a Title, in a DVD
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.Model
+{
+ using System;
+
+ /// <summary>
+ /// An object represending an AudioTrack associated with a Title, in a DVD
+ /// </summary>
+ [Serializable]
+ public class Audio
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Audio"/> class.
+ /// </summary>
+ public Audio()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Audio"/> 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 Audio(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>
+ /// 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 (this.Description == "None Found")
+ {
+ return this.Description;
+ }
+
+ return string.Format("{0} {1}", this.TrackNumber, this.Description);
+ }
+
+ /// <summary>
+ /// The equals.
+ /// </summary>
+ /// <param name="other">
+ /// The other.
+ /// </param>
+ /// <returns>
+ /// The System.Boolean.
+ /// </returns>
+ public bool Equals(Audio other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return other.TrackNumber == this.TrackNumber && object.Equals(other.Language, this.Language) && object.Equals(other.LanguageCode, this.LanguageCode) && object.Equals(other.Format, this.Format);
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><filterpriority>2</filterpriority>
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj.GetType() != typeof(Audio))
+ {
+ return false;
+ }
+
+ return this.Equals((Audio)obj);
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a particular type.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ /// <filterpriority>2</filterpriority>
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int result = this.TrackNumber;
+ result = (result * 397) ^ (this.Language != null ? this.Language.GetHashCode() : 0);
+ result = (result * 397) ^ (this.LanguageCode != null ? this.LanguageCode.GetHashCode() : 0);
+ result = (result * 397) ^ (this.Format != null ? this.Format.GetHashCode() : 0);
+ return result;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Services/Scan/Model/Chapter.cs b/win/CS/HandBrakeWPF/Services/Scan/Model/Chapter.cs
new file mode 100644
index 000000000..bc817a29c
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/Model/Chapter.cs
@@ -0,0 +1,70 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="Chapter.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// An object representing a Chapter aosciated with a Title, in a DVD
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.Model
+{
+ using System;
+ using System.Globalization;
+
+ /// <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>
+ /// 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 this.ChapterNumber.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Services/Scan/Model/Source.cs b/win/CS/HandBrakeWPF/Services/Scan/Model/Source.cs
new file mode 100644
index 000000000..b8e3bcdaf
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/Model/Source.cs
@@ -0,0 +1,57 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="Source.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// An object representing a scanned DVD
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.Model
+{
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+ using System.Xml.Serialization;
+
+ /// <summary>
+ /// An object representing a scanned DVD
+ /// </summary>
+ [DataContract]
+ public class Source
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Source"/> class.
+ /// Default constructor for this object
+ /// </summary>
+ public Source()
+ {
+ this.Titles = new List<Title>();
+ }
+
+ /// <summary>
+ /// Gets or sets ScanPath.
+ /// The Path used by the Scan Service.
+ /// </summary>
+ [DataMember]
+ public string ScanPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets Titles. A list of titles from the source
+ /// </summary>
+ [DataMember]
+ [XmlIgnore]
+ public List<Title> Titles { get; set; }
+
+ /// <summary>
+ /// Copy this Source to another Source Model
+ /// </summary>
+ /// <param name="source">
+ /// The source.
+ /// </param>
+ public void CopyTo(Source source)
+ {
+ source.Titles = this.Titles;
+ source.ScanPath = this.ScanPath;
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Services/Scan/Model/Subtitle.cs b/win/CS/HandBrakeWPF/Services/Scan/Model/Subtitle.cs
new file mode 100644
index 000000000..920ef711e
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/Model/Subtitle.cs
@@ -0,0 +1,207 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="Subtitle.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// An object that represents a subtitle associated with a Title, in a DVD
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.Model
+{
+ using System;
+ using System.Xml.Serialization;
+
+ using HandBrake.ApplicationServices.Utilities;
+
+ using HandBrakeWPF.Services.Encode.Model.Models;
+
+ /// <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="sourceId">
+ /// The source Id.
+ /// </param>
+ /// <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>
+ /// <param name="canBurn">
+ /// The can Burn.
+ /// </param>
+ /// <param name="canForce">
+ /// The can Force.
+ /// </param>
+ public Subtitle(int sourceId, int trackNumber, string language, string languageCode, SubtitleType subtitleType, bool canBurn, bool canForce)
+ {
+ this.SourceId = sourceId;
+ this.TrackNumber = trackNumber;
+ this.Language = language;
+ this.LanguageCode = languageCode;
+ this.SubtitleType = subtitleType;
+ this.CanBurnIn = canBurn;
+ this.CanForce = canForce;
+ }
+
+ /// <summary>
+ /// Gets or sets the source id.
+ /// </summary>
+ public int SourceId { get; set; }
+
+ /// <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 the language code clean.
+ /// TODO Remove this after fixing language code.
+ /// </summary>
+ public string LanguageCodeClean
+ {
+ get
+ {
+ if (this.LanguageCode != null)
+ {
+ return this.LanguageCode.Replace("iso639-2: ", string.Empty).Trim();
+ }
+ return string.Empty;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether can burn in.
+ /// </summary>
+ [XmlIgnore]
+ public bool CanBurnIn { get; private set; }
+
+ /// <summary>
+ /// Gets a value indicating whether can force.
+ /// </summary>
+ [XmlIgnore]
+ public bool CanForce { get; private 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<Enum>.GetDescription(this.SubtitleType);
+ }
+ }
+
+ /// <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 this.SubtitleType == SubtitleType.ForeignAudioSearch ? "Foreign Audio Scan" : string.Format("{0} {1} ({2})", this.TrackNumber, this.Language, this.TypeString);
+ }
+
+ /// <summary>
+ /// The equals.
+ /// </summary>
+ /// <param name="other">
+ /// The other.
+ /// </param>
+ /// <returns>
+ /// The System.Boolean.
+ /// </returns>
+ public bool Equals(Subtitle other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+ return other.TrackNumber == this.TrackNumber && object.Equals(other.Language, this.Language) && object.Equals(other.LanguageCode, this.LanguageCode) && object.Equals(other.SubtitleType, this.SubtitleType);
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><filterpriority>2</filterpriority>
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj.GetType() != typeof(Subtitle))
+ {
+ return false;
+ }
+
+ return this.Equals((Subtitle)obj);
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a particular type.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ /// <filterpriority>2</filterpriority>
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int result = this.TrackNumber;
+ result = (result * 397) ^ (this.Language != null ? this.Language.GetHashCode() : 0);
+ result = (result * 397) ^ (this.LanguageCode != null ? this.LanguageCode.GetHashCode() : 0);
+ result = (result * 397) ^ this.SubtitleType.GetHashCode();
+ return result;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrakeWPF/Services/Scan/Model/Title.cs b/win/CS/HandBrakeWPF/Services/Scan/Model/Title.cs
new file mode 100644
index 000000000..32fddb36d
--- /dev/null
+++ b/win/CS/HandBrakeWPF/Services/Scan/Model/Title.cs
@@ -0,0 +1,159 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="Title.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// An object that represents a single Title of a DVD
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Services.Scan.Model
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ using HandBrake.ApplicationServices.Interop.Model;
+
+ /// <summary>
+ /// An object that represents a single Title of a DVD
+ /// </summary>
+ public class Title
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Title"/> class.
+ /// </summary>
+ public Title()
+ {
+ this.AudioTracks = new List<Audio>();
+ 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<Audio> 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 type.
+ /// HB_DVD_TYPE, HB_BD_TYPE, HB_STREAM_TYPE, HB_FF_STREAM_TYPE
+ /// </summary>
+ public int Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets Playlist.
+ /// </summary>
+ public string Playlist { 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 decimal 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 the video frame rate numerator.
+ /// </summary>
+ public int FramerateNumerator { get; set; }
+
+ /// <summary>
+ /// Gets or sets the video frame rate denominator.
+ /// </summary>
+ public int FramerateDenominator { 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>
+ /// Calcuate the Duration
+ /// </summary>
+ /// <param name="startPoint">The Start Point (Chapters)</param>
+ /// <param name="endPoint">The End Point (Chapters)</param>
+ /// <returns>A Timespan</returns>
+ public TimeSpan CalculateDuration(int startPoint, int endPoint)
+ {
+ IEnumerable<Chapter> chapers =
+ this.Chapters.Where(c => c.ChapterNumber >= startPoint && c.ChapterNumber <= endPoint);
+
+ TimeSpan duration = TimeSpan.FromSeconds(0.0);
+ duration = chapers.Aggregate(duration, (current, chapter) => current + chapter.Duration);
+
+ return duration;
+ }
+
+ /// <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()
+ {
+ if (!string.IsNullOrEmpty(this.Playlist) && !this.Playlist.StartsWith(" "))
+ {
+ this.Playlist = string.Format(" {0}", this.Playlist);
+ }
+
+ return string.Format("{0}{1} ({2:00}:{3:00}:{4:00})", this.TitleNumber, this.Playlist, this.Duration.Hours, this.Duration.Minutes, this.Duration.Seconds);
+ }
+ }
+} \ No newline at end of file