mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-01-05 17:30:36 -06:00
Added Help links to remaining FFMPEG Builder nodes
This commit is contained in:
@@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user