diff --git a/.gitignore b/.gitignore index e1d665ef..24c53ce4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ Gotify/settings.json Plex/settings.json Emby/settings.invalid.json Emby/settings.json +VideoNodes/test.settings.dev.json diff --git a/VideoNodes/Tests/SubtitleExtractorTests.cs b/VideoNodes/Tests/SubtitleExtractorTests.cs new file mode 100644 index 00000000..efdd1e28 --- /dev/null +++ b/VideoNodes/Tests/SubtitleExtractorTests.cs @@ -0,0 +1,69 @@ +#if(DEBUG) + +namespace VideoNodes.Tests +{ + using FileFlows.VideoNodes; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + [TestClass] + public class SubtitleExtractorTests: TestBase + { + [TestMethod] + public void SubtitleExtractor_Extension_Test() + { + string file = TestFile_BasicMkv; + var vi = new VideoInfoHelper(FfmpegPath, new TestLogger()); + var vii = vi.Read(file); + + foreach (string ext in new[] { String.Empty, ".srt", ".sup" }) + { + SubtitleExtractor node = new(); + node.OutputFile = Path.Combine(TempPath, "subtitle.en" + ext); + node.Language = "eng"; + + var args = new NodeParameters(file, new TestLogger(), false, string.Empty); + args.GetToolPathActual = (string tool) => FfmpegPath; + args.TempPath = TempPath; + + Assert.AreEqual(1, new VideoFile().Execute(args)); + + int output = node.Execute(args); + + Assert.AreEqual(1, output); + } + } + + [TestMethod] + public void SubtitleExtractor_Pgs_Test() + { + string file = TestFile_Pgs; + var vi = new VideoInfoHelper(FfmpegPath, new TestLogger()); + var vii = vi.Read(file); + + foreach (string ext in new[] { String.Empty, ".srt", ".sup" }) + { + SubtitleExtractor node = new(); + node.OutputFile = Path.Combine(TempPath, "subtitle.en" + ext); + node.Language = "eng"; + + var args = new NodeParameters(file, new TestLogger(), false, string.Empty); + args.GetToolPathActual = (string tool) => FfmpegPath; + args.TempPath = TempPath; + + Assert.AreEqual(1, new VideoFile().Execute(args)); + + int output = node.Execute(args); + + Assert.AreEqual(1, output); + } + } + } +} + + +#endif \ No newline at end of file diff --git a/VideoNodes/Tests/_TestBase.cs b/VideoNodes/Tests/_TestBase.cs new file mode 100644 index 00000000..ad9baafd --- /dev/null +++ b/VideoNodes/Tests/_TestBase.cs @@ -0,0 +1,63 @@ +#if(DEBUG) + +using FileFlows.VideoNodes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace VideoNodes.Tests; + +[TestClass] +public abstract class TestBase +{ + public string TestPath { get; private set; } + public string TempPath { get; private set; } + public string FfmpegPath { get; private set; } + + [TestInitialize] + public void TestInitialize() + { + if (File.Exists("../../../test.settings.dev.json")) + { + LoadSettings("../../../test.settings.dev.json"); + } + else if (File.Exists("../../../test.settings.json")) + { + LoadSettings("../../../test.settings.json"); + } + this.TestPath = this.TestPath?.EmptyAsNull() ?? @"C:\videos\testfiles"; + this.TempPath = this.TestPath?.EmptyAsNull() ?? @"C:\videos\temp"; + this.FfmpegPath = this.FfmpegPath?.EmptyAsNull() ?? @"C:\utils\ffmpeg\ffmpeg.exe"; + if (Directory.Exists(this.TempPath) == false) + Directory.CreateDirectory(this.TempPath); + } + + private void LoadSettings(string filename) + { + try + { + string json = File.ReadAllText(filename); + var settings = JsonSerializer.Deserialize(json); + this.TestPath = settings.TestPath; + this.TempPath = settings.TempPath; + this.FfmpegPath = settings.FfmpegPath; + } + catch (Exception) { } + } + + protected virtual void TestStarting() + { + + } + + protected string TestFile_BasicMkv => Path.Combine(TestPath, "basic.mkv"); + protected string TestFile_Pgs => Path.Combine(TestPath, "pgs.mkv"); + + private class TestSettings + { + public string TestPath { get; set; } + public string TempPath { get; set; } + public string FfmpegPath { get; set; } + } +} + +#endif \ No newline at end of file diff --git a/VideoNodes/VideoNodes/SubtitleExtractor.cs b/VideoNodes/VideoNodes/SubtitleExtractor.cs index aeb6048f..43d594d1 100644 --- a/VideoNodes/VideoNodes/SubtitleExtractor.cs +++ b/VideoNodes/VideoNodes/SubtitleExtractor.cs @@ -43,8 +43,6 @@ 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) @@ -61,44 +59,23 @@ { var file = new FileInfo(args.FileName); - string extension = "srt"; - if(subTrack.Codec?.ToLower()?.Contains("pgs") == true) - extension = "sup"; - OutputFile = file.FullName.Substring(0, file.FullName.LastIndexOf(file.Extension)) + "." + extension; + OutputFile = file.FullName.Substring(0, file.FullName.LastIndexOf(file.Extension)); } OutputFile = args.MapPath(OutputFile); - bool textSubtitles = System.Text.RegularExpressions.Regex.IsMatch(OutputFile, @"\.(sup)$") == false; + + string extension = "srt"; + if (subTrack.Codec?.ToLower()?.Contains("pgs") == true) + extension = "sup"; + if (OutputFile.ToLower().EndsWith(".srt") || OutputFile.ToLower().EndsWith(".sup")) + OutputFile = OutputFile[0..^4]; + + OutputFile += "." + extension; + //bool textSubtitles = Regex.IsMatch(OutputFile, @"\.(sup)$") == false; - 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, - ArgumentList = textSubtitles ? new[] { - - "-i", args.WorkingFile, - "-map", $"{subTrack.IndexString}", - "-map", "-0:v", - "-map", "-0:a", - OutputFile - } - : new [] - { - "-i", args.WorkingFile, - "-c", "copy", - "-map", $"{subTrack.IndexString}", - OutputFile - } - }).Result; - - if (result.ExitCode == 0) + var extracted = ExtractSubtitle(args, ffmpegExe, "0:s:" + subTrack.TypeIndex, OutputFile); + if(extracted) { args.UpdateVariables(new Dictionary { @@ -110,7 +87,51 @@ return 1; } - var of = new FileInfo(OutputFile); + return -1; + } + catch (Exception ex) + { + args.Logger?.ELog("Failed processing VideoFile: " + ex.Message); + return -1; + } + } + + internal bool ExtractSubtitle(NodeParameters args, string ffmpegExe, string subtitleStream, string output) + { + if (File.Exists(OutputFile)) + { + args.Logger?.ILog("File already exists, deleting file: " + OutputFile); + File.Delete(OutputFile); + } + + bool textSubtitles = Regex.IsMatch(OutputFile.ToLower(), @"\.(sup)$") == false; + // -y means it will overwrite a file if output already exists + var result = args.Process.ExecuteShellCommand(new ExecuteArgs + { + Command = ffmpegExe, + ArgumentList = textSubtitles ? + new[] { + + "-i", args.WorkingFile, + "-map", subtitleStream, + output + } : + new[] { + + "-i", args.WorkingFile, + "-map", subtitleStream, + "-c:s", "copy", + output + } + }).Result; + + var of = new FileInfo(OutputFile); + if (result.ExitCode != 0) + { + 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); if (of.Exists && of.Length == 0) { // delete the output file if it created an empty file @@ -120,17 +141,9 @@ } catch (Exception) { } } - 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; + return false; } + return of.Exists; } } } diff --git a/VideoNodes/test.settings.json b/VideoNodes/test.settings.json new file mode 100644 index 00000000..504c54f0 --- /dev/null +++ b/VideoNodes/test.settings.json @@ -0,0 +1,5 @@ +{ + "TestPath": "C:\\videos\\testfiles", + "TempPath": "C:\\videos\\temp", + "FfmpegPath": "C:\\Utils\\ffmpeg\\ffmpeg.exe" +} \ No newline at end of file