// --------------------------------------------------------------------------------------------------------------------
//
// 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 HandBrakeWPF.Services.Scan
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Media.Imaging;
using HandBrake.Interop.Interop;
using HandBrake.Interop.Interop.Interfaces;
using HandBrake.Interop.Interop.Json.Scan;
using HandBrake.Interop.Interop.Model;
using HandBrake.Interop.Interop.Model.Encoding;
using HandBrake.Interop.Interop.Model.Preview;
using HandBrake.Interop.Model;
using HandBrakeWPF.Instance;
using HandBrakeWPF.Services.Encode.Model;
using HandBrakeWPF.Services.Interfaces;
using HandBrakeWPF.Services.Scan.EventArgs;
using HandBrakeWPF.Services.Scan.Factories;
using HandBrakeWPF.Services.Scan.Interfaces;
using HandBrakeWPF.Services.Scan.Model;
using HandBrakeWPF.Utilities;
using ILog = Logging.Interfaces.ILog;
using LogService = Logging.LogService;
using ScanProgressEventArgs = HandBrake.Interop.Interop.EventArgs.ScanProgressEventArgs;
using Title = Model.Title;
///
/// Scan a Source
///
public class LibScan : IScan, IDisposable
{
private readonly ILog log = null;
private readonly IUserSettingService userSettingService;
private TitleFactory titleFactory = new TitleFactory();
private string currentSourceScanPath;
private IHandBrakeInstance instance;
private Action postScanOperation;
private bool isCancelled = false;
public LibScan(ILog logService, IUserSettingService userSettingService)
{
this.log = logService;
this.userSettingService = userSettingService;
this.IsScanning = false;
}
public event EventHandler ScanStarted;
public event ScanCompletedStatus ScanCompleted;
public event ScanProgessStatus ScanStatusChanged;
public bool IsScanning { get; private set; }
///
/// Scan a Source Path.
/// Title 0: scan all
///
///
/// Path to the file to scan
///
///
/// int title number. 0 for scan all
///
///
/// The post Action.
///
public void Scan(string sourcePath, int title, Action postAction)
{
// Try to cleanup any previous scan instances.
if (this.instance != null)
{
try
{
this.instance.Dispose();
}
catch (Exception)
{
// Do Nothing
}
}
this.isCancelled = false;
// Handle the post scan operation.
this.postScanOperation = postAction;
// Create a new HandBrake Instance.
this.instance = HandBrakeInstanceManager.GetScanInstance(this.userSettingService.GetUserSetting(UserSettingConstants.Verbosity));
this.instance.ScanProgress += this.InstanceScanProgress;
this.instance.ScanCompleted += this.InstanceScanCompleted;
// Start the scan on a back
this.ScanSource(sourcePath, title, this.userSettingService.GetUserSetting(UserSettingConstants.PreviewScanCount));
}
///
/// Kill the scan
///
public void Stop()
{
try
{
this.ServiceLogMessage("Manually Stopping Scan ...");
this.IsScanning = false;
var handBrakeInstance = this.instance;
if (handBrakeInstance != null)
{
handBrakeInstance.StopScan();
handBrakeInstance.ScanProgress -= this.InstanceScanProgress;
handBrakeInstance.ScanCompleted -= this.InstanceScanCompleted;
handBrakeInstance.Dispose();
this.instance = null;
}
}
catch (Exception exc)
{
this.ServiceLogMessage(exc.ToString());
}
finally
{
this.ScanCompleted?.Invoke(this, new ScanCompletedEventArgs(this.isCancelled, null, null, null));
this.instance = null;
this.ServiceLogMessage("Scan Stopped ...");
}
}
///
/// Cancel the current scan.
///
public void Cancel()
{
this.isCancelled = true;
this.Stop();
}
///
/// Get a Preview image for the current job and preview number.
///
///
/// The job.
///
///
/// The preview.
///
///
/// The .
///
public BitmapImage GetPreview(EncodeTask job, int preview)
{
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
};
RawPreviewData bitmapData = this.instance.GetPreview(settings, preview, job.DeinterlaceFilter != DeinterlaceFilter.Off);
bitmapImage = BitmapUtilities.ConvertToBitmapImage(BitmapUtilities.ConvertByteArrayToBitmap(bitmapData));
}
catch (AccessViolationException e)
{
Debug.WriteLine(e);
}
return bitmapImage;
}
///
/// The service log message.
///
///
/// The message.
///
protected void ServiceLogMessage(string message)
{
this.log.LogMessage(string.Format("{0} # {1}{0}", Environment.NewLine, message));
}
///
/// Start a scan for a given source path and title
///
///
/// Path to the source file
///
///
/// the title number to look at
///
///
/// The preview Count.
///
private void ScanSource(object sourcePath, int title, int previewCount)
{
try
{
string source = sourcePath.ToString().EndsWith("\\") ? string.Format("\"{0}\\\\\"", sourcePath.ToString().TrimEnd('\\'))
: "\"" + sourcePath + "\"";
this.currentSourceScanPath = source;
this.IsScanning = true;
TimeSpan minDuration = TimeSpan.FromSeconds(this.userSettingService.GetUserSetting(UserSettingConstants.MinScanDuration));
HandBrakeUtils.SetDvdNav(!this.userSettingService.GetUserSetting(UserSettingConstants.DisableLibDvdNav));
this.ServiceLogMessage("Starting Scan ...");
this.instance.StartScan(sourcePath.ToString(), previewCount, minDuration, title != 0 ? title : 0);
this.ScanStarted?.Invoke(this, System.EventArgs.Empty);
}
catch (Exception exc)
{
this.ServiceLogMessage("Scan Failed ..." + Environment.NewLine + exc);
this.Stop();
}
}
///
/// Scan Completed Event Handler
///
///
/// The sender.
///
///
/// The EventArgs.
///
private void InstanceScanCompleted(object sender, System.EventArgs e)
{
try
{
this.ServiceLogMessage("Processing Scan Information ...");
bool cancelled = this.isCancelled;
this.isCancelled = false;
// 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?.Titles != null)
{
sourceData = new Source { Titles = this.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
this.ServiceLogMessage("Scan Finished for Queue Edit ...");
}
else
{
this.ScanCompleted?.Invoke(
this,
new ScanCompletedEventArgs(cancelled, null, string.Empty, sourceData));
this.ServiceLogMessage("Scan Finished ...");
}
}
finally
{
var handBrakeInstance = this.instance;
if (handBrakeInstance != null)
{
handBrakeInstance.ScanProgress -= this.InstanceScanProgress;
handBrakeInstance.ScanCompleted -= this.InstanceScanCompleted;
}
}
}
///
/// Scan Progress Event Handler
///
///
/// The sender.
///
///
/// The EventArgs.
///
private void InstanceScanProgress(object sender, ScanProgressEventArgs e)
{
if (this.ScanStatusChanged != null)
{
EventArgs.ScanProgressEventArgs eventArgs =
new EventArgs.ScanProgressEventArgs
{
CurrentTitle = e.CurrentTitle,
Titles = e.Titles,
Percentage = Math.Round((decimal)e.Progress * 100, 0)
};
this.ScanStatusChanged(this, eventArgs);
}
}
///
/// Convert Interop Title objects to App Services Title object
///
///
/// The titles.
///
///
/// The convert titles.
///
private List ConvertTitles(JsonScanObject titles)
{
List titleList = new List();
foreach (SourceTitle title in titles.TitleList)
{
Title converted = this.titleFactory.CreateTitle(title, titles.MainFeature);
titleList.Add(converted);
}
return titleList;
}
public void Dispose()
{
if (this.instance != null)
{
try
{
this.instance.Dispose();
this.instance = null;
}
catch (Exception e)
{
this.ServiceLogMessage("Unable to Dispose of LibScan: " + e);
}
}
}
}
}