FF-1253 - track sorter

This commit is contained in:
John Andrews
2024-02-01 22:19:31 +13:00
parent 72aefd13a4
commit c7c78f62c6
2 changed files with 160 additions and 39 deletions

View File

@@ -97,16 +97,16 @@ public class FfmpegBuilderTrackSorter : FfmpegBuilderNode
/// <summary>
/// Processes the streams
/// </summary>
/// <param name="logger">the logger parameters</param>
/// <param name="args">the node 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
internal bool ProcessStreams<T>(NodeParameters args, 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);
var orderedStreams = SortStreams(args, streams);
// Replace the unsorted items with the sorted ones
for (int i = 0; i < streams.Count; i++)
@@ -117,22 +117,22 @@ public class FfmpegBuilderTrackSorter : FfmpegBuilderNode
return true;
}
internal List<T> SortStreams<T>(List<T> streams) where T : FfmpegStream
internal List<T> SortStreams<T>(NodeParameters args, List<T> streams) where T : FfmpegStream
{
if (streams?.Any() != true || Sorters?.Any() != true)
return streams;
return streams.OrderBy(stream => GetSortKey(stream))
return streams.OrderBy(stream => GetSortKey(args, stream))
.ToList();
}
private string GetSortKey<T>(T stream) where T : FfmpegStream
private string GetSortKey<T>(NodeParameters args, 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();
var sortValue = Math.Round(SortValue<T>(args, stream, Sorters[i])).ToString();
// Trim the sort value to 15 characters
string trimmedValue = sortValue[..Math.Min(sortValue.Length, 15)];
@@ -174,22 +174,44 @@ public class FfmpegBuilderTrackSorter : FfmpegBuilderNode
/// Calculates the sort value for a stream property based on the specified sorter.
/// </summary>
/// <typeparam name="T">Type of the stream.</typeparam>
/// <param name="args">the node parameters</param>
/// <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
public static double SortValue<T>(NodeParameters args, 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;
string comparison = sorter.Value ?? string.Empty;
if (comparison.StartsWith("{") && comparison.EndsWith("}"))
{
comparison = comparison[1..^1];
if (args?.Variables?.TryGetValue(comparison, out object variable) == true)
comparison = variable?.ToString() ?? string.Empty;
else
comparison = string.Empty;
}
else if (args?.Variables?.TryGetValue(comparison, out object oVariable) == true)
{
comparison = oVariable?.ToString() ?? string.Empty;
}
if (property == nameof(stream.Language))
{
comparison = string.Join("|",
comparison.Split('|', StringSplitOptions.RemoveEmptyEntries)
.Select(LanguageHelper.GetIso2Code));
}
var value = property switch
{
nameof(FfmpegStream.Codec) => stream.Codec,
nameof(AudioStream.Bitrate) => (stream is FfmpegAudioStream audioStream) ? audioStream?.Stream?.Bitrate : null,
nameof(FfmpegStream.Language) => LanguageHelper.GetIso2Code(stream.Language),
_ => stream.GetType().GetProperty(property)?.GetValue(stream, null)
};

View File

@@ -15,7 +15,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnSorters()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -32,7 +32,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(3, sorted[0].Index);
@@ -50,7 +50,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnChannels()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -66,7 +66,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var result = trackSorter.ProcessStreams(logger, streams);
var result = trackSorter.ProcessStreams(args, streams);
// Assert
Assert.AreEqual(2, streams[0].Index);
@@ -83,7 +83,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnLanguageThenCodec()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -100,7 +100,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var result = trackSorter.ProcessStreams(logger, streams);
var result = trackSorter.ProcessStreams(args, streams);
// Assert
Assert.AreEqual(3, streams[0].Index);
@@ -117,7 +117,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnCustomMathOperation()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -133,7 +133,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var result = trackSorter.ProcessStreams(logger, streams);
var result = trackSorter.ProcessStreams(args, streams);
// Assert
Assert.AreEqual(2, streams[0].Index);
@@ -151,23 +151,23 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnRegex()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(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 = 1, Channels = 2, Language = "en", Codec = "ac3" },
new() { Index = 2, Channels = 5.1f, Language = "fr", Codec = "eac3" },
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);
var result = trackSorter.ProcessStreams(args, streams);
// Assert
Assert.AreEqual(1, streams[0].Index);
@@ -175,16 +175,16 @@ public class FfmpegBuilder_TrackSorterTests
Assert.AreEqual(2, streams[2].Index);
// Additional assertions for logging
Assert.AreEqual("1 / en / aac / 2.0", streams[0].ToString());
Assert.AreEqual("1 / en / ac3 / 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());
Assert.AreEqual("2 / fr / eac3 / 5.1", streams[2].ToString());
}
[TestMethod]
public void ProcessStreams_SortsStreamsBasedOnMultipleSorters()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -209,7 +209,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var result = trackSorter.ProcessStreams(logger, streams);
var result = trackSorter.ProcessStreams(args, streams);
// Assert
@@ -239,7 +239,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnBitrate()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -256,7 +256,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(3, sorted[0].Index);
@@ -275,7 +275,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnChannelsAsc()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -292,7 +292,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(1, sorted[0].Index);
@@ -311,13 +311,13 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnChannelsDesc()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(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 = 3, Channels = 7.0999999996f, 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 }},
};
@@ -328,7 +328,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(3, sorted[0].Index);
@@ -348,7 +348,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnBitrateInvert()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -365,7 +365,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(2, sorted[0].Index);
@@ -384,7 +384,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnBitrateAndCodec()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -402,7 +402,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(4, sorted[0].Index);
@@ -426,7 +426,7 @@ public class FfmpegBuilder_TrackSorterTests
public void ProcessStreams_SortsStreamsBasedOnBitrateUnit()
{
// Arrange
var logger = new TestLogger();
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
@@ -443,7 +443,7 @@ public class FfmpegBuilder_TrackSorterTests
};
// Act
var sorted = trackSorter.SortStreams(streams);
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(2, sorted[0].Index);
@@ -457,6 +457,105 @@ public class FfmpegBuilder_TrackSorterTests
Assert.AreEqual(140_000_000, sorted[2].Stream.Bitrate);
Assert.AreEqual(20_000_000, sorted[3].Stream.Bitrate);
}
[TestMethod]
public void ProcessStreams_SortsStreamsBasedOnLanguage()
{
// Arrange
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
new() { Index = 1, Channels = 2, Language = "eng", Codec = "aac" },
new() { Index = 2, Channels = 2, Language = "fr", Codec = "aac" },
new() { Index = 3, Channels = 5.1f, Language = "en", Codec = "eac3" },
new() { Index = 4, Channels = 5.1f, Language = "ger", Codec = "ac3" },
new() { Index = 5, Channels = 5.1f, Language = "German", Codec = "dts" },
new() { Index = 6, Channels = 5.1f, Language = "English", Codec = "aac" },
new() { Index = 7, Channels = 5.1f, Language = "eng", Codec = "ac3" },
};
// Mock sorters by different properties
trackSorter.Sorters = new List<KeyValuePair<string, string>>
{
new("Language", "en")
};
// Act
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(1, sorted[0].Index);
Assert.AreEqual(3, sorted[1].Index);
Assert.AreEqual(6, sorted[2].Index);
Assert.AreEqual(7, sorted[3].Index);
// non english
Assert.AreEqual(2, sorted[4].Index);
Assert.AreEqual(4, sorted[5].Index);
Assert.AreEqual(5, sorted[6].Index);
// Additional assertions for logging
Assert.AreEqual("eng", sorted[0].Language);
Assert.AreEqual("en", sorted[1].Language);
Assert.AreEqual("English", sorted[2].Language);
Assert.AreEqual("eng", sorted[3].Language);
Assert.AreEqual("fr", sorted[4].Language);
Assert.AreEqual("ger", sorted[5].Language);
Assert.AreEqual("German", sorted[6].Language);
}
[TestMethod]
public void ProcessStreams_SortsStreamsBasedOnLanguageRegex()
{
// Arrange
var args = new NodeParameters(new TestLogger());
var trackSorter = new FfmpegBuilderTrackSorter();
List<FfmpegAudioStream> streams = new List<FfmpegAudioStream>
{
new() { Index = 1, Channels = 2, Language = "eng", Codec = "aac" },
new() { Index = 2, Channels = 2, Language = "fr", Codec = "aac" },
new() { Index = 3, Channels = 5.1f, Language = "en", Codec = "eac3" },
new() { Index = 4, Channels = 5.1f, Language = "ger", Codec = "ac3" },
new() { Index = 5, Channels = 5.1f, Language = "German", Codec = "dts" },
new() { Index = 6, Channels = 5.1f, Language = "chi", Codec = "aac" },
new() { Index = 7, Channels = 5.1f, Language = "mao", Codec = "ac3" },
};
// Mock sorters by different properties
trackSorter.Sorters = new List<KeyValuePair<string, string>>
{
new("Language", "en|deu")
};
// Act
var sorted = trackSorter.SortStreams(args, streams);
// Assert
Assert.AreEqual(1, sorted[0].Index);
Assert.AreEqual(3, sorted[1].Index);
Assert.AreEqual(4, sorted[2].Index);
Assert.AreEqual(5, sorted[3].Index);
// non english
Assert.AreEqual(2, sorted[4].Index);
Assert.AreEqual(6, sorted[5].Index);
Assert.AreEqual(7, sorted[6].Index);
// Additional assertions for logging
Assert.AreEqual("eng", sorted[0].Language);
Assert.AreEqual("en", sorted[1].Language);
Assert.AreEqual("ger", sorted[2].Language);
Assert.AreEqual("German", sorted[3].Language);
Assert.AreEqual("fr", sorted[4].Language);
Assert.AreEqual("chi", sorted[5].Language);
Assert.AreEqual("mao", sorted[6].Language);
}
}
#endif