diff --git a/VideoNodes/VideoInfoHelper.cs b/VideoNodes/VideoInfoHelper.cs index 50526110..8f26a3f1 100644 --- a/VideoNodes/VideoInfoHelper.cs +++ b/VideoNodes/VideoInfoHelper.cs @@ -63,6 +63,8 @@ namespace FileFlows.VideoNodes var rgxStreams = new Regex(@"Stream\s#[\d]+:[\d]+(.*?)(?=(Stream\s#[\d]|$))", RegexOptions.Singleline); var streamMatches = rgxStreams.Matches(output); int streamIndex = 0; + + int subtitleIndex = 1; foreach (Match sm in streamMatches) { if (sm.Value.Contains(" Video: ")) @@ -95,11 +97,13 @@ namespace FileFlows.VideoNodes if (sub != null) { sub.Index = streamIndex; + sub.TypeIndex = subtitleIndex; var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+"); if (match.Success) sub.IndexString = match.Value; vi.SubtitleStreams.Add(sub); } + ++subtitleIndex; } ++streamIndex; } diff --git a/VideoNodes/VideoNodes.csproj b/VideoNodes/VideoNodes.csproj index 9cdae0ed..e2b66887 100644 Binary files a/VideoNodes/VideoNodes.csproj and b/VideoNodes/VideoNodes.csproj differ diff --git a/VideoNodes/VideoNodes.en.json b/VideoNodes/VideoNodes.en.json index 01000e86..3e3cbab4 100644 --- a/VideoNodes/VideoNodes.en.json +++ b/VideoNodes/VideoNodes.en.json @@ -38,11 +38,20 @@ } }, "SubtitleRemover": { - "Description": "Removes subtitles from a video file if found..\n\nOutput 1: Subtitles were removed\nOutput 2: No subtitles found that needed to be removed", + "Description": "Removes subtitles from a video file if found.\n\nOutput 1: Subtitles were removed\nOutput 2: No subtitles found that needed to be removed", "Fields": { "SubtitlesToRemove": "Subtitles To Remove" } }, + "SubtitleExtractor": { + "Description": "Extract a single subtitle tracks and saves it to the destination. Can extract srt, ass, and ssa format.\nThis will NOT update the working file, and will keep the working file the same as the input file.\n\nOutput 1: Subtitles were extracted\nOutput 2: No subtitles found to extract", + "Fields": { + "Language": "Language", + "Language-Help": "The ISO 639-2 language code to use. \nhttps://en.wikipedia.org/wiki/List_of_ISO_639-2_codes", + "OutputFile": "Output File", + "OutputFile-Help": "Where to save the the output file to, e.g. \"'{folder.Orig.FullName}\\{file.Orig.FileName}.srt'\" to save it with the original file as a srt output.\nIf left blank an srt subtitle will be created in the same folder as the original input file." + } + }, "VideoCodec": { "Description": "This node will check the codecs in the input file, and trigger when matched.\n\nOutput 1: Matches\nOutput 2: Does not match", "Fields": { diff --git a/VideoNodes/VideoNodes/SubtitleExtractor.cs b/VideoNodes/VideoNodes/SubtitleExtractor.cs new file mode 100644 index 00000000..c014df15 --- /dev/null +++ b/VideoNodes/VideoNodes/SubtitleExtractor.cs @@ -0,0 +1,87 @@ +namespace FileFlows.VideoNodes +{ + using FileFlows.Plugin; + using FileFlows.Plugin.Attributes; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + public class SubtitleExtractor : EncodingNode + { + public override int Outputs => 2; + + public override string Icon => "fas fa-comment-dots"; + + [Text(1)] + public string Language { get; set; } + + [File(2)] + public string OutputFile { get; set; } + + public override int Execute(NodeParameters args) + { + try + { + VideoInfo videoInfo = GetVideoInfo(args); + if (videoInfo == null) + return -1; + + string ffmpegExe = GetFFMpegExe(args); + if (string.IsNullOrEmpty(ffmpegExe)) + return -1; + + List ffArgs = new List(); + + // ffmpeg -i input.mkv -map "0:m:language:eng" -map "-0:v" -map "-0:a" output.srt + var subTrack = videoInfo.SubtitleStreams?.Where(x => string.IsNullOrEmpty(Language) || x.Language?.ToLower() == Language.ToLower()).FirstOrDefault(); + if (subTrack == null) + { + args.Logger?.ILog("No subtitles found to extract"); + return 2; + } + + if (string.IsNullOrEmpty(OutputFile) == false) + { + OutputFile = args.ReplaceVariables(OutputFile, true); + } + else + { + var file = new FileInfo(args.FileName); + OutputFile = file.FullName.Substring(0, file.FullName.LastIndexOf(file.Extension)) + ".srt"; + } + OutputFile = args.MapPath(OutputFile); + + + if (File.Exists(OutputFile)) + { + args.Logger?.ILog("File already exists, deleting file: " + OutputFile); + File.Delete(OutputFile); + } + + // -y means it will overwrite a file if output already exists + var result = args.Process.ExecuteShellCommand(new ExecuteArgs + { + Command = ffmpegExe, + Arguments = $"-i \"{args.WorkingFile}\" -map \"0:s:{subTrack.TypeIndex}\" -map \"-0:v\" -map \"-0:a\" \"{OutputFile}\"" + }).Result; + + if (result.ExitCode == 0) + { + return 1; + } + + args.Logger?.ELog("FFMPEG process failed to extract subtitles"); + args.Logger?.ILog("Unexpected exit code: " + result.ExitCode); + args.Logger?.ILog(result.StandardOutput ?? String.Empty); + args.Logger?.ILog(result.StandardError ?? String.Empty); + return -1; + } + catch (Exception ex) + { + args.Logger?.ELog("Failed processing VideoFile: " + ex.Message); + return -1; + } + } + } +}