mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-02-18 12:08:36 -06:00
FF-371 - FFmpeg builder now automatically converting subtitles if container changes
This commit is contained in:
@@ -71,8 +71,9 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
|
||||
((FfmpegSubtitleStream)item.stream).Stream;
|
||||
|
||||
|
||||
var streamArgs = item.stream.GetParameters(new FfmpegStream.GetParametersArgs()
|
||||
var streamArgs = item.stream.GetParameters(new ()
|
||||
{
|
||||
Logger = args.Logger,
|
||||
OutputOverallIndex = overallIndex,
|
||||
OutputTypeIndex = actualIndex,
|
||||
SourceExtension = sourceExtension,
|
||||
|
||||
@@ -60,6 +60,11 @@ public abstract class FfmpegStream
|
||||
/// Gets or sets if the default flag should be set
|
||||
/// </summary>
|
||||
public bool UpdateDefaultFlag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger
|
||||
/// </summary>
|
||||
public ILogger Logger { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +1,67 @@
|
||||
namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models
|
||||
using FileFlows.VideoNodes.Helpers;
|
||||
|
||||
namespace FileFlows.VideoNodes.FfmpegBuilderNodes.Models;
|
||||
|
||||
public class FfmpegSubtitleStream : FfmpegStream
|
||||
{
|
||||
public class FfmpegSubtitleStream : FfmpegStream
|
||||
/// <summary>
|
||||
/// Gets or sets the source subtitle stream
|
||||
/// </summary>
|
||||
public SubtitleStream Stream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if this stream has changed
|
||||
/// </summary>
|
||||
public override bool HasChange => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters for this stream
|
||||
/// </summary>
|
||||
/// <param name="args">the arguments</param>
|
||||
/// <returns>the parameters to pass to FFmpeg for this stream</returns>
|
||||
public override string[] GetParameters(GetParametersArgs args)
|
||||
{
|
||||
public SubtitleStream Stream { get; set; }
|
||||
if (Deleted)
|
||||
return new string[] { };
|
||||
|
||||
public override bool HasChange => false;
|
||||
|
||||
public override string[] GetParameters(GetParametersArgs args)
|
||||
bool containerSame =
|
||||
string.Equals(args.SourceExtension, args.DestinationExtension, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
string destCodec;
|
||||
if(containerSame)
|
||||
destCodec = "copy";
|
||||
else
|
||||
{
|
||||
if (Deleted)
|
||||
destCodec = SubtitleHelper.GetSubtitleCodec(args.DestinationExtension, Stream.Codec);
|
||||
if (string.IsNullOrEmpty(destCodec))
|
||||
{
|
||||
// this subtitle is not supported by the new container, remove it.
|
||||
args.Logger?.WLog($"Subtitle stream is not supported in destination container, removing: {Stream.Codec} {Stream.Title ?? string.Empty}");
|
||||
return new string[] { };
|
||||
|
||||
List<string> results= new List<string> { "-map", Stream.InputFileIndex + ":s:{sourceTypeIndex}", "-c:s:{index}" };
|
||||
|
||||
switch (args.DestinationExtension)
|
||||
{
|
||||
case "mkv":
|
||||
{
|
||||
if(Stream.Codec == "mov_text")
|
||||
results.Add("srt");
|
||||
else
|
||||
results.Add("copy");
|
||||
}
|
||||
break;
|
||||
case "mp4":
|
||||
{
|
||||
if (Helpers.SubtitleHelper.IsImageSubtitle(Stream.Codec))
|
||||
{
|
||||
results.Add("copy");
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add("mov_text");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
results.Add("copy");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(this.Title) == false)
|
||||
{
|
||||
// first s: means stream speicific, this is suppose to have :s:s
|
||||
// https://stackoverflow.com/a/21059838
|
||||
results.Add($"-metadata:s:s:{args.OutputTypeIndex}");
|
||||
results.Add($"title={(this.Title == FfmpegStream.REMOVED ? "" : this.Title)}");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(this.Language) == false)
|
||||
{
|
||||
results.Add($"-metadata:s:s:{args.OutputTypeIndex}");
|
||||
results.Add($"language={(this.Language == FfmpegStream.REMOVED ? "" : this.Language)}");
|
||||
}
|
||||
|
||||
if (Metadata.Any())
|
||||
{
|
||||
results.AddRange(Metadata.Select(x => x.Replace("{index}", args.OutputTypeIndex.ToString())));
|
||||
}
|
||||
|
||||
if (args.UpdateDefaultFlag)
|
||||
{
|
||||
results.AddRange(new[] { "-disposition:a:" + args.OutputTypeIndex, this.IsDefault ? "default" : "0" });
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
List<string> results= new List<string> { "-map", Stream.InputFileIndex + ":s:{sourceTypeIndex}", "-c:s:{index}", destCodec };
|
||||
|
||||
if (string.IsNullOrWhiteSpace(this.Title) == false)
|
||||
{
|
||||
// first s: means stream specific, this is suppose to have :s:s
|
||||
// https://stackoverflow.com/a/21059838
|
||||
results.Add($"-metadata:s:s:{args.OutputTypeIndex}");
|
||||
results.Add($"title={(this.Title == FfmpegStream.REMOVED ? "" : this.Title)}");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(this.Language) == false)
|
||||
{
|
||||
results.Add($"-metadata:s:s:{args.OutputTypeIndex}");
|
||||
results.Add($"language={(this.Language == FfmpegStream.REMOVED ? "" : this.Language)}");
|
||||
}
|
||||
|
||||
if (Metadata.Any())
|
||||
results.AddRange(Metadata.Select(x => x.Replace("{index}", args.OutputTypeIndex.ToString())));
|
||||
|
||||
if (args.UpdateDefaultFlag)
|
||||
results.AddRange(new[] { "-disposition:a:" + args.OutputTypeIndex, this.IsDefault ? "default" : "0" });
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
|
||||
//
|
||||
// public class FfmpegBuilderRemoveUnsupportedSubtitles : FfmpegBuilderNode
|
||||
// {
|
||||
// public override string Icon => "fas fa-comment";
|
||||
//
|
||||
// public override int Outputs => 2;
|
||||
//
|
||||
// public readonly string[] Mp4Subtitles = new[]
|
||||
// {
|
||||
// "ass", // Advanced SubStation Alpha
|
||||
// "aqtitle", // AQTitle
|
||||
// "cap", // Cheetah CAP
|
||||
// "dcin", // D-Cinema subtitles
|
||||
// "dvb_subtitle", // DVB Teletext
|
||||
// "dvd_subtitle", // DVD subtitle
|
||||
// "es", // Enhanced Subtitle
|
||||
// "fabsubtitler", // FAB Subtitler
|
||||
// "fcpxml", // Final Cut Pro X
|
||||
// "jacosub", // JACOsub subtitle
|
||||
// "microdvd", // MicroDVD subtitle
|
||||
// "mpl2", // MPL2 subtitle
|
||||
// "mpsub", // MPlayer subtitle
|
||||
// "bin", // Opaque binary subtitle (internal)
|
||||
// "pjs", // PJS (Phoenix Japanimation Society) subtitle
|
||||
// "realtext", // RealText subtitle format
|
||||
// "sami", // SAMI subtitle format
|
||||
// "srt", // SubRip subtitle
|
||||
// "ssa", // SubStation Alpha subtitle
|
||||
// "subviewer", // SubViewer 1.0 subtitle
|
||||
// "subviewer1", // SubViewer 2.0 subtitle
|
||||
// "teletext", // Teletext subtitle
|
||||
// "ttml", // Timed Text Markup Language
|
||||
// "ttxt", // TurboTitler subtitle
|
||||
// "webvtt", // WebVTT subtitle
|
||||
// "zerog" // ZeroG subtitle
|
||||
// };
|
||||
//
|
||||
// public readonly string[] MkvSubtitles = new[]
|
||||
// {
|
||||
// "ass", // Advanced SubStation Alpha
|
||||
// "ssa", // SubStation Alpha subtitle
|
||||
// "srt", // SubRip subtitle
|
||||
// "subrip", // SubRip subtitle (alternative name)
|
||||
// "vtt", // WebVTT subtitle
|
||||
// "webvtt", // WebVTT subtitle (alternative name)
|
||||
// "smi", // SAMI subtitle format
|
||||
// "sami", // SAMI subtitle format (alternative name)
|
||||
// "rt", // RealText subtitle format
|
||||
// "realtext", // RealText subtitle format (alternative name)
|
||||
// "stl", // EBU STL (Subtitling Data Exchange Format)
|
||||
// "ttml", // Timed Text Markup Language
|
||||
// "ttml_legacy" // Timed Text Markup Language (legacy name)
|
||||
// };
|
||||
//
|
||||
// public readonly string[] WebMSubtitles = new[]
|
||||
// {
|
||||
// "ass", // Advanced SubStation Alpha
|
||||
// "ssa", // SubStation Alpha subtitle
|
||||
// "srt", // SubRip subtitle
|
||||
// "subrip", // SubRip subtitle (alternative name)
|
||||
// "vtt", // WebVTT subtitle
|
||||
// "webvtt", // WebVTT subtitle (alternative name)
|
||||
// "ttml", // Timed Text Markup Language
|
||||
// "ttml_legacy" // Timed Text Markup Language (legacy name)
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// public override int Execute(NodeParameters args)
|
||||
// {
|
||||
// this.Init(args);
|
||||
// bool removing = false;
|
||||
// string[] unsupported = new[] { "" };
|
||||
// foreach (var stream in Model.SubtitleStreams)
|
||||
// {
|
||||
// if (unsupported.Contains(stream.Stream.Codec?.ToLower()))
|
||||
// {
|
||||
// stream.Deleted = true;
|
||||
// removing = true;
|
||||
// }
|
||||
// }
|
||||
// return removing ? 1 : 2;
|
||||
// }
|
||||
// }
|
||||
@@ -1,25 +0,0 @@
|
||||
//namespace FileFlows.VideoNodes.FfmpegBuilderNodes
|
||||
//{
|
||||
// public class FfmpegBuilderUnsupportedMP4Subtitles : FfmpegBuilderNode
|
||||
// {
|
||||
// public override string Icon => "fas fa-comment";
|
||||
|
||||
// public override int Outputs => 2;
|
||||
|
||||
// public override int Execute(NodeParameters args)
|
||||
// {
|
||||
// this.Init(args);
|
||||
// bool removing = false;
|
||||
// string[] unsupported = new[] { "" };
|
||||
// foreach (var stream in Model.SubtitleStreams)
|
||||
// {
|
||||
// if (unsupported.Contains(stream.Stream.Codec?.ToLower()))
|
||||
// {
|
||||
// stream.Deleted = true;
|
||||
// removing = true;
|
||||
// }
|
||||
// }
|
||||
// return removing ? 1 : 2;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,21 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileFlows.VideoNodes.Helpers;
|
||||
namespace FileFlows.VideoNodes.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helper for Subtitles
|
||||
/// </summary>
|
||||
internal class SubtitleHelper
|
||||
{
|
||||
|
||||
public static readonly string[] MkvSubtitles = new[]
|
||||
{
|
||||
"ass", // Advanced SubStation Alpha
|
||||
"ssa", // SubStation Alpha subtitle
|
||||
"srt", // SubRip subtitle
|
||||
"subrip", // SubRip subtitle (alternative name)
|
||||
"vtt", // WebVTT subtitle
|
||||
"webvtt", // WebVTT subtitle (alternative name)
|
||||
"smi", // SAMI subtitle format
|
||||
"sami", // SAMI subtitle format (alternative name)
|
||||
"rt", // RealText subtitle format
|
||||
"realtext", // RealText subtitle format (alternative name)
|
||||
"stl", // EBU STL (Subtitling Data Exchange Format)
|
||||
"ttml", // Timed Text Markup Language
|
||||
"ttml_legacy" // Timed Text Markup Language (legacy name)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Tests if a subtitle is an image based subtitle
|
||||
/// </summary>
|
||||
/// <param name="codec">the subtitle codec</param>
|
||||
/// <returns>true if the subtitle is an image based subtitle</returns>
|
||||
internal static bool IsImageSubtitle(string codec)
|
||||
=> Regex.IsMatch(codec.Replace("_", ""), "dvbsub|dvdsub|pgs|xsub", RegexOptions.IgnoreCase);
|
||||
=> Regex.IsMatch(codec.Replace("_", ""), "dvbsub|pgs|xsub|vobsub", RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Determines the appropriate subtitle codec for conversion based on the container type and current subtitle codec.
|
||||
/// </summary>
|
||||
/// <param name="containerType">The container type (mp4, mkv, webm).</param>
|
||||
/// <param name="currentCodec">The current subtitle codec.</param>
|
||||
/// <returns>The appropriate subtitle codec for conversion, or null container does not support this codec.</returns>
|
||||
public static string? GetSubtitleCodec(string containerType, string currentCodec)
|
||||
{
|
||||
// Check if the current subtitle codec is image-based
|
||||
bool isImageBased = IsImageSubtitle(currentCodec);
|
||||
|
||||
// Determine the appropriate subtitle codec based on the container type and if the current codec is image-based or text-based
|
||||
switch (containerType.ToLower())
|
||||
{
|
||||
case "mp4":
|
||||
if (isImageBased)
|
||||
{
|
||||
// MP4 container does not support image-based subtitles, so conversion is not possible
|
||||
return null;
|
||||
}
|
||||
return "mov_text";
|
||||
case "mkv":
|
||||
if (isImageBased)
|
||||
return "hdmv_pgs_subtitle";
|
||||
if (IsSupportedSubtitleCodecMKV(currentCodec) == false)
|
||||
return "srt"; // or "ssa" or any other supported codec
|
||||
return currentCodec;
|
||||
case "webm":
|
||||
if (isImageBased)
|
||||
{
|
||||
// WebM container does not support image-based subtitles, so conversion is not possible
|
||||
return null;
|
||||
}
|
||||
// WebM container supports text-based subtitles in the webvtt codec
|
||||
return "webvtt";
|
||||
default:
|
||||
// Invalid or unsupported container type
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the subtitle codec is supported in MKV container.
|
||||
/// </summary>
|
||||
/// <param name="codec">The subtitle codec to check.</param>
|
||||
/// <returns>True if the codec is supported in MKV, False otherwise.</returns>
|
||||
private static bool IsSupportedSubtitleCodecMKV(string codec)
|
||||
=> Array.IndexOf(MkvSubtitles, codec.ToLower()) >= 0;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user