Added Help links to remaining FFMPEG Builder nodes

This commit is contained in:
John Andrews
2022-04-24 12:14:26 +12:00
parent 5fdf1cd16d
commit f1c3145260
11 changed files with 484 additions and 475 deletions

View File

@@ -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<ListOption> _CodecOptions;
public static List<ListOption> 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<ListOption> _CodecOptions;
public static List<ListOption> CodecOptions
get
{
get
if (_CodecOptions == null)
{
if (_CodecOptions == null)
_CodecOptions = new List<ListOption>
{
_CodecOptions = new List<ListOption>
{
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<ListOption> _ChannelsOptions;
public static List<ListOption> ChannelsOptions
private static List<ListOption> _ChannelsOptions;
public static List<ListOption> ChannelsOptions
{
get
{
get
if (_ChannelsOptions == null)
{
if (_ChannelsOptions == null)
_ChannelsOptions = new List<ListOption>
{
_ChannelsOptions = new List<ListOption>
{
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<ListOption> _BitrateOptions;
public static List<ListOption> BitrateOptions
private static List<ListOption> _BitrateOptions;
public static List<ListOption> BitrateOptions
{
get
{
get
if (_BitrateOptions == null)
{
if (_BitrateOptions == null)
_BitrateOptions = new List<ListOption>
{
_BitrateOptions = new List<ListOption>
{
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"
};
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<string> Languages { get; set; }
[StringArray(2)]
public List<string> OrderedTracks { get; set; }
[StringArray(3)]
public List<string> 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<string> Languages { get; set; }
var reordered = Reorder(Model.AudioStreams);
[StringArray(2)]
public List<string> OrderedTracks { get; set; }
bool same = AreSame(Model.AudioStreams, reordered);
[StringArray(3)]
public List<string> 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<FfmpegAudioStream> Reorder(List<FfmpegAudioStream> input)
{
Languages ??= new List<string>();
OrderedTracks ??= new List<string>();
Channels ??= new List<string>();
List<float> 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<FfmpegAudioStream> Reorder(List<FfmpegAudioStream> input)
{
Languages ??= new List<string>();
OrderedTracks ??= new List<string>();
Channels ??= new List<string>();
List<float> 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<FfmpegAudioStream> original, List<FfmpegAudioStream> 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<FfmpegAudioStream> original, List<FfmpegAudioStream> reordered)
{
for (int i = 0; i < reordered.Count; i++)
{
if (reordered[i] != original[i])
{
return false;
}
return true;
}
return true;
}
}

View File

@@ -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;
}
}

View File

@@ -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<string> SubtitlesToRemove { get; set; }
private static List<ListOption> _Options;
public static List<ListOption> 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<string> SubtitlesToRemove { get; set; }
private static List<ListOption> _Options;
public static List<ListOption> Options
get
{
get
if (_Options == null)
{
if (_Options == null)
_Options = new List<ListOption>
{
_Options = new List<ListOption>
{
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<string>();
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<string>();
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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<ListOption> _ResolutionOptions;
public static List<ListOption> ResolutionOptions
{
[Boolean(2)]
public bool Force { get; set; }
[Select(nameof(ResolutionOptions), 1)]
public string Resolution { get; set; }
private static List<ListOption> _ResolutionOptions;
public static List<ListOption> ResolutionOptions
get
{
get
if (_ResolutionOptions == null)
{
if (_ResolutionOptions == null)
_ResolutionOptions = new List<ListOption>
{
_ResolutionOptions = new List<ListOption>
{
// 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;
}
}

View File

@@ -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())
{