diff --git a/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs b/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs index ca7b327..5d33723 100644 --- a/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs +++ b/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs @@ -6,9 +6,9 @@ namespace MatroskaLib.Test; public class ByteHelperTest { public static TheoryData> TestData1 = new() { - { 2UL, new () { 0x2 } }, - { 909UL, new () { 0x3, 0x8D } }, - { 1_800_70UL, new () { 0x2, 0xBF, 0x66 } }, + { 2UL, [0x2] }, + { 909UL, [0x3, 0x8D] }, + { 1_800_70UL, [0x2, 0xBF, 0x66] }, }; [Theory, MemberData(nameof(TestData1))] public void ToBytesTest(ulong value, List lsBytesExpected) @@ -19,10 +19,10 @@ public void ToBytesTest(ulong value, List lsBytesExpected) } public static TheoryData, List> TestData2 = new() { - { new() {0x0, 0x0, 0x0, 0x96}, new () { 0x96 } }, - { new() {0x0, 0x0, 0x5, 0x0, 0x9}, new () { 0x5, 0x0, 0x9 } }, - { new() {0x9}, new () { 0x9 } }, - { new() {}, new () { } } + { [0x0, 0x0, 0x0, 0x96], [0x96] }, + { [0x0, 0x0, 0x5, 0x0, 0x9], [0x5, 0x0, 0x9] }, + { [0x9], [0x9] }, + { [], [] } }; [Theory, MemberData(nameof(TestData2))] public void RemoveLeftZeroesTest(List lsBytes, List lsBytesExpected) @@ -34,34 +34,34 @@ public void RemoveLeftZeroesTest(List lsBytes, List lsBytesExpected) public static IEnumerable Data() { - yield return new object[] - { + yield return + [ new List{ 0x6B, 0x2D, 0xAE, 0xBB, 0xD7, 0x81, 0x02 }, new List{ 0x6B, 0x2D, 0xAE, 0xBE, 0xD7, 0x81, 0x02 }, 4, 3 - }; - yield return new object[] - { + ]; + yield return + [ new List{ 0x81, 0x02, 0xAE, 0x42, 0x83, 0xD7, 0x81, 0x03 }, new List{ 0x81, 0x02, 0xAE, 0x42, 0x87, 0xD7, 0x81, 0x03 }, 5, 4 - }; - yield return new object[] - { + ]; + yield return + [ new List{ 0x81, 0x02, 0xAE, 0x42, 0x83, 0xD7, 0x81, 0x03 }, new List{ 0x81, 0x02, 0xAE, 0x42, 0x87, 0xD7, 0x81, 0x03 }, 5, 4 - }; - yield return new object[] - { + ]; + yield return + [ new List{ 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x3A, 0xD7, 81 }, new List{ 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x3D, 0xD7, 81 }, 11, 3 - }; + ]; } [Theory, MemberData(nameof(Data))] diff --git a/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs b/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs index 7e84dc7..d462c1b 100644 --- a/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs +++ b/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs @@ -11,6 +11,7 @@ public static class MkvValidator private const string OutputRemoveRegex = @"(^At least one output file must be specified)|(^\[(.*)\] )"; public static void Validate(string filePath) { + // Validate with ffmpeg Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardError = true; @@ -26,6 +27,7 @@ public static void Validate(string filePath) throw new Exception("ffmpeg's mkv validation produced errors:" + Environment.NewLine + output); } + // Validate with mkvalidator p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardError = true; diff --git a/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj b/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj index fa88b03..4e9c1fd 100644 --- a/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj +++ b/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj @@ -49,6 +49,10 @@ mkv files\TestFile5_MkvProEdit.mkv PreserveNewest + + mkv files\TestFile6_SmallSeekHead.mkv + PreserveNewest + PreserveNewest diff --git a/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs b/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs index dc48fd0..f39665c 100644 --- a/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs +++ b/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs @@ -13,6 +13,9 @@ namespace MatroskaLib.Test; * Only first void with checksum elements * MkvProEdit * Only second void and may need to change length of that void + * TestFile6_SmallSeekHead.mkv + * SeekHead size of 2, which caused an exception (see github issue #10). + * Do that this is not a valid mkv file accordant to MkValidator. */ public class MatroskaLibTest { @@ -22,7 +25,7 @@ public class MatroskaLibTest [InlineData("mkv files/TestFile1_MkvToolNix.mkv")] public void ReadTestFile1(string file) { - string[] filePaths = { file }; + string[] filePaths = [file]; List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); List lsTracks = lsMkvFiles[0].tracks; @@ -40,12 +43,12 @@ public void ReadTestFile1(string file) public void WriteTestFile1(string file) { File.Copy(file, TestFilePath, true); - List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); lsMkvFiles[0].tracks[0].flagDefault = false; lsMkvFiles[0].tracks[2].flagDefault = false; MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); - lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); List lsTracks = lsMkvFiles[0].tracks; lsTracks.Should().HaveCount(3); @@ -59,7 +62,7 @@ public void WriteTestFile1(string file) [InlineData("mkv files/TestFile2_MkvToolNix.mkv")] public void ReadTestFile2(string file) { - string[] filePaths = { file }; + string[] filePaths = [file]; List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); List lsTracks = lsMkvFiles[0].tracks; @@ -79,12 +82,12 @@ public void ReadTestFile2(string file) public void WriteTestFile2(string file) { File.Copy(file, TestFilePath, true); - List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); lsMkvFiles[0].tracks[1].flagDefault = true; lsMkvFiles[0].tracks[3].flagDefault = true; MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); - lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); List lsTracks = lsMkvFiles[0].tracks; lsTracks.Should().HaveCount(5); @@ -100,7 +103,7 @@ public void WriteTestFile2(string file) [InlineData("mkv files/TestFile3_HandBrake.mkv")] public void ReadTestFile3(string file) { - string[] filePaths = { file }; + string[] filePaths = [file]; List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); List lsTracks = lsMkvFiles[0].tracks; @@ -119,12 +122,12 @@ public void ReadTestFile3(string file) public void WriteTestFile3(string file) { File.Copy(file, TestFilePath, true); - List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); lsMkvFiles[0].tracks[1].flagDefault = true; lsMkvFiles[0].tracks[3].flagDefault = true; MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); - lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); List lsTracks = lsMkvFiles[0].tracks; lsTracks.Should().HaveCount(4); @@ -140,7 +143,7 @@ public void WriteTestFile3(string file) [InlineData("mkv files/TestFile5_MkvProEdit.mkv")] public void ReadTestFile4(string file) { - string[] filePaths = { file }; + string[] filePaths = [file]; List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); List lsTracks = lsMkvFiles[0].tracks; @@ -158,12 +161,12 @@ public void ReadTestFile4(string file) public void WriteTestFile4(string file) { File.Copy(file, TestFilePath, true); - List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); lsMkvFiles[0].tracks[0].flagDefault = false; lsMkvFiles[0].tracks[2].flagDefault = false; MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); - lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); List lsTracks = lsMkvFiles[0].tracks; lsTracks.Should().HaveCount(3); @@ -172,4 +175,14 @@ public void WriteTestFile4(string file) lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio }); MkvValidator.Validate(TestFilePath); } + + [Fact] + public void FileWithSeekHeadSizeOf2ShouldNotThrow() + { + File.Copy("mkv files/TestFile6_SmallSeekHead.mkv", TestFilePath, true); + List lsMkvFiles = MatroskaReader.ReadMkvFiles([TestFilePath]); + lsMkvFiles[0].tracks[0].flagDefault = false; + + MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); + } } diff --git a/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs b/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs index 2b40271..ab3c1af 100644 --- a/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs +++ b/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs @@ -57,7 +57,8 @@ public static void ChangeLength(List lsBytes, int position, List lsL // Convert new length to bytes and strip bytes List lsNewBytes = ToBytes(ret); - if (lsNewBytes.Count != lsLengthBytes.Count) throw new Exception("New length doesn't fit into existing length element"); + if (lsNewBytes.Count != lsLengthBytes.Count) + throw new InvalidOperationException($"New length bytes are not the same length as the old ones. Old length: {lsLengthBytes.Count}, new length: {lsNewBytes.Count}"); // Replace old length with new length bytes lsBytes.RemoveRange(position, lsNewBytes.Count); diff --git a/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs b/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs index b1d9906..691c312 100644 --- a/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs +++ b/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs @@ -16,7 +16,7 @@ public static void LocateElement(this EbmlReader reader, ulong descriptor) } } - throw new Exception($"Cannot find descriptor 0x{descriptor:X}"); + throw new InvalidOperationException($"Cannot find descriptor 0x{descriptor:X}"); } } } diff --git a/MatroskaLib/MatroskaLib/MatroskaWriter.cs b/MatroskaLib/MatroskaLib/MatroskaWriter.cs index 4b148d7..d967a7f 100644 --- a/MatroskaLib/MatroskaLib/MatroskaWriter.cs +++ b/MatroskaLib/MatroskaLib/MatroskaWriter.cs @@ -72,10 +72,14 @@ private static void _ChangeVoidLengthAndHeaders(List seekList, int? seekHe foreach (var s in seekList.Where(s => s.seekId is MatroskaElements.Tracks or MatroskaElements.SegmentInfo)) { - int desiredLength = Convert.ToInt32(lsBytes[s.seekPositionByteNumber - 1] - 0x80); List lsNewBytes = ByteHelper.ToBytes(s.seekPosition - (ulong)offset); - if (desiredLength != lsNewBytes.Count) - throw new Exception("New seekposition doesn't fit into existing element"); + if (lsNewBytes.Count > s.elementLength) + throw new InvalidOperationException($"New seekPosition bytes are bigger than the old one. Trying to fit {lsNewBytes.Count} bytes into {s.elementLength} bytes"); + if (lsNewBytes.Count < s.elementLength) + { + // The new seekPosition is smaller than the old one, add padding + lsNewBytes.AddRange(new byte[s.elementLength - lsNewBytes.Count]); + } lsBytes.RemoveRange(s.seekPositionByteNumber, lsNewBytes.Count); lsBytes.InsertRange(s.seekPositionByteNumber, lsNewBytes); diff --git a/MatroskaLib/MatroskaLib/MkvFilesContainer.cs b/MatroskaLib/MatroskaLib/MkvFilesContainer.cs index 9499db6..5cdbd2e 100644 --- a/MatroskaLib/MatroskaLib/MkvFilesContainer.cs +++ b/MatroskaLib/MatroskaLib/MkvFilesContainer.cs @@ -8,7 +8,7 @@ namespace MatroskaLib; public class MkvFilesContainer { public readonly List MkvFiles = new(); - public readonly List MkFilesRejected = new(); + public readonly List<(MkvFile file, string error)> MkFilesRejected = new(); public MkvFilesContainer(string[] filePaths) { @@ -16,10 +16,11 @@ public MkvFilesContainer(string[] filePaths) MkvFiles.Add(files[0]); for (int i = 1; i < files.Count; i++) { - if (files[0].CompareTo(files[i]) == 0) + string? error = files[0].CompareToGetError(files[i]); + if (error is null) MkvFiles.Add(files[i]); else - MkFilesRejected.Add(files[i]); + MkFilesRejected.Add((files[i], error)); } } diff --git a/MatroskaLib/MatroskaLib/Types/MkvFile.cs b/MatroskaLib/MatroskaLib/Types/MkvFile.cs index aa538ab..4e83feb 100644 --- a/MatroskaLib/MatroskaLib/Types/MkvFile.cs +++ b/MatroskaLib/MatroskaLib/Types/MkvFile.cs @@ -6,7 +6,7 @@ namespace MatroskaLib.Types; -public record MkvFile : IComparable +public record MkvFile { public required string filePath { get; init; } public required List tracks { get; init; } @@ -18,7 +18,7 @@ public record MkvFile : IComparable public required int tracksPosition { get; init; } public required int beginHeaderPosition { get; init; } - public int CompareTo(MkvFile? other) + public string? CompareToGetError(MkvFile? other) { if (other is null) throw new ArgumentNullException(nameof(other)); @@ -27,11 +27,16 @@ public int CompareTo(MkvFile? other) { var track = tracks[i]; var trackOther = other.tracks.ElementAtOrDefault(i); - if (trackOther is null || track.number != trackOther.number || track.language != trackOther.language) - return -1; + + if (trackOther is null) + return $"Track at index {i} does not exist, expected {track.type} with language {track.language}."; + if (track.number != trackOther.number) + return $"Track number at index {i} does not match. Expected {track.number}, got {trackOther.number}."; + if (track.language != trackOther.language) + return $"Track language at index {i} does not match. Expected {track.language}, got {trackOther.language}."; } - return 0; + return null; } public override string ToString() => diff --git a/MatroskaLib/MatroskaLib/Types/Seek.cs b/MatroskaLib/MatroskaLib/Types/Seek.cs index 19abe44..bf3ab42 100644 --- a/MatroskaLib/MatroskaLib/Types/Seek.cs +++ b/MatroskaLib/MatroskaLib/Types/Seek.cs @@ -9,6 +9,7 @@ public class Seek public ulong seekId { get; private set; } public ulong seekPosition { get; private set; } public int seekPositionByteNumber { get; private set; } + public int elementLength { get; private set; } public Seek(EbmlReader reader) => _reader = reader; @@ -23,6 +24,7 @@ public void ApplyElement(FileStream fileStream) { seekPositionByteNumber = (int)fileStream.Position; seekPosition = _reader.ReadUInt(); + elementLength = (int)_reader.ElementSize; } } } diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist index f603d14..d812c9e 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist @@ -7,7 +7,7 @@ CFBundleIdentifier com.mikemoolenaar.MkvDefaultTrackChanger CFBundleShortVersionString - 1.1.0 + 1.2.0 LSMinimumSystemVersion 10.12 CFBundleDevelopmentRegion diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto index 670a827..2ead33c 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto @@ -3,18 +3,24 @@ xmlns="http://schema.picoe.ca/eto.forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MkvDefaultTrackChanger" - MinimumSize="370,530" + MinimumSize="370,570" Size="370,530" Padding="10" > - - - + + + + + - + + + - - + + + + \ No newline at end of file diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs index 381c32f..600e904 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs @@ -58,12 +58,15 @@ private void LoadFiles() mkvContainer = new MkvFilesContainer(filePaths); if (mkvContainer.MkFilesRejected.Count > 0) { + var sourceFile = Path.GetFileName(filePaths[0]); + string rejectedFiles = Environment.NewLine + Environment.NewLine; mkvContainer.MkFilesRejected.ForEach((x) => { - rejectedFiles += Path.GetFileName(x.filePath) + Environment.NewLine + Environment.NewLine; + rejectedFiles += $"- {Path.GetFileName(x.file.filePath)}: {x.error} {Environment.NewLine}{Environment.NewLine}"; }); - MessageBox.Show("The following files were rejected: " + rejectedFiles, MessageBoxType.Warning); + MessageBox.Show($"The following files have different tracks or the order is different than {sourceFile}: {rejectedFiles}These files cannot be processed.", + MessageBoxType.Warning); } var lsSubtitleTracks = mkvContainer.GetSubtitleTracks(); diff --git a/MkvReadCrawler/MkvReadCrawler.csproj b/MkvReadCrawler/MkvReadCrawler.csproj index 78a6861..e9a5c6a 100644 --- a/MkvReadCrawler/MkvReadCrawler.csproj +++ b/MkvReadCrawler/MkvReadCrawler.csproj @@ -1,8 +1,8 @@ - Exe net8.0 + Exe diff --git a/MkvReadCrawler/Program.cs b/MkvReadCrawler/Program.cs index 30bf708..b8900ed 100644 --- a/MkvReadCrawler/Program.cs +++ b/MkvReadCrawler/Program.cs @@ -41,7 +41,7 @@ static void Main(string[] args) Console.WriteLine($"{x++}/{mkvFiles.Length} {Path.GetFileName(mkvFile)}"); }); - File.WriteAllText("OutputkvDefault.txt", mainStringBuilder.ToString()); - Console.WriteLine("Done! Output written to OutputkvDefault.txt."); + File.WriteAllText("output.txt", mainStringBuilder.ToString()); + Console.WriteLine("Done! Output written to output.txt"); } } diff --git a/TestMkvFiles/mkv files/TestFile6_SmallSeekHead.mkv b/TestMkvFiles/mkv files/TestFile6_SmallSeekHead.mkv new file mode 100644 index 0000000..3912d5d Binary files /dev/null and b/TestMkvFiles/mkv files/TestFile6_SmallSeekHead.mkv differ