mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-02-13 03:28:33 -06:00
FF-1253 - track sorter
This commit is contained in:
@@ -20,7 +20,7 @@ public class PatternReplacer : Node
|
||||
|
||||
internal bool UnitTest = false;
|
||||
|
||||
[KeyValue(1)]
|
||||
[KeyValue(1, null)]
|
||||
[Required]
|
||||
public List<KeyValuePair<string, string>> Replacements{ get; set; }
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public class WebRequest : Node
|
||||
}
|
||||
|
||||
|
||||
[KeyValue(4)]
|
||||
[KeyValue(4, null)]
|
||||
public List<KeyValuePair<string, string>> Headers { get; set; }
|
||||
|
||||
[TextArea(5)]
|
||||
|
||||
@@ -18,7 +18,7 @@ public class EmbyUpdater: Node
|
||||
[Text(2)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[KeyValue(3)]
|
||||
[KeyValue(3, null)]
|
||||
public List<KeyValuePair<string, string>> Mapping { get; set; }
|
||||
|
||||
public override int Execute(NodeParameters args)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
[Required]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[KeyValue(3)]
|
||||
[KeyValue(3, null)]
|
||||
public List<KeyValuePair<string, string>> Mapping { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -20,7 +20,7 @@ public abstract class PlexNode:Node
|
||||
[Text(2)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[KeyValue(3)]
|
||||
[KeyValue(3, null)]
|
||||
public List<KeyValuePair<string, string>> Mapping { get; set; }
|
||||
|
||||
public override int Execute(NodeParameters args)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
[Required]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[KeyValue(3)]
|
||||
[KeyValue(3, null)]
|
||||
public List<KeyValuePair<string, string>> Mapping { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,14 @@ public class FfmpegBuilderKeepOriginalLanguage: FfmpegBuilderNode
|
||||
return changes > 0 ? 1 : 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the streams
|
||||
/// </summary>
|
||||
/// <param name="args">the node parameters</param>
|
||||
/// <param name="streams">the streams to process for deletion</param>
|
||||
/// <param name="originalLanguage">the original language of the source material</param>
|
||||
/// <typeparam name="T">the stream type</typeparam>
|
||||
/// <returns>the number of streams changed</returns>
|
||||
private int ProcessStreams<T>(NodeParameters args, List<T> streams, string originalLanguage) where T : FfmpegStream
|
||||
{
|
||||
if (streams?.Any() != true)
|
||||
|
||||
357
VideoNodes/FfmpegBuilderNodes/FfmpegBuilderTrackSorter.cs
Normal file
357
VideoNodes/FfmpegBuilderNodes/FfmpegBuilderTrackSorter.cs
Normal file
@@ -0,0 +1,357 @@
|
||||
using System.Globalization;
|
||||
using FileFlows.VideoNodes.FfmpegBuilderNodes.Models;
|
||||
using FileFlows.VideoNodes.Helpers;
|
||||
|
||||
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
|
||||
|
||||
/// <summary>
|
||||
/// FFmpeg Builder: Track Sorter
|
||||
/// </summary>
|
||||
public class FfmpegBuilderTrackSorter : FfmpegBuilderNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of output nodes
|
||||
/// </summary>
|
||||
public override int Outputs => 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon
|
||||
/// </summary>
|
||||
public override string Icon => "fas fa-sort-alpha-down";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the help URL
|
||||
/// </summary>
|
||||
public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/track-sorter";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stream type
|
||||
/// </summary>
|
||||
[Select(nameof(StreamTypeOptions), 1)]
|
||||
public string StreamType { get; set; }
|
||||
|
||||
[KeyValue(1, nameof(SorterOptions))]
|
||||
[Required]
|
||||
public List<KeyValuePair<string, string>> Sorters { get; set; }
|
||||
|
||||
private static List<ListOption> _StreamTypeOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stream type options
|
||||
/// </summary>
|
||||
public static List<ListOption> StreamTypeOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_StreamTypeOptions == null)
|
||||
{
|
||||
_StreamTypeOptions = new List<ListOption>
|
||||
{
|
||||
new() { Label = "Audio", Value = "Audio" },
|
||||
new() { Label = "Subtitle", Value = "Subtitle" }
|
||||
};
|
||||
}
|
||||
|
||||
return _StreamTypeOptions;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ListOption> _SorterOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sorter options
|
||||
/// </summary>
|
||||
public static List<ListOption> SorterOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_SorterOptions == null)
|
||||
{
|
||||
_SorterOptions = new List<ListOption>
|
||||
{
|
||||
new() { Label = "Bitrate", Value = "Bitrate" },
|
||||
new() { Label = "Bitrate Reversed", Value = "BitrateDesc" },
|
||||
new() { Label = "Channels", Value = "Channels" },
|
||||
new() { Label = "Channels Reversed", Value = "ChannelsDesc" },
|
||||
new() { Label = "Codec", Value = "Codec" },
|
||||
new() { Label = "Codec Reversed", Value = "CodecDesc" },
|
||||
new() { Label = "Language", Value = "Language" },
|
||||
new() { Label = "Language Reversed", Value = "LanguageDesc" },
|
||||
};
|
||||
}
|
||||
|
||||
return _SorterOptions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the flow element
|
||||
/// </summary>
|
||||
/// <param name="args">the node parameters</param>
|
||||
/// <returns>the next output node</returns>
|
||||
public override int Execute(NodeParameters args)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the streams
|
||||
/// </summary>
|
||||
/// <param name="logger">the logger parameters</param>
|
||||
/// <param name="streams">the streams to process for deletion</param>
|
||||
/// <typeparam name="T">the stream type</typeparam>
|
||||
/// <returns>if any changes were made</returns>
|
||||
internal bool ProcessStreams<T>(ILogger logger, List<T> streams, int sortIndex = 0) where T : FfmpegStream
|
||||
{
|
||||
if (streams?.Any() != true || Sorters?.Any() != true || sortIndex >= Sorters.Count)
|
||||
return false;
|
||||
|
||||
var orderedStreams = SortStreams(streams);
|
||||
|
||||
// Replace the unsorted items with the sorted ones
|
||||
for (int i = 0; i < streams.Count; i++)
|
||||
{
|
||||
streams[i] = orderedStreams[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal List<T> SortStreams<T>(List<T> streams) where T : FfmpegStream
|
||||
{
|
||||
if (streams?.Any() != true || Sorters?.Any() != true)
|
||||
return streams;
|
||||
|
||||
return streams.OrderBy(stream => GetSortKey(stream))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private string GetSortKey<T>(T stream) where T : FfmpegStream
|
||||
{
|
||||
string sortKey = "";
|
||||
|
||||
for (int i = 0; i < Sorters.Count; i++)
|
||||
{
|
||||
var sortValue = Math.Round(SortValue<T>(stream, Sorters[i])).ToString();
|
||||
// Trim the sort value to 15 characters
|
||||
string trimmedValue = sortValue[..Math.Min(sortValue.Length, 15)];
|
||||
|
||||
// Pad the trimmed value with left zeros if needed
|
||||
string paddedValue = trimmedValue.PadLeft(15, '0');
|
||||
|
||||
// Concatenate the padded value to the sort key
|
||||
sortKey += paddedValue;
|
||||
}
|
||||
|
||||
return sortKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if two lists are the same
|
||||
/// </summary>
|
||||
/// <param name="original">the original list</param>
|
||||
/// <param name="reordered">the reordered list</param>
|
||||
/// <typeparam name="T">the type of items</typeparam>
|
||||
/// <returns>true if the lists are the same, otherwise false</returns>
|
||||
public bool AreSame<T>(List<T> original, List<T> reordered) where T : FfmpegStream
|
||||
{
|
||||
for (int i = 0; i < reordered.Count; i++)
|
||||
{
|
||||
if (reordered[i] != original[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the sort value for a stream property based on the specified sorter.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the stream.</typeparam>
|
||||
/// <param name="stream">The stream instance.</param>
|
||||
/// <param name="sorter">The key-value pair representing the sorter.</param>
|
||||
/// <returns>The calculated sort value for the specified property and sorter.</returns>
|
||||
public static double SortValue<T>(T stream, KeyValuePair<string, string> sorter) where T : FfmpegStream
|
||||
{
|
||||
string property = sorter.Key;
|
||||
bool invert = property.EndsWith("Desc");
|
||||
if (invert)
|
||||
property = property[..^4]; // remove "Desc"
|
||||
|
||||
string comparison = sorter.Value;
|
||||
|
||||
var value = property switch
|
||||
{
|
||||
nameof(FfmpegStream.Codec) => stream.Codec,
|
||||
nameof(AudioStream.Bitrate) => (stream is FfmpegAudioStream audioStream) ? audioStream?.Stream?.Bitrate : null,
|
||||
_ => stream.GetType().GetProperty(property)?.GetValue(stream, null)
|
||||
};
|
||||
|
||||
double result;
|
||||
|
||||
if (value != null && value is string == false && string.IsNullOrWhiteSpace(comparison) &&
|
||||
double.TryParse(value.ToString(), out double dblValue))
|
||||
{
|
||||
// invert the bits of dbl value and return that
|
||||
result = dblValue;
|
||||
}
|
||||
else if (IsMathOperation(comparison))
|
||||
result = ApplyMathOperation(value.ToString(), comparison) ? 0 : 1;
|
||||
else if (IsRegex(comparison))
|
||||
result = Regex.IsMatch(value.ToString(), comparison, RegexOptions.IgnoreCase) ? 0 : 1;
|
||||
else if (value != null && double.TryParse(value.ToString(), out double dbl))
|
||||
result = dbl;
|
||||
else
|
||||
result = string.Equals(value?.ToString() ?? string.Empty, comparison ?? string.Empty, StringComparison.OrdinalIgnoreCase) ? 0 : 1;
|
||||
|
||||
return invert ? InvertBits(result) : result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the comparison string by handling common mistakes in units and converting them into full numbers.
|
||||
/// </summary>
|
||||
/// <param name="comparisonValue">The original comparison string to be adjusted.</param>
|
||||
/// <returns>The adjusted comparison string with corrected units or the original comparison if no adjustments are made.</returns>
|
||||
private static string AdjustComparisonValue(string comparisonValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(comparisonValue))
|
||||
return string.Empty;
|
||||
|
||||
string adjustedComparison = comparisonValue.ToLower().Trim();
|
||||
|
||||
// Handle common mistakes in units
|
||||
if (adjustedComparison.EndsWith("mbps"))
|
||||
{
|
||||
// Make an educated guess for Mbps to kbps conversion
|
||||
return adjustedComparison[..^4] switch
|
||||
{
|
||||
{ } value when double.TryParse(value, out var numericValue) => (numericValue * 1_000_000).ToString(
|
||||
CultureInfo.InvariantCulture),
|
||||
_ => comparisonValue
|
||||
};
|
||||
}
|
||||
if (adjustedComparison.EndsWith("kbps"))
|
||||
{
|
||||
// Make an educated guess for kbps to bps conversion
|
||||
return adjustedComparison[..^4] switch
|
||||
{
|
||||
{ } value when double.TryParse(value, out var numericValue) => (numericValue * 1_000).ToString(
|
||||
CultureInfo.InvariantCulture),
|
||||
_ => comparisonValue
|
||||
};
|
||||
}
|
||||
if (adjustedComparison.EndsWith("gb"))
|
||||
{
|
||||
// Make an educated guess for GB to bytes conversion
|
||||
return adjustedComparison[..^2] switch
|
||||
{
|
||||
{ } value when double.TryParse(value, out var numericValue) => (numericValue * Math.Pow(1024, 3))
|
||||
.ToString(CultureInfo.InvariantCulture),
|
||||
_ => comparisonValue
|
||||
};
|
||||
}
|
||||
if (adjustedComparison.EndsWith("tb"))
|
||||
{
|
||||
// Make an educated guess for TB to bytes conversion
|
||||
return adjustedComparison[..^2] switch
|
||||
{
|
||||
{ } value when double.TryParse(value, out var numericValue) => (numericValue * Math.Pow(1024, 4))
|
||||
.ToString(CultureInfo.InvariantCulture),
|
||||
_ => comparisonValue
|
||||
};
|
||||
}
|
||||
|
||||
return comparisonValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the bits of a double value.
|
||||
/// </summary>
|
||||
/// <param name="value">The double value to invert.</param>
|
||||
/// <returns>The inverted double value.</returns>
|
||||
private static double InvertBits(double value)
|
||||
{
|
||||
// Convert the double to a string with 15 characters above the decimal point
|
||||
string stringValue = Math.Round(value, 0).ToString("F0");
|
||||
|
||||
// Invert the digits and pad left with zeros
|
||||
char[] charArray = stringValue.PadLeft(15, '0').ToCharArray();
|
||||
for (int i = 0; i < charArray.Length; i++)
|
||||
{
|
||||
charArray[i] = (char)('9' - (charArray[i] - '0'));
|
||||
}
|
||||
|
||||
// Parse the inverted string back to a double
|
||||
double invertedDouble;
|
||||
if (double.TryParse(new string(charArray), out invertedDouble))
|
||||
{
|
||||
return invertedDouble;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle parsing error
|
||||
throw new InvalidOperationException("Failed to parse inverted double string.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the comparison string represents a mathematical operation.
|
||||
/// </summary>
|
||||
/// <param name="comparison">The comparison string to check.</param>
|
||||
/// <returns>True if the comparison is a mathematical operation, otherwise false.</returns>
|
||||
private static bool IsMathOperation(string comparison)
|
||||
{
|
||||
// Check if the comparison string starts with <=, <, >, >=, ==, or =
|
||||
return new[] { "<=", "<", ">", ">=", "==", "=" }.Any(comparison.StartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the comparison string represents a regular expression.
|
||||
/// </summary>
|
||||
/// <param name="comparison">The comparison string to check.</param>
|
||||
/// <returns>True if the comparison is a regular expression, otherwise false.</returns>
|
||||
private static bool IsRegex(string comparison)
|
||||
{
|
||||
return new[] { "?", "|", "^", "$" }.Any(ch => comparison.Contains(ch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a mathematical operation to the value based on the specified operation string.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to apply the operation to.</param>
|
||||
/// <param name="operation">The operation string representing the mathematical operation.</param>
|
||||
/// <returns>True if the mathematical operation is successful, otherwise false.</returns>
|
||||
private static bool ApplyMathOperation(string value, string operation)
|
||||
{
|
||||
// This is a basic example; you may need to handle different operators
|
||||
switch (operation.Substring(0, 2))
|
||||
{
|
||||
case "<=":
|
||||
return Convert.ToDouble(value) <= Convert.ToDouble(AdjustComparisonValue(operation[2..]));
|
||||
case ">=":
|
||||
return Convert.ToDouble(value) >= Convert.ToDouble(AdjustComparisonValue(operation[2..]));
|
||||
case "==":
|
||||
return Math.Abs(Convert.ToDouble(value) - Convert.ToDouble(AdjustComparisonValue(operation[2..]))) < 0.05f;
|
||||
case "!=":
|
||||
return Math.Abs(Convert.ToDouble(value) - Convert.ToDouble(AdjustComparisonValue(operation[2..]))) > 0.05f;
|
||||
}
|
||||
|
||||
switch (operation.Substring(0, 1))
|
||||
{
|
||||
case "<":
|
||||
return Convert.ToDouble(value) < Convert.ToDouble(AdjustComparisonValue(operation[1..]));
|
||||
case ">":
|
||||
return Convert.ToDouble(value) > Convert.ToDouble(AdjustComparisonValue(operation[1..]));
|
||||
case "=":
|
||||
return Math.Abs(Convert.ToDouble(value) - Convert.ToDouble(AdjustComparisonValue(operation[1..]))) < 0.05f;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,18 @@
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> Stream.ToString();
|
||||
{
|
||||
if (Stream != null)
|
||||
return Stream.ToString();
|
||||
// can be null in unit tests
|
||||
return string.Join(" / ", new string[]
|
||||
{
|
||||
Index.ToString(),
|
||||
Language,
|
||||
Codec,
|
||||
Title,
|
||||
Channels > 0 ? Channels.ToString("0.0") : null
|
||||
}.Where(x => string.IsNullOrWhiteSpace(x) == false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
#if(DEBUG)
|
||||
|
||||
using FileFlows.VideoNodes.FfmpegBuilderNodes;
|
||||
using FileFlows.VideoNodes.FfmpegBuilderNodes.Models;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using VideoNodes.Tests;
|
||||
|
||||
namespace FileFlows.VideoNodes.Tests.FfmpegBuilderTests;
|
||||
|
||||
[TestClass]
|
||||
public class FfmpegBuilder_TrackSorterTests
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnSorters()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac" },
|
||||
new() { Index = 2, Channels = 2, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 3, Channels = 5.1f, Language = "en", Codec = "ac3" },
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Language", "en"),
|
||||
new("Channels", ">=5.1"),
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, sorted[0].Index);
|
||||
Assert.AreEqual(1, sorted[1].Index);
|
||||
Assert.AreEqual(2, sorted[2].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual("3 / en / ac3 / 5.1", sorted[0].ToString());
|
||||
Assert.AreEqual("1 / en / aac / 2.0", sorted[1].ToString());
|
||||
Assert.AreEqual("2 / fr / aac / 2.0", sorted[2].ToString());
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnChannels()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac" },
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" },
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Channels", ">=5.1"),
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = trackSorter.ProcessStreams(logger, streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, streams[0].Index);
|
||||
Assert.AreEqual(3, streams[1].Index);
|
||||
Assert.AreEqual(1, streams[2].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual("2 / fr / aac / 5.1", streams[0].ToString());
|
||||
Assert.AreEqual("3 / en / ac3 / 7.1", streams[1].ToString());
|
||||
Assert.AreEqual("1 / en / aac / 2.0", streams[2].ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnLanguageThenCodec()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac" },
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" },
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Language", "en"),
|
||||
new("Codec", "ac3"),
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = trackSorter.ProcessStreams(logger, streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, streams[0].Index);
|
||||
Assert.AreEqual(1, streams[1].Index);
|
||||
Assert.AreEqual(2, streams[2].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual("3 / en / ac3 / 7.1", streams[0].ToString());
|
||||
Assert.AreEqual("1 / en / aac / 2.0", streams[1].ToString());
|
||||
Assert.AreEqual("2 / fr / aac / 5.1", streams[2].ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnCustomMathOperation()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac" },
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" },
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Channels", ">4.0"),
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = trackSorter.ProcessStreams(logger, streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, streams[0].Index);
|
||||
Assert.AreEqual(3, streams[1].Index);
|
||||
Assert.AreEqual(1, streams[2].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual("2 / fr / aac / 5.1", streams[0].ToString());
|
||||
Assert.AreEqual("3 / en / ac3 / 7.1", streams[1].ToString());
|
||||
Assert.AreEqual("1 / en / aac / 2.0", streams[2].ToString());
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnRegex()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac" },
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" },
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("Language", "^en$"),
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = trackSorter.ProcessStreams(logger, streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, streams[0].Index);
|
||||
Assert.AreEqual(3, streams[1].Index);
|
||||
Assert.AreEqual(2, streams[2].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual("1 / en / aac / 2.0", streams[0].ToString());
|
||||
Assert.AreEqual("3 / en / ac3 / 7.1", streams[1].ToString());
|
||||
Assert.AreEqual("2 / fr / aac / 5.1", streams[2].ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnMultipleSorters()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac" },
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" },
|
||||
new() { Index = 4, Channels = 2, Language = "fr", Codec = "ac3" },
|
||||
new() { Index = 5, Channels = 5.1f, Language = "en", Codec = "ac3" },
|
||||
new() { Index = 6, Channels = 7.1f, Language = "fr", Codec = "aac" },
|
||||
new() { Index = 7, Channels = 2, Language = "en", Codec = "ac3" },
|
||||
new() { Index = 8, Channels = 5.1f, Language = "fr", Codec = "ac3" },
|
||||
new() { Index = 9, Channels = 7.1f, Language = "en", Codec = "aac" },
|
||||
new() { Index = 10, Channels = 2, Language = "fr", Codec = "aac" },
|
||||
};
|
||||
|
||||
// Mock sorters by different properties and custom math operations
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Language", "en"),
|
||||
new("Channels", ">4.0"),
|
||||
new("Codec", "ac3")
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = trackSorter.ProcessStreams(logger, streams);
|
||||
|
||||
// Assert
|
||||
|
||||
// en
|
||||
Assert.AreEqual(3, streams[0].Index);
|
||||
Assert.AreEqual(5, streams[1].Index);
|
||||
Assert.AreEqual(9, streams[2].Index);
|
||||
Assert.AreEqual(7, streams[3].Index);
|
||||
Assert.AreEqual(1, streams[4].Index);
|
||||
|
||||
// >5 non en, 2, 6, 8 , 8 first cause ac3
|
||||
Assert.AreEqual(8, streams[5].Index);
|
||||
Assert.AreEqual(2, streams[6].Index);
|
||||
Assert.AreEqual(6, streams[7].Index);
|
||||
|
||||
|
||||
Assert.AreEqual(4, streams[8].Index);
|
||||
Assert.AreEqual(10, streams[9].Index);
|
||||
|
||||
|
||||
// Additional assertions for logging
|
||||
// Add assertions for the log messages if needed
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnBitrate()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac", Stream = new AudioStream() { Bitrate = 140_000_000 }},
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "dts" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 20_000_000 }},
|
||||
new() { Index = 4, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("Bitrate", "")
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, sorted[0].Index);
|
||||
Assert.AreEqual(1, sorted[1].Index);
|
||||
Assert.AreEqual(2, sorted[2].Index);
|
||||
Assert.AreEqual(4, sorted[3].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual(20_000_000, sorted[0].Stream.Bitrate);
|
||||
Assert.AreEqual(140_000_000, sorted[1].Stream.Bitrate);
|
||||
Assert.AreEqual(200_000_000, sorted[2].Stream.Bitrate);
|
||||
Assert.AreEqual(200_000_000, sorted[3].Stream.Bitrate);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnChannelsAsc()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac", Stream = new AudioStream() { Bitrate = 140_000_000 }},
|
||||
new() { Index = 2, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 20_000_000 }},
|
||||
new() { Index = 3, Channels = 5.1f, Language = "fr", Codec = "dts" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
new() { Index = 4, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("Channels", "")
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, sorted[0].Index);
|
||||
Assert.AreEqual(3, sorted[1].Index);
|
||||
Assert.AreEqual(2, sorted[2].Index);
|
||||
Assert.AreEqual(4, sorted[3].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual(2.0f, sorted[0].Channels);
|
||||
Assert.AreEqual(5.1f, sorted[1].Channels);
|
||||
Assert.AreEqual(7.1f, sorted[2].Channels);
|
||||
Assert.AreEqual(7.1f, sorted[3].Channels);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnChannelsDesc()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac", Stream = new AudioStream() { Bitrate = 140_000_000 }},
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "dts" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 20_000_000 }},
|
||||
new() { Index = 4, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("ChannelsDesc", "")
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, sorted[0].Index);
|
||||
Assert.AreEqual(4, sorted[1].Index);
|
||||
Assert.AreEqual(2, sorted[2].Index);
|
||||
Assert.AreEqual(1, sorted[3].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual(7.1f, sorted[0].Channels);
|
||||
Assert.AreEqual(7.1f, sorted[1].Channels);
|
||||
Assert.AreEqual(5.1f, sorted[2].Channels);
|
||||
Assert.AreEqual(2.0f, sorted[3].Channels);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnBitrateInvert()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac", Stream = new AudioStream() { Bitrate = 140_000_000 }},
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "dts" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 20_000_000 }},
|
||||
new() { Index = 4, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("BitrateDesc", "")
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, sorted[0].Index);
|
||||
Assert.AreEqual(4, sorted[1].Index);
|
||||
Assert.AreEqual(1, sorted[2].Index);
|
||||
Assert.AreEqual(3, sorted[3].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual(200_000_000, sorted[0].Stream.Bitrate);
|
||||
Assert.AreEqual(200_000_000, sorted[1].Stream.Bitrate);
|
||||
Assert.AreEqual(140_000_000, sorted[2].Stream.Bitrate);
|
||||
Assert.AreEqual(20_000_000, sorted[3].Stream.Bitrate);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnBitrateAndCodec()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac", Stream = new AudioStream() { Bitrate = 140_000_000 }},
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "dts" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 20_000_000 }},
|
||||
new() { Index = 4, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("BitrateDesc", ""),
|
||||
new ("Codec", "ac3"),
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(4, sorted[0].Index);
|
||||
Assert.AreEqual(2, sorted[1].Index);
|
||||
Assert.AreEqual(1, sorted[2].Index);
|
||||
Assert.AreEqual(3, sorted[3].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual(200_000_000, sorted[0].Stream.Bitrate);
|
||||
Assert.AreEqual(200_000_000, sorted[1].Stream.Bitrate);
|
||||
Assert.AreEqual(140_000_000, sorted[2].Stream.Bitrate);
|
||||
Assert.AreEqual(20_000_000, sorted[3].Stream.Bitrate);
|
||||
|
||||
Assert.AreEqual("ac3", sorted[0].Codec);
|
||||
Assert.AreEqual("dts", sorted[1].Codec);
|
||||
Assert.AreEqual("aac", sorted[2].Codec);
|
||||
Assert.AreEqual("ac3", sorted[3].Codec);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessStreams_SortsStreamsBasedOnBitrateUnit()
|
||||
{
|
||||
// Arrange
|
||||
var logger = new TestLogger();
|
||||
var trackSorter = new FfmpegBuilderTrackSorter();
|
||||
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
|
||||
{
|
||||
new() { Index = 1, Channels = 2, Language = "en", Codec = "aac", Stream = new AudioStream() { Bitrate = 140_000_000 }},
|
||||
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "dts" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
new() { Index = 3, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 20_000_000 }},
|
||||
new() { Index = 4, Channels = 7.1f, Language = "en", Codec = "ac3" , Stream = new AudioStream() { Bitrate = 200_000_000 }},
|
||||
};
|
||||
|
||||
// Mock sorters by different properties
|
||||
trackSorter.Sorters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new ("Bitrate", ">=150Mbps")
|
||||
};
|
||||
|
||||
// Act
|
||||
var sorted = trackSorter.SortStreams(streams);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, sorted[0].Index);
|
||||
Assert.AreEqual(4, sorted[1].Index);
|
||||
Assert.AreEqual(1, sorted[2].Index);
|
||||
Assert.AreEqual(3, sorted[3].Index);
|
||||
|
||||
// Additional assertions for logging
|
||||
Assert.AreEqual(200_000_000, sorted[0].Stream.Bitrate);
|
||||
Assert.AreEqual(200_000_000, sorted[1].Stream.Bitrate);
|
||||
Assert.AreEqual(140_000_000, sorted[2].Stream.Bitrate);
|
||||
Assert.AreEqual(20_000_000, sorted[3].Stream.Bitrate);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -365,6 +365,22 @@
|
||||
"MakeFirst-Help": "If the original language track should also become the first track."
|
||||
}
|
||||
},
|
||||
"FfmpegBuilderTrackSorter": {
|
||||
"Label": "FFMPEG Builder: Track Sorter",
|
||||
"Outputs": {
|
||||
"1": "Tracks have been modified",
|
||||
"2": "No tracks have been changed"
|
||||
},
|
||||
"Description": "This flow element sorts tracks based on sorting options set by the user.",
|
||||
"Fields": {
|
||||
"StreamType": "Type",
|
||||
"StreamType-Help": "The type of tracks that should be updated",
|
||||
"Sorters": "Sorters",
|
||||
"Sorters-Help": "Add one or more sorting options to sort the tracks by.",
|
||||
"SortersKey": "Field",
|
||||
"SortersValue": "Value"
|
||||
}
|
||||
},
|
||||
"FfmpegBuilderSubtitleClearDefault": {
|
||||
"Label": "FFMPEG Builder: Subtitle Clear Default",
|
||||
"Description": "This node will clear the default flag from subtitles.",
|
||||
|
||||
Reference in New Issue
Block a user