// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// Parse a CLI Query
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrake.ApplicationServices.Utilities
{
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using HandBrake.ApplicationServices.Model;
using HandBrake.ApplicationServices.Model.Encoding;
using HandBrake.Interop.Model;
using HandBrake.Interop.Model.Encoding;
///
/// Parse a CLI Query
///
public class QueryParserUtility
{
/**
* TODO
* - Add support for PointToPointMode = Seconds or Frames
**/
///
/// The Culture
///
private static readonly CultureInfo Culture = new CultureInfo("en-US", false);
///
/// Takes in a query which can be in any order and parses it.
/// All varibles are then set so they can be used elsewhere.
///
/// A ClI Query
/// A Parsed Query
public static EncodeTask Parse(string input)
{
var parsed = new EncodeTask();
#region Regular Expressions
// Source
Match title = Regex.Match(input, @"-t ([0-9]+)");
Match chapters = Regex.Match(input, @"-c ([0-9-]+)");
// Output Settings
Match format = Regex.Match(input, @"-f ([a-zA-Z0-9]+)");
Match grayscale = Regex.Match(input, @" -g");
Match ipodAtom = Regex.Match(input, @" -I");
// Picture Settings Tab
Match width = Regex.Match(input, @"-w ([0-9]+)");
Match height = Regex.Match(input, @"-l ([0-9]+)");
Match maxWidth = Regex.Match(input, @"-X ([0-9]+)");
Match maxHeight = Regex.Match(input, @"-Y ([0-9]+)");
Match crop = Regex.Match(input, @"--crop ([0-9]*):([0-9]*):([0-9]*):([0-9]*)");
Match looseAnamorphic = Regex.Match(input, @"--loose-anamorphic");
Match strictAnamorphic = Regex.Match(input, @"--strict-anamorphic");
Match customAnamorphic = Regex.Match(input, @"--custom-anamorphic");
Match keepDisplayAsect = Regex.Match(input, @"--keep-display-aspect");
Match displayWidth = Regex.Match(input, @"--display-width ([0-9]*)");
Match pixelAspect = Regex.Match(input, @"--pixel-aspect ([0-9]*):([0-9]*)");
Match modulus = Regex.Match(input, @"--modulus ([0-9]*)");
// Picture Settings - Filters
Match decomb = Regex.Match(input, @" --decomb");
Match decombValue = Regex.Match(input, @" --decomb=([a-zA-Z0-9.:""\\]*)");
Match deinterlace = Regex.Match(input, @"--deinterlace=\""([a-zA-Z0-9.:]*)\""");
Match denoise = Regex.Match(input, @"--denoise=\""([a-zA-Z0-9.:]*)\""");
Match nlmeans = Regex.Match(input, @"--nlmeans=\""([a-zA-Z0-9.:]*)\""");
Match nlmeansTune = Regex.Match(input, @"--nlmeans-tune=\""([a-zA-Z0-9.:]*)\""");
Match deblock = Regex.Match(input, @"--deblock=([0-9:]*)");
Match detelecine = Regex.Match(input, @"--detelecine");
Match detelecineValue = Regex.Match(input, @" --detelecine=\""([a-zA-Z0-9.:]*)\""");
// Video Settings Tab
Match videoEncoder = Regex.Match(input, @"-e ([a-zA-Z0-9]+)");
Match videoFramerate = Regex.Match(input, @"-r ([0-9.]+)");
Match videoBitrate = Regex.Match(input, @"-b ([0-9]+)");
Match videoQuality = Regex.Match(input, @"-q ([0-9.]+)");
Match twoPass = Regex.Match(input, @" -2");
Match turboFirstPass = Regex.Match(input, @" -T");
Match optimizeMP4 = Regex.Match(input, @" -O");
Match pfr = Regex.Match(input, @" --pfr");
Match cfr = Regex.Match(input, @" --cfr");
// Audio Settings Tab
Match audioTrackMixes = Regex.Match(input, @"-6 ([0-9a-zA-Z,]+)");
Match audioEncoders = Regex.Match(input, @"-E ([a-zA-Z0-9+,:\*]+)");
Match audioBitrates = Regex.Match(input, @"-B ([0-9a-zA-Z,]+)"); // Auto = a-z
Match audioSampleRates = Regex.Match(input, @"-R ([0-9a-zA-Z.,]+)"); // Auto = a-z
Match drcValues = Regex.Match(input, @"-D ([0-9.,]+)");
Match gainValues = Regex.Match(input, @"--gain=([0-9.,-]+)");
Match fallbackEncoder = Regex.Match(input, @"--audio-fallback([a-zA-Z0-9:=\s ]*)");
Match allowedPassthru = Regex.Match(input, @"--audio-copy-mask([a-zA-Z0-9:,=\s ]*)");
// Chapters Tab
Match chapterMarkers = Regex.Match(input, @" -m");
Match chapterMarkersFileMode = Regex.Match(input, @"--markers");
// Advanced Tab
Match advanced = Regex.Match(input, @"-x ([.,/a-zA-Z0-9=:-]*)");
Match x264Preset = Regex.Match(input, @"--x264-preset([=a-zA-Z0-9\s ]*)");
Match x264Tune = Regex.Match(input, @"--x264-tune([=,a-zA-Z0-9\s ]*)");
Match h264Profile = Regex.Match(input, @"--h264-profile([=a-zA-Z0-9\s ]*)");
Match x264Profile = Regex.Match(input, @"--x264-profile([=a-zA-Z0-9\s ]*)");
Match h264Level = Regex.Match(input, @"--h264-level([=a-zA-Z0-9.\s ]*)");
Match x265Profile = Regex.Match(input, @"--x265-profile([=a-zA-Z0-9\s ]*)");
Match x265Tune = Regex.Match(input, @"--x265-tune([=,a-zA-Z0-9\s ]*)");
Match x265Preset = Regex.Match(input, @"--x265-preset([=a-zA-Z0-9\s ]*)");
#endregion
#region Set Varibles
try
{
#region Source Tab
if (title.Success)
{
parsed.Title = int.Parse(title.ToString().Replace("-t ", string.Empty));
}
if (chapters.Success)
{
parsed.PointToPointMode = PointToPointMode.Chapters;
string[] actTitles = chapters.ToString().Replace("-c ", string.Empty).Split('-');
parsed.StartPoint = int.Parse(actTitles[0]);
if (actTitles.Length > 1)
{
parsed.EndPoint = int.Parse(actTitles[1]);
}
if ((parsed.StartPoint == 1) && (parsed.EndPoint == 0))
{
parsed.EndPoint = parsed.StartPoint;
}
}
#endregion
#region Output Settings
if (format.Success)
{
parsed.OutputFormat = Converters.GetFileFormat(format.Groups[1].ToString());
}
parsed.IPod5GSupport = ipodAtom.Success;
parsed.OptimizeMP4 = optimizeMP4.Success;
#endregion
#region Picture Tab
if (width.Success)
parsed.Width = int.Parse(width.Groups[0].Value.Replace("-w ", string.Empty));
if (height.Success)
parsed.Height = int.Parse(height.Groups[0].Value.Replace("-l ", string.Empty));
if (maxWidth.Success)
parsed.MaxWidth = int.Parse(maxWidth.Groups[0].Value.Replace("-X ", string.Empty));
if (maxHeight.Success)
parsed.MaxHeight = int.Parse(maxHeight.Groups[0].Value.Replace("-Y ", string.Empty));
if (crop.Success)
{
try
{
string values = crop.ToString().Replace("--crop ", string.Empty);
string[] actCropValues = values.Split(':');
parsed.Cropping = new Cropping(
int.Parse(actCropValues[0]),
int.Parse(actCropValues[1]),
int.Parse(actCropValues[2]),
int.Parse(actCropValues[3]));
parsed.HasCropping = true;
}
catch (Exception)
{
parsed.Cropping = null;
parsed.HasCropping = false;
// No need to do anything.
}
}
if (strictAnamorphic.Success)
parsed.Anamorphic = Anamorphic.Strict;
else if (looseAnamorphic.Success)
parsed.Anamorphic = Anamorphic.Loose;
else if (customAnamorphic.Success)
parsed.Anamorphic = Anamorphic.Custom;
else
parsed.Anamorphic = Anamorphic.None;
parsed.KeepDisplayAspect = keepDisplayAsect.Success;
if (displayWidth.Success)
parsed.DisplayWidth =
double.Parse(displayWidth.Groups[0].Value.Replace("--display-width ", string.Empty), Culture);
if (pixelAspect.Success)
parsed.PixelAspectX = int.Parse(pixelAspect.Groups[1].Value.Replace("--pixel-aspect ", string.Empty));
if (pixelAspect.Success && pixelAspect.Groups.Count >= 3)
parsed.PixelAspectY = int.Parse(pixelAspect.Groups[2].Value.Replace("--pixel-aspect ", string.Empty));
if (modulus.Success)
parsed.Modulus = int.Parse(modulus.Groups[0].Value.Replace("--modulus ", string.Empty));
#endregion
#region Filters
parsed.Decomb = Decomb.Off;
if (decomb.Success)
{
parsed.Decomb = Decomb.Default;
if (decombValue.Success)
{
string value = decombValue.ToString().Replace("--decomb=", string.Empty).Replace("\"", string.Empty).Trim();
if (value == "bob")
{
parsed.Decomb = Decomb.Bob;
}
else if (value == "fast")
{
parsed.Decomb = Decomb.Fast;
}
else
{
parsed.CustomDecomb = value;
parsed.Decomb = parsed.CustomDecomb == "7:2:6:9:1:80" ? Decomb.Fast : Decomb.Custom;
}
}
}
parsed.Deinterlace = Deinterlace.Off;
if (deinterlace.Success)
{
switch (deinterlace.ToString().Replace("--deinterlace=", string.Empty).Replace("\"", string.Empty).ToLower())
{
case "fast":
parsed.Deinterlace = Deinterlace.Fast;
break;
case "slow":
parsed.Deinterlace = Deinterlace.Slow;
break;
case "slower":
parsed.Deinterlace = Deinterlace.Slower;
break;
case "bob":
parsed.Deinterlace = Deinterlace.Bob;
break;
default:
parsed.Deinterlace = Deinterlace.Custom;
parsed.CustomDeinterlace = deinterlace.ToString().Replace("--deinterlace=", string.Empty).Replace("\"", string.Empty).ToLower();
break;
}
}
parsed.Denoise = Denoise.Off;
if (denoise.Success)
{
parsed.Denoise = Denoise.hqdn3d;
switch (denoise.ToString().Replace("--denoise=", string.Empty).Replace("\"", string.Empty))
{
case "weak":
parsed.DenoisePreset = DenoisePreset.Weak;
break;
case "medium":
parsed.DenoisePreset = DenoisePreset.Medium;
break;
case "strong":
parsed.DenoisePreset = DenoisePreset.Strong;
break;
default:
parsed.DenoisePreset = DenoisePreset.Custom;
parsed.CustomDenoise = denoise.ToString().Replace("--denoise=", string.Empty).Replace("\"", string.Empty);
break;
}
}
if (nlmeans.Success)
{
parsed.Denoise = Denoise.NLMeans;
switch (nlmeans.ToString().Replace("--nlmeans=", string.Empty).Replace("\"", string.Empty))
{
case "ultralight":
parsed.DenoisePreset = DenoisePreset.Ultralight;
break;
case "light":
parsed.DenoisePreset = DenoisePreset.Light;
break;
case "medium":
parsed.DenoisePreset = DenoisePreset.Medium;
break;
case "strong":
parsed.DenoisePreset = DenoisePreset.Strong;
break;
}
if (nlmeansTune.Success)
{
switch (nlmeansTune.ToString().Replace("--nlmeans-tune=", string.Empty).Replace("\"", string.Empty))
{
case "animation":
parsed.DenoiseTune = DenoiseTune.Animation;
break;
case "film":
parsed.DenoiseTune = DenoiseTune.Film;
break;
case "grain":
parsed.DenoiseTune = DenoiseTune.Grain;
break;
case "highmotion":
parsed.DenoiseTune = DenoiseTune.HighMotion;
break;
}
}
}
parsed.Deblock = 0;
if (deblock.Success)
{
int dval;
int.TryParse(deblock.ToString().Replace("--deblock=", string.Empty), out dval);
parsed.Deblock = dval;
}
parsed.Detelecine = Detelecine.Off;
if (detelecine.Success)
{
parsed.Detelecine = Detelecine.Default;
if (detelecineValue.Success)
{
parsed.CustomDetelecine = detelecineValue.ToString().Replace("--detelecine=", string.Empty).Replace("\"", string.Empty);
parsed.Detelecine = Detelecine.Custom;
}
}
#endregion
#region Video Settings Tab
parsed.VideoEncoder = videoEncoder.Success
? Converters.GetVideoEncoder(videoEncoder.ToString().Replace("-e ", string.Empty))
: VideoEncoder.FFMpeg;
if (videoFramerate.Success)
{
double fps;
double.TryParse(videoFramerate.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out fps);
parsed.Framerate = fps;
}
if (pfr.Success)
parsed.FramerateMode = FramerateMode.PFR;
else if (cfr.Success)
parsed.FramerateMode = FramerateMode.CFR;
else
parsed.FramerateMode = FramerateMode.VFR; // Default to VFR
parsed.Grayscale = grayscale.Success;
parsed.TwoPass = twoPass.Success;
parsed.TurboFirstPass = turboFirstPass.Success;
if (videoBitrate.Success)
{
parsed.VideoEncodeRateType = VideoEncodeRateType.AverageBitrate;
parsed.VideoBitrate = int.Parse(videoBitrate.ToString().Replace("-b ", string.Empty));
}
if (videoQuality.Success)
{
float quality = float.Parse(videoQuality.ToString().Replace("-q ", string.Empty), Culture);
parsed.Quality = quality;
parsed.VideoEncodeRateType = VideoEncodeRateType.ConstantQuality;
}
#endregion
#region Audio Tab
// Find out how many tracks we need to add by checking how many encoders or audio tracks are selected.
int encoderCount = 0;
if (audioEncoders.Success)
{
string[] audioDataCounters = audioEncoders.ToString().Replace("-E ", string.Empty).Split(',');
encoderCount = audioDataCounters.Length;
}
// Get the data from the regular expression results
string[] trackMixes = null;
string[] trackEncoders = null;
string[] trackBitrates = null;
string[] trackSamplerates = null;
string[] trackDRCvalues = null;
string[] trackGainValues = null;
if (audioTrackMixes.Success)
trackMixes = audioTrackMixes.ToString().Replace("-6 ", string.Empty).Split(',');
if (audioEncoders.Success)
trackEncoders = audioEncoders.ToString().Replace("-E ", string.Empty).Split(',');
if (audioBitrates.Success)
trackBitrates = audioBitrates.ToString().Replace("-B ", string.Empty).Split(',');
if (audioSampleRates.Success)
trackSamplerates = audioSampleRates.ToString().Replace("-R ", string.Empty).Split(',');
if (drcValues.Success)
trackDRCvalues = drcValues.ToString().Replace("-D ", string.Empty).Split(',');
if (gainValues.Success)
trackGainValues = gainValues.ToString().Replace("--gain=", string.Empty).Split(',');
// Create new Audio Track Classes and store them in the ArrayList
ObservableCollection allAudioTrackInfo = new ObservableCollection();
for (int x = 0; x < encoderCount; x++)
{
AudioTrack track = new AudioTrack();
if (trackMixes != null)
if (trackMixes.Length >= (x + 1)) // Audio Mix
track.MixDown = Converters.GetAudioMixDown(Converters.GetMixDown(trackMixes[x].Trim()));
if (trackEncoders != null)
if (trackEncoders.Length >= (x + 1)) // Audio Mix
track.Encoder = Converters.GetAudioEncoderFromCliString(trackEncoders[x].Trim());
if (trackBitrates != null)
if (trackBitrates.Length >= (x + 1)) // Audio Encoder
track.Bitrate = int.Parse(trackBitrates[x].Trim() == "auto" ? "0" : trackBitrates[x].Trim());
if (trackSamplerates != null)
if (trackSamplerates.Length >= (x + 1)) // Audio SampleRate
track.SampleRate = double.Parse(trackSamplerates[x].Replace("Auto", "0").Trim(), Culture);
if (trackDRCvalues != null)
if (trackDRCvalues.Length >= (x + 1)) // Audio DRC Values
track.DRC = double.Parse(trackDRCvalues[x].Trim(), Culture);
if (trackGainValues != null)
if (trackGainValues.Length >= (x + 1)) // Audio DRC Values
track.Gain = int.Parse(trackGainValues[x].Trim());
allAudioTrackInfo.Add(track);
}
parsed.AudioTracks = allAudioTrackInfo;
if (fallbackEncoder.Success)
{
parsed.AllowedPassthruOptions.AudioEncoderFallback =
Converters.GetAudioEncoderFromCliString(fallbackEncoder.Groups[1].ToString().Trim());
}
if (allowedPassthru.Success)
{
string[] allowedEncoders = allowedPassthru.Groups[1].ToString().Trim().Split(',');
parsed.AllowedPassthruOptions.AudioAllowAACPass = allowedEncoders.Contains("aac");
parsed.AllowedPassthruOptions.AudioAllowAC3Pass = allowedEncoders.Contains("ac3");
parsed.AllowedPassthruOptions.AudioAllowDTSHDPass = allowedEncoders.Contains("dtshd");
parsed.AllowedPassthruOptions.AudioAllowDTSPass = allowedEncoders.Contains("dts");
parsed.AllowedPassthruOptions.AudioAllowMP3Pass = allowedEncoders.Contains("mp3");
}
#endregion
#region Chapters Tab
if (chapterMarkersFileMode.Success || chapterMarkers.Success)
parsed.IncludeChapterMarkers = true;
#endregion
#region Advanced and other
if (advanced.Success)
parsed.AdvancedEncoderOptions = advanced.ToString().Replace("-x ", string.Empty);
if (x264Preset.Success)
parsed.X264Preset =
Converters.Getx264PresetFromCli(x264Preset.ToString().Replace("--x264-preset", string.Empty).Replace("=", string.Empty).Trim());
if (h264Profile.Success)
parsed.H264Profile =
Converters.Getx264ProfileFromCli(h264Profile.ToString().Replace("--h264-profile", string.Empty).Replace("=", string.Empty).Trim());
if (x264Profile.Success)
parsed.H264Profile =
Converters.Getx264ProfileFromCli(x264Profile.ToString().Replace("--x264-profile", string.Empty).Replace("=", string.Empty).Trim());
if (h264Level.Success)
parsed.H264Level =
h264Level.ToString().Replace("--h264-level", string.Empty).Replace("=", string.Empty).Trim();
if (x264Tune.Success)
{
string tuneOptions =
x264Tune.ToString().Replace("--x264-tune", string.Empty).Replace("=", string.Empty).Trim();
parsed.FastDecode = tuneOptions.Contains("fastdecode");
// Remove these options. They are not in the dropdown.
tuneOptions = tuneOptions.Replace("fastdecode", string.Empty).Replace(
",", string.Empty);
parsed.X264Tune = Converters.Getx264TuneFromCli(tuneOptions);
}
if (x265Preset.Success)
parsed.X265Preset =
Converters.Getx265PresetFromCli(x265Preset.ToString().Replace("--x265-preset", string.Empty).Replace("=", string.Empty).Trim());
if (h264Profile.Success)
parsed.H265Profile =
Converters.Getx265ProfileFromCli(h264Profile.ToString().Replace("--h265-profile", string.Empty).Replace("=", string.Empty).Trim());
if (x265Profile.Success)
parsed.H265Profile =
Converters.Getx265ProfileFromCli(x265Profile.ToString().Replace("--x265-profile", string.Empty).Replace("=", string.Empty).Trim());
if (x265Tune.Success)
{
string tuneOptions =
x265Tune.ToString().Replace("--x265-tune", string.Empty).Replace("=", string.Empty).Trim();
parsed.FastDecode = tuneOptions.Contains("fastdecode");
// Remove these options. They are not in the dropdown.
tuneOptions = tuneOptions.Replace("fastdecode", string.Empty).Replace(
",", string.Empty);
parsed.X265Tune = Converters.Getx265TuneFromCli(tuneOptions);
}
#endregion
}
catch (Exception exc)
{
throw new Exception("An error has occured in the QueryParser Utility.", exc);
}
#endregion
return parsed;
}
}
}