// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// Scan a Source
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrake.ApplicationServices.Services
{
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using HandBrake.ApplicationServices.EventArgs;
using HandBrake.ApplicationServices.Exceptions;
using HandBrake.ApplicationServices.Parsing;
using HandBrake.ApplicationServices.Services.Interfaces;
using HandBrake.ApplicationServices.Utilities;
using Parser = HandBrake.ApplicationServices.Parsing.Parser;
///
/// Scan a Source
///
public class ScanService : IScan
{
#region Private Variables
///
/// The User Setting Service
///
private readonly IUserSettingService userSettingService;
///
/// The Log File Header
///
private readonly StringBuilder header;
///
/// The CLI data parser
///
private Parser readData;
///
/// The Log Buffer
///
private StringBuilder logBuffer;
///
/// The Process belonging to the CLI
///
private Process hbProc;
///
/// The stop scan.
///
private bool cancelScan;
#endregion
///
/// Initializes a new instance of the class.
///
///
/// The user Setting Service.
///
public ScanService(IUserSettingService userSettingService)
{
this.userSettingService = userSettingService;
this.logBuffer = new StringBuilder();
header = GeneralUtilities.CreateCliLogHeader();
}
#region Events
///
/// Scan has Started
///
public event EventHandler ScanStared;
///
/// Scan has completed
///
public event ScanCompletedStatus ScanCompleted;
///
/// Encode process has progressed
///
public event ScanProgessStatus ScanStatusChanged;
#endregion
#region Public Properties
///
/// Gets a value indicating whether IsScanning.
///
public bool IsScanning { get; private set; }
///
/// Gets the Souce Data.
///
public Source SouceData { get; private set; }
///
/// Gets ActivityLog.
///
public string ActivityLog
{
get
{
string noLog = "No log data available... Log data will show here after you scan a source. \n\nOpen the log file directory to get previous log files.";
return string.IsNullOrEmpty(this.logBuffer.ToString()) ? this.header + noLog : this.header + this.logBuffer.ToString();
}
}
#endregion
#region Public Methods
///
/// Scan a Source Path.
/// Title 0: scan all
///
///
/// Path to the file to scan
///
///
/// int title number. 0 for scan all
///
///
/// The preview Count.
///
///
/// The post Scan Action.
///
public void Scan(string sourcePath, int title, int previewCount, Action postScanAction)
{
Thread t = new Thread(unused => this.ScanSource(sourcePath, title, previewCount, postScanAction));
t.Start();
}
///
/// Kill the scan
///
public void Stop()
{
try
{
// Try to clean things up as best as possible.
if (this.readData != null)
{
this.readData.OnScanProgress -= this.OnScanProgress;
}
cancelScan = true;
if (this.hbProc != null && !this.hbProc.HasExited)
{
this.hbProc.Kill();
}
}
catch
{
// We don't really need to notify the user of any errors here.
}
}
///
/// Take a Scan Log file, and process it as if it were from the CLI.
///
///
/// The path to the log file.
///
public void DebugScanLog(string path)
{
try
{
StreamReader parseLog = new StreamReader(path);
this.readData = new Parser(parseLog.BaseStream);
this.SouceData = Source.Parse(this.readData, this.userSettingService.GetUserSetting(ASUserSettingConstants.DisableLibDvdNav));
this.SouceData.ScanPath = path;
if (this.ScanCompleted != null)
{
this.ScanCompleted(this, new ScanCompletedEventArgs(false, null, string.Empty));
}
}
catch (Exception e)
{
throw new GeneralApplicationException("Debug Run Failed", string.Empty, e);
}
}
///
/// Shutdown the service.
///
public void Shutdown()
{
// Nothing to do for this implementation.
}
#endregion
#region Private Methods
///
/// Start a scan for a given source path and title
///
///
/// Path to the source file
///
///
/// the title number to look at
///
///
/// The preview Count.
///
///
/// The post Scan Action. Disables the Scan Completed Event
///
private void ScanSource(object sourcePath, int title, int previewCount, Action postScanAction)
{
try
{
this.IsScanning = true;
this.cancelScan = false;
if (this.ScanStared != null)
{
this.ScanStared(this, new EventArgs());
}
this.logBuffer = new StringBuilder();
string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\HandBrake\\logs";
string dvdInfoPath = Path.Combine(
logDir,
string.Format("last_scan_log{0}.txt", GeneralUtilities.ProcessId));
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
// Make we don't pick up a stale last_encode_log.txt (and that we have rights to the file)
if (File.Exists(dvdInfoPath))
{
File.Delete(dvdInfoPath);
}
string extraArguments = string.Empty;
if (previewCount != 10)
{
extraArguments += " --previews " + previewCount;
}
if (this.userSettingService.GetUserSetting(ASUserSettingConstants.DisableLibDvdNav))
{
extraArguments += " --no-dvdnav";
}
extraArguments += string.Format(" --min-duration={0}", this.userSettingService.GetUserSetting(ASUserSettingConstants.MinScanDuration));
if (title > 0)
{
extraArguments += " --scan ";
}
// Quick fix for "F:\\" style paths. We need \\\\ (Escaped \ twice)
string source = sourcePath.ToString().EndsWith("\\") ? string.Format("\"{0}\\\\\"", sourcePath.ToString().TrimEnd('\\'))
: "\"" + sourcePath + "\"";
string query = string.Format(@" -i {0} -t{1} {2} -v ", source, title, extraArguments);
this.hbProc = new Process
{
StartInfo =
{
FileName = handbrakeCLIPath,
Arguments = string.Format(@" -i {0} -t{1} {2} -v ", source, title, extraArguments),
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
// Start the Scan
this.hbProc.Start();
this.readData = new Parser(this.hbProc.StandardError.BaseStream);
this.readData.OnScanProgress += this.OnScanProgress;
this.SouceData = Source.Parse(this.readData, this.userSettingService.GetUserSetting(ASUserSettingConstants.DisableLibDvdNav));
this.SouceData.ScanPath = (string)sourcePath;
// Write the Buffer out to file.
using (StreamWriter scanLog = new StreamWriter(dvdInfoPath))
{
// Only write the log file to disk if it's less than 50MB.
if (this.readData.Buffer.Length < 50000000)
{
scanLog.WriteLine(header);
scanLog.WriteLine(query);
scanLog.Write(this.readData.Buffer);
this.logBuffer.AppendLine(query);
this.logBuffer.AppendLine(this.readData.Buffer.ToString());
}
else
{
throw new GeneralApplicationException(
"The Log file has not been written to disk as it has grown above the 50MB limit", " This indicates there was a problem during the scan process.", null);
}
}
this.IsScanning = false;
if (postScanAction != null)
{
postScanAction(true);
}
else
{
if (this.ScanCompleted != null)
{
if (logBuffer.ToString().Contains("scan: unrecognized file type"))
this.ScanCompleted(this, new ScanCompletedEventArgs(false, null, "Unrecognized file type."));
else
this.ScanCompleted(this, new ScanCompletedEventArgs(this.cancelScan, null, string.Empty));
}
}
}
catch (GeneralApplicationException)
{
throw;
}
catch (Exception exc)
{
this.Stop();
if (postScanAction != null)
{
postScanAction(false);
}
else
{
if (this.ScanCompleted != null)
{
this.ScanCompleted(this, new ScanCompletedEventArgs(false, exc, "An Error has occured in ScanService.ScanSource()"));
}
}
}
}
///
/// Fire an event when the scan process progresses
///
/// the sender
/// the current title being scanned
/// the total number of titles
/// The Percentage
private void OnScanProgress(object sender, int currentTitle, int titleCount, decimal percentage)
{
ScanProgressEventArgs eventArgs = new ScanProgressEventArgs
{
CurrentTitle = currentTitle,
Titles = titleCount,
Percentage = percentage
};
if (this.ScanStatusChanged != null)
{
this.ScanStatusChanged(this, eventArgs);
}
}
#endregion
}
}