From f1c3145260d617dee9e8123afbf7e0ece15f9ef5 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Sun, 24 Apr 2022 12:14:26 +1200 Subject: [PATCH] Added Help links to remaining FFMPEG Builder nodes --- .../Audio/FfmpegBuilderAudioAddTrack.cs | 261 +++++++++--------- .../Audio/FfmpegBuilderAudioSetLanguage.cs | 43 +-- .../Audio/FfmpegBuilderAudioTrackRemover.cs | 89 +++--- .../Audio/FfmpegBuilderAudioTrackReorder.cs | 165 ++++++----- .../Metadata/FfmpegBuilderComskipChapters.cs | 46 +-- .../FfmpegBuilderSubtitleFormatRemover.cs | 137 ++++----- .../FfmpegBuilderSubtitleTrackRemover.cs | 71 ++--- .../Video/FfmpegBuilderRemuxToMP4.cs | 17 +- .../Video/FfmpegBuilderRemuxToMkv.cs | 16 +- .../Video/FfmpegBuilderScaler.cs | 112 ++++---- VideoNodes/LogicalNodes/DetectBlackBars.cs | 2 +- 11 files changed, 484 insertions(+), 475 deletions(-) diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs index 03014282..288f958f 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs @@ -1,173 +1,174 @@ using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode { - public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode + public override string Icon => "fas fa-volume-off"; + + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Audio-Add-Track"; + + [NumberInt(1)] + [Range(0, 100)] + [DefaultValue(1)] + public int Index { get; set; } + + + [DefaultValue("aac")] + [Select(nameof(CodecOptions), 1)] + public string Codec { get; set; } + + private static List _CodecOptions; + public static List CodecOptions { - public override string Icon => "fas fa-volume-off"; - - [NumberInt(1)] - [Range(0, 100)] - [DefaultValue(1)] - public int Index { get; set; } - - - [DefaultValue("aac")] - [Select(nameof(CodecOptions), 1)] - public string Codec { get; set; } - - private static List _CodecOptions; - public static List CodecOptions + get { - get + if (_CodecOptions == null) { - if (_CodecOptions == null) + _CodecOptions = new List { - _CodecOptions = new List - { - new ListOption { Label = "AAC", Value = "aac"}, - new ListOption { Label = "AC3", Value = "ac3"}, - new ListOption { Label = "EAC3", Value = "eac3" }, - new ListOption { Label = "MP3", Value = "mp3"}, - }; - } - return _CodecOptions; + new ListOption { Label = "AAC", Value = "aac"}, + new ListOption { Label = "AC3", Value = "ac3"}, + new ListOption { Label = "EAC3", Value = "eac3" }, + new ListOption { Label = "MP3", Value = "mp3"}, + }; } + return _CodecOptions; } + } - [DefaultValue(2f)] - [Select(nameof(ChannelsOptions), 2)] - public float Channels { get; set; } + [DefaultValue(2f)] + [Select(nameof(ChannelsOptions), 2)] + public float Channels { get; set; } - private static List _ChannelsOptions; - public static List ChannelsOptions + private static List _ChannelsOptions; + public static List ChannelsOptions + { + get { - get + if (_ChannelsOptions == null) { - if (_ChannelsOptions == null) + _ChannelsOptions = new List { - _ChannelsOptions = new List - { - new ListOption { Label = "Same as source", Value = 0}, - new ListOption { Label = "Mono", Value = 1f}, - new ListOption { Label = "Stereo", Value = 2f} - }; - } - return _ChannelsOptions; + new ListOption { Label = "Same as source", Value = 0}, + new ListOption { Label = "Mono", Value = 1f}, + new ListOption { Label = "Stereo", Value = 2f} + }; } + return _ChannelsOptions; } + } - [Select(nameof(BitrateOptions), 3)] - public int Bitrate { get; set; } + [Select(nameof(BitrateOptions), 3)] + public int Bitrate { get; set; } - private static List _BitrateOptions; - public static List BitrateOptions + private static List _BitrateOptions; + public static List BitrateOptions + { + get { - get + if (_BitrateOptions == null) { - if (_BitrateOptions == null) + _BitrateOptions = new List { - _BitrateOptions = new List - { - new ListOption { Label = "Automatic", Value = 0}, - new ListOption { Label = "64 Kbps", Value = 64}, - new ListOption { Label = "96 Kbps", Value = 96}, - new ListOption { Label = "128 Kbps", Value = 128}, - new ListOption { Label = "160 Kbps", Value = 160}, - new ListOption { Label = "192 Kbps", Value = 192}, - new ListOption { Label = "224 Kbps", Value = 224}, - new ListOption { Label = "256 Kbps", Value = 256}, - new ListOption { Label = "288 Kbps", Value = 288}, - new ListOption { Label = "320 Kbps", Value = 320}, - }; - } - return _BitrateOptions; + new ListOption { Label = "Automatic", Value = 0}, + new ListOption { Label = "64 Kbps", Value = 64}, + new ListOption { Label = "96 Kbps", Value = 96}, + new ListOption { Label = "128 Kbps", Value = 128}, + new ListOption { Label = "160 Kbps", Value = 160}, + new ListOption { Label = "192 Kbps", Value = 192}, + new ListOption { Label = "224 Kbps", Value = 224}, + new ListOption { Label = "256 Kbps", Value = 256}, + new ListOption { Label = "288 Kbps", Value = 288}, + new ListOption { Label = "320 Kbps", Value = 320}, + }; } + return _BitrateOptions; } + } - [DefaultValue("eng")] - [TextVariable(4)] - public string Language { get; set; } + [DefaultValue("eng")] + [TextVariable(4)] + public string Language { get; set; } - public override int Execute(NodeParameters args) - { - base.Init(args); + public override int Execute(NodeParameters args) + { + base.Init(args); - var audio = new FfmpegAudioStream(); + var audio = new FfmpegAudioStream(); #pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - var bestAudio = Model.AudioStreams.Where(x => System.Text.Json.JsonSerializer.Serialize(x.Stream).ToLower().Contains("commentary") == false) + var bestAudio = Model.AudioStreams.Where(x => System.Text.Json.JsonSerializer.Serialize(x.Stream).ToLower().Contains("commentary") == false) #pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - .OrderBy(x => - { - if (Language != string.Empty) - { - args.Logger?.ILog("Language: " + x.Stream.Language, x); - if (string.IsNullOrEmpty(x.Stream.Language)) - return 50; // no language specified - if (x.Stream.Language?.ToLower() != Language) - return 100; // low priority not the desired language - } - return 0; - }) - .ThenByDescending(x => x.Stream.Channels) - .ThenBy(x => x.Index) - .FirstOrDefault(); - audio.Stream = bestAudio.Stream; - - audio.EncodingParameters.AddRange(GetNewAudioTrackParameters("0:a:" + (bestAudio.Stream.TypeIndex))); - if (Index > Model.AudioStreams.Count - 1) - Model.AudioStreams.Add(audio); - else - Model.AudioStreams.Insert(Math.Max(0, Index), audio); - - return 1; - } - - - private string[] GetNewAudioTrackParameters(string source) + .OrderBy(x => { - if (Channels == 0) + if (Language != string.Empty) { - // same as source - if (Bitrate == 0) + args.Logger?.ILog("Language: " + x.Stream.Language, x); + if (string.IsNullOrEmpty(x.Stream.Language)) + return 50; // no language specified + if (x.Stream.Language?.ToLower() != Language) + return 100; // low priority not the desired language + } + return 0; + }) + .ThenByDescending(x => x.Stream.Channels) + .ThenBy(x => x.Index) + .FirstOrDefault(); + audio.Stream = bestAudio.Stream; + + audio.EncodingParameters.AddRange(GetNewAudioTrackParameters("0:a:" + (bestAudio.Stream.TypeIndex))); + if (Index > Model.AudioStreams.Count - 1) + Model.AudioStreams.Add(audio); + else + Model.AudioStreams.Insert(Math.Max(0, Index), audio); + + return 1; + } + + + private string[] GetNewAudioTrackParameters(string source) + { + if (Channels == 0) + { + // same as source + if (Bitrate == 0) + { + return new[] { - return new[] - { - "-map", source, - "-c:a:{index}", - Codec - }; - } + "-map", source, + "-c:a:{index}", + Codec + }; + } + return new[] + { + "-map", source, + "-c:a:{index}", + Codec, + "-b:a:{index}", Bitrate + "k" + }; + } + else + { + if (Bitrate == 0) + { return new[] { "-map", source, "-c:a:{index}", Codec, - "-b:a:{index}", Bitrate + "k" + "-ac:a:{index}", Channels.ToString() }; } - else + return new[] { - if (Bitrate == 0) - { - return new[] - { - "-map", source, - "-c:a:{index}", - Codec, - "-ac:a:{index}", Channels.ToString() - }; - } - return new[] - { - "-map", source, - "-c:a:{index}", - Codec, - "-ac:a:{index}", Channels.ToString(), - "-b:a:{index}", Bitrate + "k" - }; - } + "-map", source, + "-c:a:{index}", + Codec, + "-ac:a:{index}", Channels.ToString(), + "-b:a:{index}", Bitrate + "k" + }; } } } diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioSetLanguage.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioSetLanguage.cs index 57601ba1..61b89c52 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioSetLanguage.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioSetLanguage.cs @@ -1,32 +1,33 @@ using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderAudioSetLanguage : FfmpegBuilderNode { - public class FfmpegBuilderAudioSetLanguage : FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Audio-Set-Language"; + + public override int Outputs => 2; + + public override string Icon => "fas fa-comment-dots"; + + [Required] + [Text(1)] + public string Language { get; set; } + + public override int Execute(NodeParameters args) { - public override int Outputs => 2; + base.Init(args); - public override string Icon => "fas fa-comment-dots"; - - [Required] - [Text(1)] - public string Language { get; set; } - - public override int Execute(NodeParameters args) + bool changes = false; + foreach (var at in Model.AudioStreams) { - base.Init(args); - - bool changes = false; - foreach (var at in Model.AudioStreams) + if (string.IsNullOrEmpty(at.Language)) { - if (string.IsNullOrEmpty(at.Language)) - { - at.Language = Language.ToLower(); - at.ForcedChange = true; // this will ensure the language is set even if there are no changes anywhere else - changes = true; - } + at.Language = Language.ToLower(); + at.ForcedChange = true; // this will ensure the language is set even if there are no changes anywhere else + changes = true; } - return changes ? 1 : 2; } + return changes ? 1 : 2; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs index 1295abb0..5fd06e94 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackRemover.cs @@ -1,56 +1,57 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderAudioTrackRemover: FfmpegBuilderNode { - public class FfmpegBuilderAudioTrackRemover: FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Audio-Track-Remover"; + + public override string Icon => "fas fa-volume-off"; + + public override int Outputs => 2; + + [Boolean(1)] + public bool RemoveAll { get; set; } + + + [TextVariable(2)] + [ConditionEquals(nameof(RemoveAll), false)] + public string Pattern { get; set; } + + [Boolean(3)] + [ConditionEquals(nameof(RemoveAll), false)] + public bool NotMatching { get; set; } + + [Boolean(4)] + [ConditionEquals(nameof(RemoveAll), false)] + public bool UseLanguageCode { get; set; } + + public override int Execute(NodeParameters args) { - public override string Icon => "fas fa-volume-off"; - - public override int Outputs => 2; - - [Boolean(1)] - public bool RemoveAll { get; set; } - - - [TextVariable(2)] - [ConditionEquals(nameof(RemoveAll), false)] - public string Pattern { get; set; } - - [Boolean(3)] - [ConditionEquals(nameof(RemoveAll), false)] - public bool NotMatching { get; set; } - - [Boolean(4)] - [ConditionEquals(nameof(RemoveAll), false)] - public bool UseLanguageCode { get; set; } - - public override int Execute(NodeParameters args) + this.Init(args); + bool removing = false; + Regex? regex = null; + foreach(var audio in Model.AudioStreams) { - this.Init(args); - bool removing = false; - Regex? regex = null; - foreach(var audio in Model.AudioStreams) + if (RemoveAll) { - if (RemoveAll) + audio.Deleted = true; + removing = true; + continue; + } + if(regex == null) + regex = new Regex(this.Pattern, RegexOptions.IgnoreCase); + string str = UseLanguageCode ? audio.Stream.Language : audio.Stream.Title; + if (string.IsNullOrEmpty(str) == false) // if empty we always use this since we have no info to go on + { + bool matches = regex.IsMatch(str); + if (NotMatching) + matches = !matches; + if (matches) { audio.Deleted = true; removing = true; - continue; - } - if(regex == null) - regex = new Regex(this.Pattern, RegexOptions.IgnoreCase); - string str = UseLanguageCode ? audio.Stream.Language : audio.Stream.Title; - if (string.IsNullOrEmpty(str) == false) // if empty we always use this since we have no info to go on - { - bool matches = regex.IsMatch(str); - if (NotMatching) - matches = !matches; - if (matches) - { - audio.Deleted = true; - removing = true; - } } } - return removing ? 1 : 2; } + return removing ? 1 : 2; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs index a9fbbde7..74643f58 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs @@ -1,104 +1,103 @@ using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; using System.Text; -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode { - public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode + public override int Outputs => 2; + + public override string Icon => "fas fa-volume-off"; + + [StringArray(1)] + public List Languages { get; set; } + + [StringArray(2)] + public List OrderedTracks { get; set; } + + [StringArray(3)] + public List Channels { get; set; } + + public override int Execute(NodeParameters args) { - public override int Outputs => 2; + base.Init(args); - public override string Icon => "fas fa-volume-off"; + OrderedTracks = OrderedTracks?.Select(x => x.ToLower())?.ToList() ?? new(); - [StringArray(1)] - public List Languages { get; set; } + var reordered = Reorder(Model.AudioStreams); - [StringArray(2)] - public List OrderedTracks { get; set; } + bool same = AreSame(Model.AudioStreams, reordered); - [StringArray(3)] - public List Channels { get; set; } - - public override int Execute(NodeParameters args) + if (same) { - base.Init(args); + args.Logger?.ILog("No audio tracks need reordering"); + return 2; + } - OrderedTracks = OrderedTracks?.Select(x => x.ToLower())?.ToList() ?? new(); + Model.AudioStreams = reordered; + // set the first audio track as the default track + Model.MetadataParameters.AddRange(new[] { "-disposition:a:0", "default" }); - var reordered = Reorder(Model.AudioStreams); + return 1; + } - bool same = AreSame(Model.AudioStreams, reordered); + public List Reorder(List input) + { + Languages ??= new List(); + OrderedTracks ??= new List(); + Channels ??= new List(); + List actualChannels = Channels.Select(x => + { + if (float.TryParse(x, out float value)) + return value; + return -1f; + }).Where(x => x > 0f).ToList(); - if (same) + if (Languages.Any() == false && OrderedTracks.Any() == false && actualChannels.Any() == false) + return input; // nothing to do + + Languages.Reverse(); + OrderedTracks.Reverse(); + actualChannels.Reverse(); + + const int base_number = 1_000_000_000; + int count = base_number; + var debug = new StringBuilder(); + var data = input.OrderBy(x => + { + int langIndex = Languages.IndexOf(x.Stream.Language?.ToLower() ?? String.Empty); + int codecIndex = OrderedTracks.IndexOf(x.Stream.Codec?.ToLower() ?? String.Empty); + int channelIndex = actualChannels.IndexOf(x.Stream.Channels); + + int result = base_number; + if (langIndex >= 0) { - args.Logger?.ILog("No audio tracks need reordering"); - return 2; + result -= ((langIndex + 1) * 10_000_000); } - - Model.AudioStreams = reordered; - // set the first audio track as the default track - Model.MetadataParameters.AddRange(new[] { "-disposition:a:0", "default" }); - - return 1; - } - - public List Reorder(List input) - { - Languages ??= new List(); - OrderedTracks ??= new List(); - Channels ??= new List(); - List actualChannels = Channels.Select(x => + if (codecIndex >= 0) { - if (float.TryParse(x, out float value)) - return value; - return -1f; - }).Where(x => x > 0f).ToList(); - - if (Languages.Any() == false && OrderedTracks.Any() == false && actualChannels.Any() == false) - return input; // nothing to do - - Languages.Reverse(); - OrderedTracks.Reverse(); - actualChannels.Reverse(); - - const int base_number = 1_000_000_000; - int count = base_number; - var debug = new StringBuilder(); - var data = input.OrderBy(x => - { - int langIndex = Languages.IndexOf(x.Stream.Language?.ToLower() ?? String.Empty); - int codecIndex = OrderedTracks.IndexOf(x.Stream.Codec?.ToLower() ?? String.Empty); - int channelIndex = actualChannels.IndexOf(x.Stream.Channels); - - int result = base_number; - if (langIndex >= 0) - { - result -= ((langIndex + 1) * 10_000_000); - } - if (codecIndex >= 0) - { - result -= ((codecIndex + 1) * 100_000); - } - if (channelIndex >= 0) - { - result -= ((channelIndex + 1) * 1_000); - } - if (result == base_number) - result = ++count; - return result; - }).ToList(); - - return data; - } - public bool AreSame(List original, List reordered) - { - for (int i = 0; i < reordered.Count; i++) - { - if (reordered[i] != original[i]) - { - return false; - } + result -= ((codecIndex + 1) * 100_000); + } + if (channelIndex >= 0) + { + result -= ((channelIndex + 1) * 1_000); + } + if (result == base_number) + result = ++count; + return result; + }).ToList(); + + return data; + } + public bool AreSame(List original, List reordered) + { + for (int i = 0; i < reordered.Count; i++) + { + if (reordered[i] != original[i]) + { + return false; } - return true; } + return true; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Metadata/FfmpegBuilderComskipChapters.cs b/VideoNodes/FfmpegBuilderNodes/Metadata/FfmpegBuilderComskipChapters.cs index 034820bb..791eb908 100644 --- a/VideoNodes/FfmpegBuilderNodes/Metadata/FfmpegBuilderComskipChapters.cs +++ b/VideoNodes/FfmpegBuilderNodes/Metadata/FfmpegBuilderComskipChapters.cs @@ -1,30 +1,30 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderComskipChapters : FfmpegBuilderNode { - public class FfmpegBuilderComskipChapters : FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Comskip-Chapters"; + public override int Outputs => 2; + + public override int Execute(NodeParameters args) { - public override int Outputs => 2; + base.Init(args); - public override int Execute(NodeParameters args) + VideoInfo videoInfo = GetVideoInfo(args); + if (videoInfo == null) + return -1; + + if (videoInfo.Chapters?.Count > 3) { - base.Init(args); - - VideoInfo videoInfo = GetVideoInfo(args); - if (videoInfo == null) - return -1; - - if (videoInfo.Chapters?.Count > 3) - { - args.Logger.ILog(videoInfo.Chapters.Count + " chapters already detected in file"); - return 2; - } - - string tempMetaDataFile = ComskipChapters.GenerateMetaDataFile(args, videoInfo); - if (string.IsNullOrEmpty(tempMetaDataFile)) - return 2; - - Model.InputFiles.Add(tempMetaDataFile); - Model.MetadataParameters.AddRange(new[] { "-map_metadata", (Model.InputFiles.Count - 1).ToString() }); - return 1; + args.Logger.ILog(videoInfo.Chapters.Count + " chapters already detected in file"); + return 2; } + + string tempMetaDataFile = ComskipChapters.GenerateMetaDataFile(args, videoInfo); + if (string.IsNullOrEmpty(tempMetaDataFile)) + return 2; + + Model.InputFiles.Add(tempMetaDataFile); + Model.MetadataParameters.AddRange(new[] { "-map_metadata", (Model.InputFiles.Count - 1).ToString() }); + return 1; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs index e8c1f6c2..3e9837a2 100644 --- a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs +++ b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleFormatRemover.cs @@ -1,77 +1,78 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderSubtitleFormatRemover : FfmpegBuilderNode { - public class FfmpegBuilderSubtitleFormatRemover : FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Subtitle-Format-Remover"; + + public override string Icon => "fas fa-comment"; + public override int Outputs => 2; + + [Boolean(1)] + public bool RemoveAll { get; set; } + + [Checklist(nameof(Options), 2)] + public List SubtitlesToRemove { get; set; } + + private static List _Options; + public static List Options { - public override string Icon => "fas fa-comment"; - public override int Outputs => 2; - - [Boolean(1)] - public bool RemoveAll { get; set; } - - [Checklist(nameof(Options), 2)] - public List SubtitlesToRemove { get; set; } - - private static List _Options; - public static List Options + get { - get + if (_Options == null) { - if (_Options == null) + _Options = new List { - _Options = new List - { - new ListOption { Value = "mov_text", Label = "3GPP Timed Text subtitle"}, - new ListOption { Value = "ssa", Label = "ASS (Advanced SubStation Alpha) subtitle (codec ass)"}, - new ListOption { Value = "ass", Label = "ASS (Advanced SubStation Alpha) subtitle"}, - new ListOption { Value = "xsub", Label = "DivX subtitles (XSUB)" }, - new ListOption { Value = "dvbsub", Label = "DVB subtitles (codec dvb_subtitle)"}, - new ListOption { Value = "dvdsub", Label = "DVD subtitles (codec dvd_subtitle)"}, - new ListOption { Value = "dvb_teletext", Label = "DVB/Teletext Format"}, - new ListOption { Value = "text", Label = "Raw text subtitle"}, - new ListOption { Value = "subrip", Label = "SubRip subtitle"}, - new ListOption { Value = "srt", Label = "SubRip subtitle (codec subrip)"}, - new ListOption { Value = "ttml", Label = "TTML subtitle"}, - new ListOption { Value = "webvtt", Label = "WebVTT subtitle"}, - }; - } - return _Options; + new ListOption { Value = "mov_text", Label = "3GPP Timed Text subtitle"}, + new ListOption { Value = "ssa", Label = "ASS (Advanced SubStation Alpha) subtitle (codec ass)"}, + new ListOption { Value = "ass", Label = "ASS (Advanced SubStation Alpha) subtitle"}, + new ListOption { Value = "xsub", Label = "DivX subtitles (XSUB)" }, + new ListOption { Value = "dvbsub", Label = "DVB subtitles (codec dvb_subtitle)"}, + new ListOption { Value = "dvdsub", Label = "DVD subtitles (codec dvd_subtitle)"}, + new ListOption { Value = "dvb_teletext", Label = "DVB/Teletext Format"}, + new ListOption { Value = "text", Label = "Raw text subtitle"}, + new ListOption { Value = "subrip", Label = "SubRip subtitle"}, + new ListOption { Value = "srt", Label = "SubRip subtitle (codec subrip)"}, + new ListOption { Value = "ttml", Label = "TTML subtitle"}, + new ListOption { Value = "webvtt", Label = "WebVTT subtitle"}, + }; } - } - - - public override int Execute(NodeParameters args) - { - this.Init(args); - - if (RemoveAll) - { - if (Model.SubtitleStreams.Any() == false) - return 2; - foreach (var stream in Model.SubtitleStreams) - stream.Deleted = true; - return 1; - } - - - var removeCodecs = SubtitlesToRemove?.Where(x => string.IsNullOrWhiteSpace(x) == false)?.Select(x => x.ToLower())?.ToList() ?? new List(); - - if (removeCodecs.Count == 0) - return 2; // nothing to remove - - - bool removing = false; - foreach (var sub in Model.SubtitleStreams) - { - args.Logger?.ILog("Subtitle found: " + sub.Stream.Codec + ", " + sub.Stream.Title); - if (removeCodecs.Contains(sub.Stream.Codec.ToLower())) - { - sub.Deleted = true; - removing = true; - continue; - } - } - - return removing ? 1 : 2; + return _Options; } } + + + public override int Execute(NodeParameters args) + { + this.Init(args); + + if (RemoveAll) + { + if (Model.SubtitleStreams.Any() == false) + return 2; + foreach (var stream in Model.SubtitleStreams) + stream.Deleted = true; + return 1; + } + + + var removeCodecs = SubtitlesToRemove?.Where(x => string.IsNullOrWhiteSpace(x) == false)?.Select(x => x.ToLower())?.ToList() ?? new List(); + + if (removeCodecs.Count == 0) + return 2; // nothing to remove + + + bool removing = false; + foreach (var sub in Model.SubtitleStreams) + { + args.Logger?.ILog("Subtitle found: " + sub.Stream.Codec + ", " + sub.Stream.Title); + if (removeCodecs.Contains(sub.Stream.Codec.ToLower())) + { + sub.Deleted = true; + removing = true; + continue; + } + } + + return removing ? 1 : 2; + } } diff --git a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs index db73f971..d509e582 100644 --- a/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs +++ b/VideoNodes/FfmpegBuilderNodes/Subtitle/FfmpegBuilderSubtitleTrackRemover.cs @@ -1,42 +1,45 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderSubtitleTrackRemover : FfmpegBuilderNode { - public class FfmpegBuilderSubtitleTrackRemover : FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Subtitle-Track-Remover"; + + public override string Icon => "fas fa-comment"; + + public override int Outputs => 2; + + + [TextVariable(1)] + public string Pattern { get; set; } + + [Boolean(2)] + public bool NotMatching { get; set; } + + [Boolean(3)] + public bool UseLanguageCode { get; set; } + + public override int Execute(NodeParameters args) { - public override string Icon => "fas fa-comment"; - - public override int Outputs => 2; - - - [TextVariable(1)] - public string Pattern { get; set; } - - [Boolean(2)] - public bool NotMatching { get; set; } - - [Boolean(3)] - public bool UseLanguageCode { get; set; } - - public override int Execute(NodeParameters args) + this.Init(args); + bool removing = false; + var regex = new Regex(this.Pattern, RegexOptions.IgnoreCase); + foreach(var stream in Model.SubtitleStreams) { - this.Init(args); - bool removing = false; - var regex = new Regex(this.Pattern, RegexOptions.IgnoreCase); - foreach(var stream in Model.SubtitleStreams) + string str = UseLanguageCode ? stream.Stream.Language : stream.Stream.Title; + bool matches = false; + if (string.IsNullOrEmpty(str)) + matches = false; // doesn't match since its empty + else + matches = regex.IsMatch(str); + + if (NotMatching) + matches = !matches; + if (matches) { - string str = UseLanguageCode ? stream.Stream.Language : stream.Stream.Title; - if (string.IsNullOrEmpty(str) == false) // if empty we always use this since we have no info to go on - { - bool matches = regex.IsMatch(str); - if (NotMatching) - matches = !matches; - if (matches) - { - stream.Deleted = true; - removing = true; - } - } + stream.Deleted = true; + removing = true; } - return removing ? 1 : 2; } + return removing ? 1 : 2; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs index 5fc00f23..c6891399 100644 --- a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMP4.cs @@ -1,12 +1,13 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderRemuxToMP4: FfmpegBuilderNode { - public class FfmpegBuilderRemuxToMP4: FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Remux-to-MP4"; + + public override int Execute(NodeParameters args) { - public override int Execute(NodeParameters args) - { - base.Init(args); - this.Model.Extension = "mp4"; - return 1; - } + base.Init(args); + this.Model.Extension = "mp4"; + return 1; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs index 9b7844cf..c1995a14 100644 --- a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs @@ -1,12 +1,12 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderRemuxToMkv: FfmpegBuilderNode { - public class FfmpegBuilderRemuxToMkv: FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Remux-to-MKV"; + public override int Execute(NodeParameters args) { - public override int Execute(NodeParameters args) - { - base.Init(args); - this.Model.Extension = "mkv"; - return 1; - } + base.Init(args); + this.Model.Extension = "mkv"; + return 1; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs index 562967ae..74d625b7 100644 --- a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs +++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderScaler.cs @@ -1,64 +1,66 @@ -namespace FileFlows.VideoNodes.FfmpegBuilderNodes +namespace FileFlows.VideoNodes.FfmpegBuilderNodes; + +public class FfmpegBuilderScaler : FfmpegBuilderNode { - public class FfmpegBuilderScaler : FfmpegBuilderNode + public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Video-Scaler"; + + [Boolean(2)] + public bool Force { get; set; } + + + [Select(nameof(ResolutionOptions), 1)] + public string Resolution { get; set; } + + + + private static List _ResolutionOptions; + public static List ResolutionOptions { - [Boolean(2)] - public bool Force { get; set; } - - - [Select(nameof(ResolutionOptions), 1)] - public string Resolution { get; set; } - - - private static List _ResolutionOptions; - public static List ResolutionOptions + get { - get + if (_ResolutionOptions == null) { - if (_ResolutionOptions == null) + _ResolutionOptions = new List { - _ResolutionOptions = new List - { - // we use -2 here so the width is divisible by 2 and automatically scaled to - // the appropriate height, if we forced the height it could be stretched - new ListOption { Value = "640:-2", Label = "480P"}, - new ListOption { Value = "1280:-2", Label = "720P"}, - new ListOption { Value = "1920:-2", Label = "1080P"}, - new ListOption { Value = "3840:-2", Label = "4K" } - }; - } - return _ResolutionOptions; + // we use -2 here so the width is divisible by 2 and automatically scaled to + // the appropriate height, if we forced the height it could be stretched + new ListOption { Value = "640:-2", Label = "480P"}, + new ListOption { Value = "1280:-2", Label = "720P"}, + new ListOption { Value = "1920:-2", Label = "1080P"}, + new ListOption { Value = "3840:-2", Label = "4K" } + }; } - } - public override int Outputs => 2; - public override int Execute(NodeParameters args) - { - base.Init(args); - - string ffmpeg = GetFFMpegExe(args); - if (string.IsNullOrEmpty(ffmpeg)) - return -1; - - var videoInfo = GetVideoInfo(args); - if (videoInfo == null || videoInfo.VideoStreams?.Any() != true) - return -1; - - if (Force == false) - { - var resolution = ResolutionHelper.GetResolution(videoInfo); - if (resolution == ResolutionHelper.Resolution.r1080p && Resolution.StartsWith("1920")) - return 2; - else if (resolution == ResolutionHelper.Resolution.r4k && Resolution.StartsWith("3840")) - return 2; - else if (resolution == ResolutionHelper.Resolution.r720p && Resolution.StartsWith("1280")) - return 2; - else if (resolution == ResolutionHelper.Resolution.r480p && Resolution.StartsWith("640")) - return 2; - } - - Model.VideoStreams[0].Filter.AddRange(new[] { $"scale={Resolution}:flags=lanczos" }); - - return 1; + return _ResolutionOptions; } } + public override int Outputs => 2; + public override int Execute(NodeParameters args) + { + base.Init(args); + + string ffmpeg = GetFFMpegExe(args); + if (string.IsNullOrEmpty(ffmpeg)) + return -1; + + var videoInfo = GetVideoInfo(args); + if (videoInfo == null || videoInfo.VideoStreams?.Any() != true) + return -1; + + if (Force == false) + { + var resolution = ResolutionHelper.GetResolution(videoInfo); + if (resolution == ResolutionHelper.Resolution.r1080p && Resolution.StartsWith("1920")) + return 2; + else if (resolution == ResolutionHelper.Resolution.r4k && Resolution.StartsWith("3840")) + return 2; + else if (resolution == ResolutionHelper.Resolution.r720p && Resolution.StartsWith("1280")) + return 2; + else if (resolution == ResolutionHelper.Resolution.r480p && Resolution.StartsWith("640")) + return 2; + } + + Model.VideoStreams[0].Filter.AddRange(new[] { $"scale={Resolution}:flags=lanczos" }); + + return 1; + } } diff --git a/VideoNodes/LogicalNodes/DetectBlackBars.cs b/VideoNodes/LogicalNodes/DetectBlackBars.cs index 6c532cba..acb48e30 100644 --- a/VideoNodes/LogicalNodes/DetectBlackBars.cs +++ b/VideoNodes/LogicalNodes/DetectBlackBars.cs @@ -79,7 +79,7 @@ namespace FileFlows.VideoNodes int y = int.MaxValue; int width = 0; int height = 0; - foreach (int ss in new int[] { 60, 100, 240, 360 }) // check at multiple times + foreach (int ss in new int[] { 60, 120, 240, 360 }) // check at multiple times { using (var process = new Process()) {