// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// The preset service manages HandBrake's presets
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrakeWPF.Services.Presets
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using HandBrake.ApplicationServices.Exceptions;
using HandBrake.ApplicationServices.Model.Encoding;
using HandBrake.ApplicationServices.Utilities;
using HandBrakeWPF.Model.Preset;
using HandBrakeWPF.Services.Presets.Interfaces;
///
/// The preset service manages HandBrake's presets
///
public class PresetService : IPresetService
{
#region Private Variables
///
/// XML Serializer
///
private static readonly XmlSerializer Ser = new XmlSerializer(typeof(List));
///
/// User Preset Default Catgory Name
///
public static string UserPresetCatgoryName = "User Presets";
///
/// The User Preset file
///
private readonly string userPresetFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\user_presets.xml";
///
/// The Built In Presets File
///
private readonly string builtInPresetFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\presets.xml";
///
/// A Collection of presets
///
private readonly ObservableCollection presets = new ObservableCollection();
#endregion
///
/// Gets a Collection of presets.
///
public ObservableCollection Presets
{
get
{
return this.presets;
}
}
///
/// Gets or sets LastPresetAdded.
///
public Preset LastPresetAdded { get; set; }
///
/// Gets the DefaultPreset.
///
public Preset DefaultPreset
{
get
{
return this.presets.FirstOrDefault(p => p.IsDefault);
}
}
#region Public Methods
///
/// The load.
///
public void Load()
{
// If the preset file doesn't exist. Create it.
if (!File.Exists(this.builtInPresetFile))
{
this.UpdateBuiltInPresets();
}
this.LoadPresets();
}
///
/// Add a new preset to the system.
/// Performs an Update if it already exists
///
///
/// A Preset to add
///
///
/// True if added,
/// False if name already exists
///
public bool Add(Preset preset)
{
if (this.CheckIfPresetExists(preset.Name) == false)
{
this.presets.Add(preset);
this.LastPresetAdded = preset;
// Update the presets file
this.UpdatePresetFiles();
return true;
}
this.Update(preset);
return true;
}
///
/// Update a preset
///
///
/// The updated preset
///
public void Update(Preset update)
{
// TODO - Change this to be a lookup
foreach (Preset preset in this.presets)
{
if (preset.Name == update.Name)
{
preset.Task = update.Task;
preset.UsePictureFilters = update.UsePictureFilters;
preset.PictureSettingsMode = update.PictureSettingsMode;
preset.Category = update.Category;
preset.Description = update.Description;
// Update the presets file
this.UpdatePresetFiles();
break;
}
}
}
///
/// Remove a preset with a given name from either the built in or user preset list.
///
///
/// The Preset to remove
///
public void Remove(Preset preset)
{
if (preset == null || preset.IsDefault)
{
return;
}
this.presets.Remove(preset);
this.UpdatePresetFiles();
}
///
/// Remove a group of presets by category
///
///
/// The Category to remove
///
public void RemoveGroup(string category)
{
List removeList = this.presets.Where(p => p.Category == category).ToList();
foreach (Preset preset in removeList)
{
if (preset.IsDefault)
{
// Skip default preset
continue;
}
this.presets.Remove(preset);
}
this.UpdatePresetFiles();
}
///
/// Set Default Preset
///
///
/// The name.
///
public void SetDefault(Preset name)
{
foreach (Preset preset in this.presets)
{
preset.IsDefault = false;
}
name.IsDefault = true;
this.UpdatePresetFiles();
}
///
/// Get a Preset
///
///
/// The name of the preset to get
///
///
/// A Preset or null object
///
public Preset GetPreset(string name)
{
return this.presets.FirstOrDefault(item => item.Name == name);
}
///
/// Clear Built-in Presets
///
public void ClearBuiltIn()
{
List remove = this.presets.Where(p => p.IsBuildIn).ToList();
foreach (Preset preset in remove)
{
this.presets.Remove(preset);
}
}
///
/// Clear all presets
///
public void ClearAll()
{
this.presets.Clear();
}
///
/// Reads the CLI's CLI output format and load's them into the preset List Preset
///
public void UpdateBuiltInPresets()
{
// Create a new tempory file and execute the CLI to get the built in Presets.
string handbrakeCLIPath = Path.Combine(System.Windows.Forms.Application.StartupPath, "HandBrakeCLI.exe");
string presetsPath = Path.Combine(Path.GetTempPath(), "temp_presets.dat");
string strCmdLine = String.Format(@"cmd /c """"{0}"" --preset-list >""{1}"" 2>&1""", handbrakeCLIPath, presetsPath);
ProcessStartInfo getPresets = new ProcessStartInfo("CMD.exe", strCmdLine) { WindowStyle = ProcessWindowStyle.Hidden };
Process hbproc = Process.Start(getPresets);
hbproc.WaitForExit();
hbproc.Dispose();
hbproc.Close();
// Clear the current built in Presets and now parse the tempory Presets file.
this.ClearBuiltIn();
if (File.Exists(presetsPath))
{
using (StreamReader presetInput = new StreamReader(presetsPath))
{
StringBuilder contents = new StringBuilder();
string category = String.Empty;
while (!presetInput.EndOfStream)
{
string line = presetInput.ReadLine();
contents.AppendLine(line);
// Found the beginning of a preset block )
if (line != null && line.Contains("<") && !line.Contains("<<"))
{
category = line.Replace("<", string.Empty).Trim();
}
// Found a preset
if (line != null && line.Contains("+"))
{
Regex r = new Regex("(: )"); // Split on hyphens.
string[] presetName = r.Split(line);
Preset newPreset = new Preset
{
Category = category,
Name = presetName[0].Replace("+", string.Empty).Trim(),
Version = VersionHelper.GetVersion(),
Description = string.Empty, // Maybe one day we will populate this.
IsBuildIn = true,
UsePictureFilters = true,
Task = QueryParserUtility.Parse(presetName[2])
};
if (newPreset.Name == "iPod")
{
newPreset.Task.KeepDisplayAspect = true;
}
newPreset.Task.AllowedPassthruOptions = new AllowedPassthru(true); // We don't want to override the built-in preset
if (newPreset.Name == "Normal")
{
newPreset.IsDefault = true;
}
this.presets.Add(newPreset);
}
}
// Verify we have presets.
if (this.presets.Count == 0)
{
throw new GeneralApplicationException("Failed to load built-in presets.", "Restarting HandBrake may resolve this issue", new Exception(contents.ToString()));
}
}
}
else
{
// Something really bad must have happened if the file is missing.
throw new GeneralApplicationException("Preset Export Failed. Unable to load in Presets.", "Please restart HandBrake", null);
}
// Finally, Create a new or update the current Presets.xml file
this.UpdatePresetFiles();
}
///
/// Check if the built in Presets stored are not out of date.
/// Update them if they are.
///
/// true if out of date
public bool CheckIfPresetsAreOutOfDate()
{
// Update built-in Presets if the built-in Presets belong to an older version.
if (this.presets.Count != 0)
{
List preset = this.presets.Where(p => p.IsBuildIn).ToList();
if (preset.Count > 0)
{
if (preset[0].Version != VersionHelper.GetVersion())
{
this.UpdateBuiltInPresets();
return true;
}
}
}
return false;
}
///
/// Check if the preset "name" exists in either Presets or UserPresets lists.
///
/// Name of the preset
/// True if found
public bool CheckIfPresetExists(string name)
{
return name == string.Empty || this.presets.Any(item => item.Name == name);
}
///
/// Returns a value if the preset can be updated / resaved
///
///
/// The name.
///
///
/// True if it's not a built-in preset, false otherwise.
///
public bool CanUpdatePreset(string name)
{
return this.presets.Where(preset => preset.Name == name).Any(preset => preset.IsBuildIn == false);
}
#endregion
#region Private Helpers
///
/// Recover from a courrpted preset file
/// Add .old to the current filename, and delete the current file.
///
///
/// The broken presets file.
///
private static void RecoverFromCorruptedPresetFile(string file)
{
try
{
// Recover from Error.
if (File.Exists(file))
{
string disabledFile = string.Format("{0}.{1}", file, GeneralUtilities.ProcessId);
File.Move(file, disabledFile);
if (File.Exists(file))
{
File.Delete(file);
}
}
}
catch (IOException)
{
// Give up
}
}
///
/// Load in the Built-in and User presets into the collection
///
private void LoadPresets()
{
// First clear the Presets arraylists
this.presets.Clear();
// Load in the Presets from Presets.xml
try
{
if (File.Exists(this.builtInPresetFile))
{
using (StreamReader reader = new StreamReader(this.builtInPresetFile))
{
List list = (List)Ser.Deserialize(reader);
foreach (Preset preset in list)
{
preset.IsBuildIn = true;
// Older versions did not have this flag so explicitly make sure it is set.
this.presets.Add(preset);
}
}
}
}
catch (Exception)
{
RecoverFromCorruptedPresetFile(this.builtInPresetFile);
this.UpdateBuiltInPresets();
}
// Load in the users Presets from UserPresets.xml
try
{
if (File.Exists(this.userPresetFile))
{
using (StreamReader reader = new StreamReader(this.userPresetFile))
{
List list = (List)Ser.Deserialize(reader);
foreach (Preset preset in list)
{
this.presets.Add(preset);
}
}
}
}
catch (Exception exc)
{
RecoverFromCorruptedPresetFile(this.userPresetFile);
throw new GeneralApplicationException("HandBrake has detected a problem with your presets.", "Your old presets file has been renamed so that it doesn't get loaded on next launch.", exc);
}
}
///
/// Update the preset files
///
private void UpdatePresetFiles()
{
try
{
string directory = Path.GetDirectoryName(this.userPresetFile);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
using (FileStream strm = new FileStream(this.builtInPresetFile, FileMode.Create, FileAccess.Write))
{
Ser.Serialize(strm, this.presets.Where(p => p.IsBuildIn).ToList());
}
using (FileStream strm = new FileStream(this.userPresetFile, FileMode.Create, FileAccess.Write))
{
Ser.Serialize(strm, this.presets.Where(p => p.IsBuildIn == false).ToList());
}
}
catch (Exception exc)
{
throw new GeneralApplicationException("Unable to write to the presets file.", "The details section below may indicate why this error has occured.", exc);
}
}
#endregion
}
}