diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..67c44cb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,125 @@ +############################### +# Core EditorConfig Options # +############################### +root = true +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8 +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true \ No newline at end of file diff --git a/.github/workflows/dotnet.yml b/.github/workflows/build-and-test.yml similarity index 60% rename from .github/workflows/dotnet.yml rename to .github/workflows/build-and-test.yml index 5764e7b..d6c7afa 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/build-and-test.yml @@ -2,32 +2,25 @@ name: Build & test on: push: - branches: [ main ] + branches: [ main, develop ] paths: - 'MatroskaLib/**' - 'MkvDefaultTrackChanger/**' - 'TestMkvFiles/**' + - '.github/workflows/build-and-test.yml' pull_request: - branches: [ main ] + branches: [ main, develop ] jobs: build-and-test: - runs-on: windows-latest - steps: - uses: actions/checkout@v2 - uses: FedericoCarboni/setup-ffmpeg@v1 -# - uses: actions/cache@v2 -# with: -# path: ~/.nuget/packages -# key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} -# restore-keys: | -# ${{ runner.os }}-nuget- - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: 5.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0c76bfe..5885c71 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,13 +4,13 @@ on: workflow_dispatch: inputs: version: - description: 'Version' + description: 'Version (example: "1.1.0")' required: true default: '' env: - PUBLISH_WIN_PATH: ./MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/bin/Release/net5.0-windows/win-x64/publish - PUBLISH_LINUX_PATH: ./MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/bin/Release/net5.0/linux-x64/publish - PUBLISH_MAC_PATH: ./MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/bin/Release/net5.0/osx-x64/publish + PUBLISH_WIN_PATH: ./MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/bin/Release/net8.0-windows/win-x64/publish + PUBLISH_LINUX_PATH: ./MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/bin/Release/net8.0/linux-x64/publish + PUBLISH_MAC_PATH: ./MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/bin/Release/net8.0/osx-x64/publish jobs: build: runs-on: windows-latest @@ -20,24 +20,23 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore - name: Build Windows - run: dotnet publish MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/MkvDefaultTrackChanger.WinForms.csproj --configuration Release -r win-x64 -p:PublishSingleFile=true --self-contained true -p:PublishTrimmed=True -p:IncludeNativeLibrariesForSelfExtract=true -p:Version=${{ github.event.inputs.version }} + run: dotnet publish MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/MkvDefaultTrackChanger.WinForms.csproj -c Release -r win-x64 -p:PublishSingleFile=true -p:Version=${{ github.event.inputs.version }} - name: Build Linux - run: dotnet publish MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/MkvDefaultTrackChanger.Gtk.csproj --configuration Release -r linux-x64 -p:PublishSingleFile=true --self-contained true -p:PublishTrimmed=True -p:TrimMode=CopyUsed -p:Version=${{ github.event.inputs.version }} + run: dotnet publish MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/MkvDefaultTrackChanger.Gtk.csproj -c Release -r linux-x64 -p:PublishSingleFile=true -p:Version=${{ github.event.inputs.version }} - name: Build Mac - run: dotnet publish MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/MkvDefaultTrackChanger.Mac.csproj --configuration Release -r osx-x64 -p:PublishSingleFile=true --self-contained true -p:Version=${{ github.event.inputs.version }} + run: dotnet publish MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/MkvDefaultTrackChanger.Mac.csproj -c Release -r osx-x64 -p:PublishSingleFile=true -p:Version=${{ github.event.inputs.version }} - name: Zip releases run: | powershell Rename-Item $env:PUBLISH_WIN_PATH/MkvDefaultTrackChanger.WinForms.exe MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.exe powershell Compress-Archive $env:PUBLISH_WIN_PATH/MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.exe ./MkvDefaultTrackChanger-Windows-V${{ github.event.inputs.version }}.zip powershell Rename-Item $env:PUBLISH_LINUX_PATH/MkvDefaultTrackChanger.Gtk MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.Gtk powershell Compress-Archive $env:PUBLISH_LINUX_PATH/MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.Gtk ./MkvDefaultTrackChanger-Linux-V${{ github.event.inputs.version }}.zip - # TODO: Fix MacOS archive not working (it extracts to a folder instead of a .app file) - # powershell Rename-Item $env:PUBLISH_MAC_PATH/MkvDefaultTrackChanger.Mac.app ./MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.Mac.app - # powershell Compress-Archive $env:PUBLISH_MAC_PATH/MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.Mac.app ./MkvDefaultTrackChanger-MacOS-V${{ github.event.inputs.version }}.zip + powershell Rename-Item $env:PUBLISH_MAC_PATH/MkvDefaultTrackChanger.Mac.app ./MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.Mac.app + powershell Compress-Archive $env:PUBLISH_MAC_PATH/MkvDefaultTrackChanger-V${{ github.event.inputs.version }}.Mac.app ./MkvDefaultTrackChanger-MacOS-V${{ github.event.inputs.version }}.zip - name: Create Draft Release id: create_release uses: actions/create-release@v1 @@ -48,7 +47,7 @@ jobs: release_name: V${{ github.event.inputs.version }} body: TODO add description and rebuild MacOS locally draft: true - prerelease: false + prerelease: true - name: Upload release Windows uses: actions/upload-release-asset@v1.0.1 env: @@ -67,12 +66,12 @@ jobs: asset_path: ./MkvDefaultTrackChanger-Linux-V${{ github.event.inputs.version }}.zip asset_name: MkvDefaultTrackChanger-Linux-V${{ github.event.inputs.version }}.zip asset_content_type: application/zip - #- name: Upload release Mac - # uses: actions/upload-release-asset@v1.0.1 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # with: - # upload_url: ${{ steps.create_release.outputs.upload_url }} - # asset_path: ./MkvDefaultTrackChanger-MacOS-V${{ github.event.inputs.version }}.zip - # asset_name: MkvDefaultTrackChanger-MacOS-V${{ github.event.inputs.version }}.zip - # asset_content_type: application/zip + - name: Upload release Mac + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./MkvDefaultTrackChanger-MacOS-V${{ github.event.inputs.version }}.zip + asset_name: MkvDefaultTrackChanger-MacOS-V${{ github.event.inputs.version }}.zip + asset_content_type: application/zip diff --git a/Assets/Screenshot linux.jpg b/Assets/Screenshot linux.jpg deleted file mode 100644 index 17a2401..0000000 Binary files a/Assets/Screenshot linux.jpg and /dev/null differ diff --git a/Assets/Screenshot linux.png b/Assets/Screenshot linux.png new file mode 100644 index 0000000..7aa6c3c Binary files /dev/null and b/Assets/Screenshot linux.png differ diff --git a/Assets/Screenshot mac.png b/Assets/Screenshot mac.png index 2011e2f..cfd010b 100644 Binary files a/Assets/Screenshot mac.png and b/Assets/Screenshot mac.png differ diff --git a/Assets/Screenshot windows.jpg b/Assets/Screenshot windows.jpg index cfa1de9..4bccc21 100755 Binary files a/Assets/Screenshot windows.jpg and b/Assets/Screenshot windows.jpg differ diff --git a/Assets/logo.png b/Assets/logo.png new file mode 100644 index 0000000..8db4523 Binary files /dev/null and b/Assets/logo.png differ diff --git a/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs b/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs index 61b8521..ca7b327 100644 --- a/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs +++ b/MatroskaLib/MatroskaLib.Test/ByteHelperTest.cs @@ -1,76 +1,74 @@ using System.Collections.Generic; using Xunit; -namespace MatroskaLib.Test +namespace MatroskaLib.Test; + +public class ByteHelperTest { - public class ByteHelperTest + public static TheoryData> TestData1 = new() { + { 2UL, new () { 0x2 } }, + { 909UL, new () { 0x3, 0x8D } }, + { 1_800_70UL, new () { 0x2, 0xBF, 0x66 } }, + }; + [Theory, MemberData(nameof(TestData1))] + public void ToBytesTest(ulong value, List lsBytesExpected) + { + List lsResult = ByteHelper.ToBytes(value); + + Assert.Equal(lsBytesExpected, lsResult); + } + + 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 () { } } + }; + [Theory, MemberData(nameof(TestData2))] + public void RemoveLeftZeroesTest(List lsBytes, List lsBytesExpected) + { + ByteHelper.RemoveLeftZeroes(lsBytes); + + Assert.Equal(lsBytes, lsBytesExpected); + } + + public static IEnumerable Data() { - public static TheoryData> TestData1 = new () { - { 2UL, new () { 0x2 } }, - { 909UL, new () { 0x3, 0x8D } }, - { 1_800_70UL, new () { 0x2, 0xBF, 0x66 } }, + yield return new object[] + { + new List{ 0x6B, 0x2D, 0xAE, 0xBB, 0xD7, 0x81, 0x02 }, + new List{ 0x6B, 0x2D, 0xAE, 0xBE, 0xD7, 0x81, 0x02 }, + 4, + 3 }; - [Theory, MemberData(nameof(TestData1))] - public void ToBytesTest(ulong value, List lsBytesExpected) + yield return new object[] { - List lsResult = ByteHelper.ToBytes(value); - - Assert.Equal(lsBytesExpected, lsResult); - } - - 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 () { } } + 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[] + { + new List{ 0x81, 0x02, 0xAE, 0x42, 0x83, 0xD7, 0x81, 0x03 }, + new List{ 0x81, 0x02, 0xAE, 0x42, 0x87, 0xD7, 0x81, 0x03 }, + 5, + 4 }; - [Theory, MemberData(nameof(TestData2))] - public void RemoveLeftZeroesTest(List lsBytes, List lsBytesExpected) + yield return new object[] { - ByteHelper.RemoveLeftZeroes(lsBytes); + 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))] - Assert.Equal(lsBytes, lsBytesExpected); - } - - public static IEnumerable Data() { - yield return new object[] - { - new List{ 0x6B, 0x2D, 0xAE, 0xBB, 0xD7, 0x81, 0x02 }, - new List{ 0x6B, 0x2D, 0xAE, 0xBE, 0xD7, 0x81, 0x02 }, - 4, - 3 - }; - yield return new object[] - { - 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[] - { - 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[] - { - 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))] + public void TestChangeLength(List inputData, List expectedData, int position, int newAddition) + { + ByteHelper.ChangeLength(inputData, position, 0xAE, newAddition); - public void TestChangeLength(List inputData, List expectedData, int position, int newAddition) - { - ByteHelper.ChangeLength(inputData, position, 0xAE, newAddition); - - Assert.Equal(inputData, expectedData); - } - - + Assert.Equal(inputData, expectedData); } -} \ No newline at end of file +} diff --git a/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs b/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs index 5977c19..7e84dc7 100644 --- a/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs +++ b/MatroskaLib/MatroskaLib.Test/Helpers/MkvValidator.cs @@ -1,46 +1,48 @@ using System; using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; -namespace MatroskaLib.Test.Helpers +namespace MatroskaLib.Test.Helpers; + +public static class MkvValidator { - public class MkvValidator + private const string OutputRemoveRegex = @"(^At least one output file must be specified)|(^\[(.*)\] )"; + public static void Validate(string filePath) { - public static string outputRemoveRegex = @"(^At least one output file must be specified)|(^\[(.*)\] )"; - public static void Validate(string filePath) + Process p = new Process(); + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardError = true; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.FileName = "ffmpeg"; + p.StartInfo.Arguments = $"-i \"{filePath}\" -v error"; + p.Start(); + string output = p.StandardError.ReadToEnd(); + p.Close(); + output = Regex.Replace(output, OutputRemoveRegex, "", RegexOptions.Multiline).Trim(); + if (output.Length > 0) + { + throw new Exception("ffmpeg's mkv validation produced errors:" + Environment.NewLine + output); + } + + p = new Process(); + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardError = true; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + "mkvalidator.exe" : + "mkvalidator"; + p.StartInfo.Arguments = $"\"{filePath}\""; + p.Start(); + output = p.StandardError.ReadToEnd(); + p.Close(); + + if (!output.Contains("the file appears to be valid")) { - Process p = new Process(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardError = true; - p.StartInfo.CreateNoWindow = true; - p.StartInfo.FileName = "ffmpeg"; - p.StartInfo.Arguments = $"-i \"{filePath}\" -v error"; - p.Start(); - string output = p.StandardError.ReadToEnd(); - p.Close(); - output = Regex.Replace(output, outputRemoveRegex, "", RegexOptions.Multiline).Trim(); - if (output.Length > 0) - { - throw new Exception("ffmpeg's mkv validation produced errors:" + Environment.NewLine + output); - } - - /* - p = new Process(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardError = true; - p.StartInfo.CreateNoWindow = true; - p.StartInfo.FileName = "./mkvalidator.exe"; - p.StartInfo.Arguments = "\"" + filePath + "\""; - p.Start(); - output = p.StandardError.ReadToEnd(); - p.Close(); - - if (!output.Contains("the file appears to be valid")) - { - output = output.Replace(".", "").Trim(); - string errors = string.Join("\n", output.Split("\r\n").Where(x => x.Contains("ERR"))).Trim(); - throw new Exception(errors + "\r\n" + output); - }*/ + output = output.Replace(".", "").Trim(); + string errors = string.Join("\n", output.Split("\r\n").Where(x => x.Contains("ERR"))).Trim(); + throw new Exception(errors + "\r\n" + output); } } -} \ No newline at end of file +} diff --git a/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj b/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj index 78cb33d..fa88b03 100644 --- a/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj +++ b/MatroskaLib/MatroskaLib.Test/MatroskaLib.Test.csproj @@ -1,58 +1,64 @@ - - net5.0 + + net8.0 + false + true + - false - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + mkv files\TestFile1_MkvToolNix.mkv + PreserveNewest + + + mkv files\TestFile2_MkvToolNix.mkv + PreserveNewest + + + mkv files\TestFile3_HandBrake.mkv + PreserveNewest + + + mkv files\TestFile4_MkvProEdit.mkv + PreserveNewest + + + mkv files\TestFile5_MkvProEdit.mkv + PreserveNewest + + + PreserveNewest + + - - - mkv files\TestFile1_MkvToolNix.mkv - Always - - - mkv files\TestFile2_MkvToolNix.mkv - Always - - - mkv files\TestFile3_HandBrake.mkv - Always - - - mkv files\TestFile4_MkvProEdit.mkv - Always - - - mkv files\TestFile5_MkvProEdit.mkv - Always - - - - - - + + + + + + diff --git a/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs b/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs index ad99b7b..dc48fd0 100644 --- a/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs +++ b/MatroskaLib/MatroskaLib.Test/MatroskaLibTest.cs @@ -1,190 +1,175 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using Xunit; -using Xunit.Abstractions; using FluentAssertions; using MatroskaLib.Test.Helpers; +using MatroskaLib.Types; +using Xunit; -namespace MatroskaLib.Test +namespace MatroskaLib.Test; + +/* MkvToolNix + * Both voids, default track elements not present + * Handbrake + * Only first void with checksum elements + * MkvProEdit + * Only second void and may need to change length of that void + */ +public class MatroskaLibTest { - /* MkvToolNix - * Both voids, default track elements not present - * Handbrake - * Only first void with checksum elements - * MkvProEdit - * Only second void and may need to change length of that void - */ - public class MatroskaLibTest + private const string TestFilePath = "mkv files/TestFile.mkv"; + + [Theory] + [InlineData("mkv files/TestFile1_MkvToolNix.mkv")] + public void ReadTestFile1(string file) + { + string[] filePaths = { file }; + + List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); + List lsTracks = lsMkvFiles[0].tracks; + + lsMkvFiles.Should().HaveCount(1); + lsMkvFiles[0].voidPosition.Should().Be(119); + lsTracks.Should().HaveCount(3); + lsTracks[0].Should().BeEquivalentTo(new { number = 1, flagDefault = false, flagForced = true, language = "eng", name = "English main", type = TrackTypeEnum.subtitle }); + lsTracks[1].Should().BeEquivalentTo(new { number = 2, flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video }); + lsTracks[2].Should().BeEquivalentTo(new { number = 3, flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio }); + } + + [Theory] + [InlineData("mkv files/TestFile1_MkvToolNix.mkv")] + public void WriteTestFile1(string file) { - private const string testFilePath = "mkv files/TestFile.mkv"; - private readonly ITestOutputHelper _testOutputHelper; - - public MatroskaLibTest(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - - [Theory] - [InlineData("mkv files/TestFile1_MkvToolNix.mkv")] - public void ReadTestFile1(string file) - { - string[] filePaths = {file}; - - List lsMkvFiles = MatroskaIO.ReadMkvFiles(filePaths); - List lsTracks = lsMkvFiles[0].tracks; - - lsMkvFiles.Should().HaveCount(1); - lsMkvFiles[0].voidPosition.Should().Be(119); - lsTracks.Should().HaveCount(3); - lsTracks[0].Should().BeEquivalentTo(new {number = 1, flagDefault = false, flagForced = true, language = "eng", name = "English main", type = TrackTypeEnum.subtitle}); - lsTracks[1].Should().BeEquivalentTo(new {number = 2, flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video}); - lsTracks[2].Should().BeEquivalentTo(new {number = 3, flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio}); - } - - [Theory] - [InlineData("mkv files/TestFile1_MkvToolNix.mkv")] - public void WriteTestFile1(string file) - { - File.Copy(file, testFilePath, true); - List lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - lsMkvFiles[0].tracks[0].flagDefault = false; - lsMkvFiles[0].tracks[2].flagDefault = false; - - MatroskaIO.WriteMkvFile(testFilePath, lsMkvFiles[0].seekList, lsMkvFiles[0].tracks, lsMkvFiles[0].seekHeadCheckSum, lsMkvFiles[0].tracksCheckSum, lsMkvFiles[0].voidPosition, - lsMkvFiles[0].endPosition, lsMkvFiles[0].tracksPosition, lsMkvFiles[0].beginHeaderPosition); - lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - List lsTracks = lsMkvFiles[0].tracks; - - lsTracks.Should().HaveCount(3); - lsTracks[0].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle}); - lsTracks[1].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video}); - lsTracks[2].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio}); - MkvValidator.Validate(testFilePath); - } - - [Theory] - [InlineData("mkv files/TestFile2_MkvToolNix.mkv")] - public void ReadTestFile2(string file) - { - string[] filePaths = {file}; - - List lsMkvFiles = MatroskaIO.ReadMkvFiles(filePaths); - List lsTracks = lsMkvFiles[0].tracks; - - lsMkvFiles.Should().HaveCount(1); - lsMkvFiles[0].voidPosition.Should().Be(119); - lsTracks.Should().HaveCount(5); - lsTracks[0].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "fre", name = "French commentary", type = TrackTypeEnum.audio}); - lsTracks[1].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "eng", name = "", type = TrackTypeEnum.audio}); - lsTracks[2].Should().BeEquivalentTo(new {flagDefault = false, flagForced = true, language = "eng", name = "English main", type = TrackTypeEnum.subtitle}); - lsTracks[3].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "jpn", name = "日本語", type = TrackTypeEnum.subtitle}); - lsTracks[4].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video}); - } - - [Theory] - [InlineData("mkv files/TestFile2_MkvToolNix.mkv")] - public void WriteTestFile2(string file) - { - File.Copy(file, testFilePath, true); - List lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - lsMkvFiles[0].tracks[1].flagDefault = true; - lsMkvFiles[0].tracks[3].flagDefault = true; - - MatroskaIO.WriteMkvFile(testFilePath, lsMkvFiles[0].seekList, lsMkvFiles[0].tracks, lsMkvFiles[0].seekHeadCheckSum, lsMkvFiles[0].tracksCheckSum, lsMkvFiles[0].voidPosition, - lsMkvFiles[0].endPosition, lsMkvFiles[0].tracksPosition, lsMkvFiles[0].beginHeaderPosition); - lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - List lsTracks = lsMkvFiles[0].tracks; - - lsTracks.Should().HaveCount(5); - lsTracks[0].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "fre", name = "French commentary", type = TrackTypeEnum.audio}); - lsTracks[1].Should().BeEquivalentTo(new {flagDefault = true, flagForced = false, language = "eng", name = "", type = TrackTypeEnum.audio}); - lsTracks[2].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle}); - lsTracks[3].Should().BeEquivalentTo(new {flagDefault = true, flagForced = false, language = "jpn", name = "日本語", type = TrackTypeEnum.subtitle}); - lsTracks[4].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video}); - MkvValidator.Validate(testFilePath); - } - - [Theory] - [InlineData("mkv files/TestFile3_HandBrake.mkv")] - public void ReadTestFile3(string file) - { - string[] filePaths = {file}; - - List lsMkvFiles = MatroskaIO.ReadMkvFiles(filePaths); - List lsTracks = lsMkvFiles[0].tracks; - - lsMkvFiles.Should().HaveCount(1); - lsMkvFiles[0].voidPosition.Should().Be(123); - lsTracks.Should().HaveCount(4); - lsTracks[0].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", name = "", type = TrackTypeEnum.video}); - lsTracks[1].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", name = "Stereo", type = TrackTypeEnum.audio}); - lsTracks[2].Should().BeEquivalentTo(new {flagDefault = false, flagForced = true, language = "eng", name = "", type = TrackTypeEnum.subtitle}); - lsTracks[3].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "jpn", name = "", type = TrackTypeEnum.subtitle}); - } - - [Theory] - [InlineData("mkv files/TestFile3_HandBrake.mkv")] - public void WriteTestFile3(string file) - { - File.Copy(file, testFilePath, true); - List lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - lsMkvFiles[0].tracks[1].flagDefault = true; - lsMkvFiles[0].tracks[3].flagDefault = true; - - MatroskaIO.WriteMkvFile(testFilePath, lsMkvFiles[0].seekList, lsMkvFiles[0].tracks, lsMkvFiles[0].seekHeadCheckSum, lsMkvFiles[0].tracksCheckSum, lsMkvFiles[0].voidPosition, - lsMkvFiles[0].endPosition, lsMkvFiles[0].tracksPosition, lsMkvFiles[0].beginHeaderPosition); - lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - List lsTracks = lsMkvFiles[0].tracks; - - lsTracks.Should().HaveCount(4); - lsTracks[0].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", name = "", type = TrackTypeEnum.video}); - lsTracks[1].Should().BeEquivalentTo(new {flagDefault = true, flagForced = false, language = "und", name = "Stereo", type = TrackTypeEnum.audio}); - lsTracks[2].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "eng", name = "", type = TrackTypeEnum.subtitle}); - lsTracks[3].Should().BeEquivalentTo(new {flagDefault = true, flagForced = false, language = "jpn", name = "", type = TrackTypeEnum.subtitle}); - MkvValidator.Validate(testFilePath); - } - - [Theory] - [InlineData("mkv files/TestFile4_MkvProEdit.mkv")] - [InlineData("mkv files/TestFile5_MkvProEdit.mkv")] - public void ReadTestFile4(string file) - { - string[] filePaths = {file}; - - List lsMkvFiles = MatroskaIO.ReadMkvFiles(filePaths); - List lsTracks = lsMkvFiles[0].tracks; - - lsMkvFiles.Should().HaveCount(1); - lsTracks.Should().HaveCount(3); - lsTracks[0].Should().BeEquivalentTo(new {number = 1, flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle}); - lsTracks[1].Should().BeEquivalentTo(new {number = 2, flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video}); - lsTracks[2].Should().BeEquivalentTo(new {number = 3, flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio}); - } - - [Theory] - [InlineData("mkv files/TestFile4_MkvProEdit.mkv")] - [InlineData("mkv files/TestFile5_MkvProEdit.mkv")] - public void WriteTestFile4(string file) - { - File.Copy(file, testFilePath, true); - List lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - lsMkvFiles[0].tracks[0].flagDefault = false; - lsMkvFiles[0].tracks[2].flagDefault = false; - - MatroskaIO.WriteMkvFile(testFilePath, lsMkvFiles[0].seekList, lsMkvFiles[0].tracks, lsMkvFiles[0].seekHeadCheckSum, lsMkvFiles[0].tracksCheckSum, lsMkvFiles[0].voidPosition, - lsMkvFiles[0].endPosition, lsMkvFiles[0].tracksPosition, lsMkvFiles[0].beginHeaderPosition); - lsMkvFiles = MatroskaIO.ReadMkvFiles(new[] {testFilePath}); - List lsTracks = lsMkvFiles[0].tracks; - - lsTracks.Should().HaveCount(3); - lsTracks[0].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle}); - lsTracks[1].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video}); - lsTracks[2].Should().BeEquivalentTo(new {flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio}); - MkvValidator.Validate(testFilePath); - } + File.Copy(file, TestFilePath, true); + List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles[0].tracks[0].flagDefault = false; + lsMkvFiles[0].tracks[2].flagDefault = false; + + MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); + lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsTracks = lsMkvFiles[0].tracks; + + lsTracks.Should().HaveCount(3); + lsTracks[0].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle }); + lsTracks[1].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video }); + lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio }); + MkvValidator.Validate(TestFilePath); + } + [Theory] + [InlineData("mkv files/TestFile2_MkvToolNix.mkv")] + public void ReadTestFile2(string file) + { + string[] filePaths = { file }; + + List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); + List lsTracks = lsMkvFiles[0].tracks; + + lsMkvFiles.Should().HaveCount(1); + lsMkvFiles[0].voidPosition.Should().Be(119); + lsTracks.Should().HaveCount(5); + lsTracks[0].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "fre", name = "French commentary", type = TrackTypeEnum.audio }); + lsTracks[1].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "eng", name = "", type = TrackTypeEnum.audio }); + lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = true, language = "eng", name = "English main", type = TrackTypeEnum.subtitle }); + lsTracks[3].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "jpn", name = "日本語", type = TrackTypeEnum.subtitle }); + lsTracks[4].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video }); + } + [Theory] + [InlineData("mkv files/TestFile2_MkvToolNix.mkv")] + public void WriteTestFile2(string file) + { + File.Copy(file, TestFilePath, true); + List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles[0].tracks[1].flagDefault = true; + lsMkvFiles[0].tracks[3].flagDefault = true; + + MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); + lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsTracks = lsMkvFiles[0].tracks; + + lsTracks.Should().HaveCount(5); + lsTracks[0].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "fre", name = "French commentary", type = TrackTypeEnum.audio }); + lsTracks[1].Should().BeEquivalentTo(new { flagDefault = true, flagForced = false, language = "eng", name = "", type = TrackTypeEnum.audio }); + lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle }); + lsTracks[3].Should().BeEquivalentTo(new { flagDefault = true, flagForced = false, language = "jpn", name = "日本語", type = TrackTypeEnum.subtitle }); + lsTracks[4].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video }); + MkvValidator.Validate(TestFilePath); + } + + [Theory] + [InlineData("mkv files/TestFile3_HandBrake.mkv")] + public void ReadTestFile3(string file) + { + string[] filePaths = { file }; + + List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); + List lsTracks = lsMkvFiles[0].tracks; + + lsMkvFiles.Should().HaveCount(1); + lsMkvFiles[0].voidPosition.Should().Be(123); + lsTracks.Should().HaveCount(4); + lsTracks[0].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", name = "", type = TrackTypeEnum.video }); + lsTracks[1].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", name = "Stereo", type = TrackTypeEnum.audio }); + lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = true, language = "eng", name = "", type = TrackTypeEnum.subtitle }); + lsTracks[3].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "jpn", name = "", type = TrackTypeEnum.subtitle }); + } + + [Theory] + [InlineData("mkv files/TestFile3_HandBrake.mkv")] + public void WriteTestFile3(string file) + { + File.Copy(file, TestFilePath, true); + List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles[0].tracks[1].flagDefault = true; + lsMkvFiles[0].tracks[3].flagDefault = true; + + MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); + lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsTracks = lsMkvFiles[0].tracks; + + lsTracks.Should().HaveCount(4); + lsTracks[0].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", name = "", type = TrackTypeEnum.video }); + lsTracks[1].Should().BeEquivalentTo(new { flagDefault = true, flagForced = false, language = "und", name = "Stereo", type = TrackTypeEnum.audio }); + lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "eng", name = "", type = TrackTypeEnum.subtitle }); + lsTracks[3].Should().BeEquivalentTo(new { flagDefault = true, flagForced = false, language = "jpn", name = "", type = TrackTypeEnum.subtitle }); + MkvValidator.Validate(TestFilePath); + } + + [Theory] + [InlineData("mkv files/TestFile4_MkvProEdit.mkv")] + [InlineData("mkv files/TestFile5_MkvProEdit.mkv")] + public void ReadTestFile4(string file) + { + string[] filePaths = { file }; + + List lsMkvFiles = MatroskaReader.ReadMkvFiles(filePaths); + List lsTracks = lsMkvFiles[0].tracks; + + lsMkvFiles.Should().HaveCount(1); + lsTracks.Should().HaveCount(3); + lsTracks[0].Should().BeEquivalentTo(new { number = 1, flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle }); + lsTracks[1].Should().BeEquivalentTo(new { number = 2, flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video }); + lsTracks[2].Should().BeEquivalentTo(new { number = 3, flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio }); + } + + [Theory] + [InlineData("mkv files/TestFile4_MkvProEdit.mkv")] + [InlineData("mkv files/TestFile5_MkvProEdit.mkv")] + public void WriteTestFile4(string file) + { + File.Copy(file, TestFilePath, true); + List lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + lsMkvFiles[0].tracks[0].flagDefault = false; + lsMkvFiles[0].tracks[2].flagDefault = false; + + MatroskaWriter.WriteMkvFile(lsMkvFiles[0]); + lsMkvFiles = MatroskaReader.ReadMkvFiles(new[] { TestFilePath }); + List lsTracks = lsMkvFiles[0].tracks; + + lsTracks.Should().HaveCount(3); + lsTracks[0].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "eng", name = "English main", type = TrackTypeEnum.subtitle }); + lsTracks[1].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "und", type = TrackTypeEnum.video }); + lsTracks[2].Should().BeEquivalentTo(new { flagDefault = false, flagForced = false, language = "jpn", type = TrackTypeEnum.audio }); + MkvValidator.Validate(TestFilePath); } -} \ No newline at end of file +} diff --git a/MatroskaLib/MatroskaLib.Test/MkvFileTest.cs b/MatroskaLib/MatroskaLib.Test/MkvFileTest.cs new file mode 100644 index 0000000..0fcfca0 --- /dev/null +++ b/MatroskaLib/MatroskaLib.Test/MkvFileTest.cs @@ -0,0 +1,54 @@ +using FluentAssertions; +using MatroskaLib.Types; +using Xunit; + +namespace MatroskaLib.Test; + +public class MkvFileTest +{ + [Fact] + public void TestToString() + { + var mkvFile = new MkvFile() + { + filePath = "/home/some-path", + tracks = [new Track(null!) { type = TrackTypeEnum.subtitle, flagForced = true }], + endPosition = 20, + beginHeaderPosition = 30, + seekHeadCheckSum = null, + seekList = [], + tracksCheckSum = 50, + tracksPosition = 30, + voidPosition = 40 + }; + + string json = mkvFile.ToString(); + + json.Should().Be(""" + { + "filePath": "", + "tracks": [ + { + "trackLengthByteNumber": 0, + "number": 0, + "flagDefault": false, + "flagDefaultByteNumber": 0, + "flagForced": true, + "flagForcedByteNumber": 0, + "flagTypebytenumber": 0, + "type": "subtitle", + "name": "", + "language": "eng" + } + ], + "seekList": [], + "seekHeadCheckSum": null, + "tracksCheckSum": 50, + "voidPosition": 40, + "endPosition": 20, + "tracksPosition": 30, + "beginHeaderPosition": 30 + } + """); + } +} diff --git a/MatroskaLib/MatroskaLib.Test/README.md b/MatroskaLib/MatroskaLib.Test/README.md new file mode 100644 index 0000000..68c4666 --- /dev/null +++ b/MatroskaLib/MatroskaLib.Test/README.md @@ -0,0 +1,20 @@ +# Unit tests + +## Setup +The unit tests depend on ffmpeg and mkvalidator to make sure the program outputs valid MKV files. If you want to run the tests locally, please follow the steps below: + +**Windows** +- `winget install ffmpeg` +- *mkvalidator.exe is already included for windows, no need to install* + +**Linux** +- Install ffmpeg using your package manager or see the [ffmpeg website](https://ffmpeg.org/download.html) for instructions to install. +- If you are on Arch Linux: [install mkvalidator from the AUR](https://aur.archlinux.org/packages/mkvalidator) + If you are on a Ubuntu based distro: [use this PPA](https://launchpad.net/~hizo/+archive/ubuntu/mkv-extractor-gui) to install mkvalidator + Otherwise: [Download the source code](https://sourceforge.net/projects/matroska/files/mkvalidator/) and compile using make. + +**MacOS** +Use homebrew: +``` +brew install mkvalidator ffmpeg +``` \ No newline at end of file diff --git a/MatroskaLib/MatroskaLib.Test/mkvalidator.exe b/MatroskaLib/MatroskaLib.Test/mkvalidator.exe new file mode 100644 index 0000000..9a81023 Binary files /dev/null and b/MatroskaLib/MatroskaLib.Test/mkvalidator.exe differ diff --git a/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs b/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs index 595b8a3..2b40271 100644 --- a/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs +++ b/MatroskaLib/MatroskaLib/Helpers/ByteHelper.cs @@ -9,10 +9,10 @@ public static class ByteHelper public static List ToBytes(ulong value, bool removePaddingZeroes = true) { List lsBytes = BitConverter.GetBytes(value).ToList(); - - if (BitConverter.IsLittleEndian) + + if (BitConverter.IsLittleEndian) lsBytes.Reverse(); - if (removePaddingZeroes) + if (removePaddingZeroes) RemoveLeftZeroes(lsBytes); return lsBytes; } @@ -30,9 +30,9 @@ public static void RemoveLeftZeroes(List lsValue) public static void ChangeLength(List lsBytes, int position, ulong elementId, int newAdition) { List elementIdBytes = ToBytes(elementId); - + List lsLengthBytes = new(); - for (int i = position-1; i >= 0; i--) + for (int i = position - 1; i >= 0; i--) { lsLengthBytes.Add(lsBytes[i]); if (lsBytes.GetRange(i - elementIdBytes.Count, elementIdBytes.Count).SequenceEqual(elementIdBytes)) @@ -52,17 +52,17 @@ public static void ChangeLength(List lsBytes, int position, List lsL // Apply addition or negative if (newAdition > 0) ret += Convert.ToUInt32(newAdition); - else + else ret -= Convert.ToUInt32(newAdition * -1); // 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"); - + // Replace old length with new length bytes lsBytes.RemoveRange(position, lsNewBytes.Count); lsBytes.InsertRange(position, lsNewBytes); - + } public static ulong FromBytesToUlong(List lsLengthBytes) @@ -72,7 +72,7 @@ public static ulong FromBytesToUlong(List lsLengthBytes) for (int i = 0; i < 8 && i < lsLengthBytes.Count; i++) { ret <<= 8; - ret |= (ulong) lsLengthBytes[i] & 0xFF; + ret |= (ulong)lsLengthBytes[i] & 0xFF; } return ret; @@ -95,13 +95,13 @@ public static void ChangeVoidLength(List lsBytes, int voidPosition) lsBytes.InsertRange(voidPosition + 1, voidLengthBytes); } - public static List GetLengthBytes(uint value, int maxLength) => + public static List GetLengthBytes(uint value, int maxLength) => ToBytes(value | 1UL << (7 * maxLength)); - + public static void ReplaceHashWithVoid(List lsBytes, int checkSumPosition) { lsBytes.RemoveRange(checkSumPosition, 6); lsBytes.InsertRange(checkSumPosition, new byte[] { 0xEC, 0x84, 0x0, 0x0, 0x0, 0x0 }); } } -} \ No newline at end of file +} diff --git a/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs b/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs index d734768..b1d9906 100644 --- a/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs +++ b/MatroskaLib/MatroskaLib/Helpers/CustomExtensions.cs @@ -1,22 +1,22 @@ -using NEbml.Core; using System; +using NEbml.Core; namespace NEbml.Matroska { - public static class CustomExtensions - { - public static void LocateElement(this EbmlReader reader, ulong descriptor) - { - while (reader.ReadNext()) - { - if (reader.ElementId.EncodedValue == descriptor) - { - reader.EnterContainer(); - return; - } - } + public static class CustomExtensions + { + public static void LocateElement(this EbmlReader reader, ulong descriptor) + { + while (reader.ReadNext()) + { + if (reader.ElementId.EncodedValue == descriptor) + { + reader.EnterContainer(); + return; + } + } - throw new Exception($"Cannot find descriptor 0x{descriptor:X}"); - } - } -} \ No newline at end of file + throw new Exception($"Cannot find descriptor 0x{descriptor:X}"); + } + } +} diff --git a/MatroskaLib/MatroskaLib/MatroskaIO.cs b/MatroskaLib/MatroskaLib/MatroskaIO.cs deleted file mode 100644 index 9f2be64..0000000 --- a/MatroskaLib/MatroskaLib/MatroskaIO.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.IO; -using NEbml.Core; -using NEbml.Matroska; -using System; - -namespace MatroskaLib -{ - // MKV header structure https://hybridego.net/entry/Matroska-Header - // https://github.com/OlegZee/NEbml/blob/master/Src/MkvTitleEdit/Matroska/SegmentInfoUpdater.cs - public static class MatroskaIO - { - public static List ReadMkvFiles(string[] filePaths) - { - var lsMkvFiles = new List(); - foreach (var filePath in filePaths) - { - List trackList = new List(); - List seekList = new List(); - using (var dataStream = File.Open(filePath, FileMode.Open)) - { - var reader = new EbmlReader(dataStream); - - reader.LocateElement(MatroskaElements.segment); - - reader.LocateElement(MatroskaElements.seekHead); - int? seekHeadCheckSum = null; - int? tracksCheckSum = null; - - while (reader.ReadNext()) - { - if (reader.ElementId.EncodedValue == MatroskaElements.checkSum) - { - seekHeadCheckSum = (int) reader.ElementPosition; - } - else if (reader.ElementId.EncodedValue == MatroskaElements.seekEntry) - { - var seek = new Seek(reader); - - reader.EnterContainer(); - while (reader.ReadNext()) - { - seek.applyElement(dataStream); - } - - reader.LeaveContainer(); - - if (seekList.All(x => x.seekID != seek.seekID)) - seekList.Add(seek); - } - } - - reader.LeaveContainer(); - - reader.LocateElement(MatroskaElements.voidElement); - int voidPosition = (int) reader.ElementPosition; - reader.LeaveContainer(); - - int beginHeaderPosition = 0; - int tracksPosition; - if (seekList.FirstOrDefault(x => x.seekID == MatroskaElements.tracks)?.seekPosition < (ulong) voidPosition) - { - // Void is after track element, read file again and go to tracks element - dataStream.Position = 0; - reader = new EbmlReader(dataStream); - reader.LocateElement(MatroskaElements.segment); - reader.LocateElement(MatroskaElements.tracks); - tracksPosition = (int) dataStream.Position; - } - else - { - reader.ReadNext(); - beginHeaderPosition = (int) reader.ElementPosition; - - if (reader.ElementId.EncodedValue != MatroskaElements.tracks) - reader.LocateElement(MatroskaElements.tracks); - else - reader.EnterContainer(); - tracksPosition = (int) dataStream.Position; - } - - // Loop over tracks - while (reader.ReadNext()) - { - if (reader.ElementId.EncodedValue == MatroskaElements.checkSum) - { - tracksCheckSum = (int) reader.ElementPosition; - } - else if (reader.ElementId.EncodedValue == TrackElements.entry) - { - var track = new Track(reader); - track.trackLengthByteNumber = (int)dataStream.Position; - - // Loop over track element and put them in track - reader.EnterContainer(); - while (reader.ReadNext()) - { - track.ApplyElement(dataStream); - } - - reader.LeaveContainer(); - - trackList.Add(track); - } - } - - reader.LeaveContainer(); - reader.ReadNext(); - int endPosition = beginHeaderPosition != 0 ? (int) reader.ElementPosition : voidPosition + 8 + 1; - - // TODO way too many parameters, put in seperate object - lsMkvFiles.Add(new MkvFile(filePath, trackList, seekList, seekHeadCheckSum, tracksCheckSum, voidPosition, endPosition, - tracksPosition, beginHeaderPosition)); - } - } - - return lsMkvFiles; - } - - public static void WriteMkvFile(string filePath, List seekList, List trackList, - int? seekHeadCheckSum, int? tracksCheckSum, int voidPosition, - int endPosition, int tracksPosition, int beginHeaderPosition) - { - using (var dataStream = File.Open(filePath, FileMode.Open)) - { - byte[] bytes = new byte[endPosition]; - - dataStream.Seek(0, SeekOrigin.Begin); - dataStream.Read(bytes, 0, bytes.Length); - List lsBytes = new List(bytes); - - int offset = 0; - foreach (Track t in trackList - .Where(x => x.type is TrackTypeEnum.audio or TrackTypeEnum.subtitle) - .Where(x => x is not TrackDisable) // Maybe isn't needed? - ) - { - byte defaultFlag = (byte) (t.flagDefault ? 0x1 : 0x0); - if (t.flagDefaultByteNumber != 0) - { - // Default flag is present, change it - lsBytes[offset + t.flagDefaultByteNumber] = defaultFlag; - } - else if (t.flagTypebytenumber != 0) - { - // Default flag is not present, add it after the track entry element - ByteHelper.ChangeLength(lsBytes, offset + t.trackLengthByteNumber, TrackElements.entry, 3); - lsBytes.InsertRange(offset + t.flagTypebytenumber + 1, - new byte[] {0x88, 0x81, defaultFlag}); - offset += 3; - } - - // Set forced flag to 0 if present - if (t.flagForcedByteNumber != 0) - { - int correction = t.flagForcedByteNumber < t.flagTypebytenumber ? 3 : 0; - lsBytes[offset + t.flagForcedByteNumber - correction] = 0x0; - } - } - - // Change length of Tracks element - ByteHelper.ChangeLength(lsBytes, tracksPosition, MatroskaElements.tracks, offset); - - if (beginHeaderPosition != 0 && offset != 0) - { - // Remove some void data so it fits in the file without re-writing the entire file - lsBytes.RemoveRange(beginHeaderPosition - offset, offset); - ByteHelper.ChangeVoidLength(lsBytes, voidPosition); - - // In the Segment Information part, change the position of the tracks and segmentinfo - // elements as they have been changed. - foreach (var s in seekList.Where(s => - s.seekID == MatroskaElements.tracks || s.seekID == 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"); - - lsBytes.RemoveRange(s.seekPositionByteNumber, lsNewBytes.Count); - lsBytes.InsertRange(s.seekPositionByteNumber, lsNewBytes); - } - - if (seekHeadCheckSum.HasValue) - ByteHelper.ReplaceHashWithVoid(lsBytes, seekHeadCheckSum.Value); - if (tracksCheckSum.HasValue) - ByteHelper.ReplaceHashWithVoid(lsBytes, tracksCheckSum.Value - offset); - } - else if (beginHeaderPosition == 0 && offset != 0) - { - var lsVoidLength = lsBytes.GetRange(voidPosition + offset + 1, 8); - ByteHelper.ChangeLength(lsBytes, voidPosition + offset + 1, lsVoidLength, offset*-1); - } - - // Write modiefied changes to file - dataStream.Seek(0, SeekOrigin.Begin); - dataStream.Write(lsBytes.ToArray(), 0, lsBytes.Count); - } - } - } -} \ No newline at end of file diff --git a/MatroskaLib/MatroskaLib/MatroskaLib.csproj b/MatroskaLib/MatroskaLib/MatroskaLib.csproj index f0ac6e8..52260f7 100644 --- a/MatroskaLib/MatroskaLib/MatroskaLib.csproj +++ b/MatroskaLib/MatroskaLib/MatroskaLib.csproj @@ -1,11 +1,14 @@  - net5.0 + net8.0 + 11 + enable + true - + diff --git a/MatroskaLib/MatroskaLib/MatroskaReader.cs b/MatroskaLib/MatroskaLib/MatroskaReader.cs new file mode 100644 index 0000000..9398bc9 --- /dev/null +++ b/MatroskaLib/MatroskaLib/MatroskaReader.cs @@ -0,0 +1,153 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MatroskaLib.Types; +using NEbml.Core; +using NEbml.Matroska; + +namespace MatroskaLib; + +public static class MatroskaReader +{ + public static List ReadMkvFiles(string[] filePaths) + { + var mkvFiles = new List(); + foreach (var filePath in filePaths) + { + var tracks = new List(); + var seekList = new List(); + + using var fileStream = File.Open(filePath, FileMode.Open); + var reader = new EbmlReader(fileStream); + + int? seekHeadCheckSum = _ReadSeekHead(reader, fileStream, seekList); + int voidPosition = _LocateVoidElement(reader); + + (int tracksPosition, int beginHeaderPosition) = _DetermineTracksPosition(ref reader, fileStream, seekList, voidPosition); + int? tracksCheckSum = _ReadTracks(reader, fileStream, tracks); + + int endPosition = _DetermineEndPosition(reader, beginHeaderPosition, voidPosition); + + mkvFiles.Add(new MkvFile + { + filePath = filePath, + beginHeaderPosition = beginHeaderPosition, + endPosition = endPosition, + seekHeadCheckSum = seekHeadCheckSum, + seekList = seekList, + tracks = tracks, + tracksCheckSum = tracksCheckSum, + tracksPosition = tracksPosition, + voidPosition = voidPosition + }); + } + + return mkvFiles; + } + + private static int? _ReadSeekHead(EbmlReader reader, FileStream fileStream, List seekList) + { + int? seekHeadCheckSum = null; + + reader.LocateElement(MatroskaElements.Segment); + reader.LocateElement(MatroskaElements.SeekHead); + + while (reader.ReadNext()) + { + if (reader.ElementId.EncodedValue == MatroskaElements.CheckSum) + { + seekHeadCheckSum = (int)reader.ElementPosition; + } + else if (reader.ElementId.EncodedValue == MatroskaElements.SeekEntry) + { + var seek = new Seek(reader); + + reader.EnterContainer(); + while (reader.ReadNext()) + { + seek.ApplyElement(fileStream); + } + reader.LeaveContainer(); + + if (seekList.All(x => x.seekId != seek.seekId)) + seekList.Add(seek); + } + } + reader.LeaveContainer(); + + return seekHeadCheckSum; + } + + private static int _LocateVoidElement(EbmlReader reader) + { + reader.LocateElement(MatroskaElements.VoidElement); + var voidPosition = (int)reader.ElementPosition; + reader.LeaveContainer(); + return voidPosition; + } + + private static (int tracksPosition, int beginHeaderPosition) _DetermineTracksPosition(ref EbmlReader reader, FileStream fileStream, List seekList, int voidPosition) + { + int beginHeaderPosition = 0; + + if (seekList.FirstOrDefault(x => x.seekId == MatroskaElements.Tracks)?.seekPosition < (ulong)voidPosition) + { + // Void is after track element, read file again and go to tracks element + fileStream.Position = 0; + reader = new EbmlReader(fileStream); + reader.LocateElement(MatroskaElements.Segment); + reader.LocateElement(MatroskaElements.Tracks); + } + else + { + reader.ReadNext(); + beginHeaderPosition = (int)reader.ElementPosition; + + if (reader.ElementId.EncodedValue != MatroskaElements.Tracks) + reader.LocateElement(MatroskaElements.Tracks); + else + reader.EnterContainer(); + } + return ((int)fileStream.Position, beginHeaderPosition); + } + + private static int? _ReadTracks(EbmlReader reader, FileStream fileStream, List trackList) + { + int? tracksCheckSum = null; + + while (reader.ReadNext()) + { + if (reader.ElementId.EncodedValue == MatroskaElements.CheckSum) + { + tracksCheckSum = (int)reader.ElementPosition; + } + else if (reader.ElementId.EncodedValue == TrackElements.Entry) + { + var track = new Track(reader) + { + trackLengthByteNumber = (int)fileStream.Position + }; + + reader.EnterContainer(); + while (reader.ReadNext()) + { + track.ApplyElement(fileStream); + } + reader.LeaveContainer(); + + trackList.Add(track); + } + } + reader.LeaveContainer(); + + return tracksCheckSum; + } + + private static int _DetermineEndPosition(EbmlReader reader, int beginHeaderPosition, int voidPosition) + { + reader.ReadNext(); + return beginHeaderPosition != 0 ? + (int)reader.ElementPosition : + voidPosition + 9; + } +} diff --git a/MatroskaLib/MatroskaLib/MatroskaWriter.cs b/MatroskaLib/MatroskaLib/MatroskaWriter.cs new file mode 100644 index 0000000..8806a97 --- /dev/null +++ b/MatroskaLib/MatroskaLib/MatroskaWriter.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MatroskaLib.Types; + +namespace MatroskaLib; + +public static class MatroskaWriter +{ + public static void WriteMkvFile(MkvFile mkfFile) + { + using var dataStream = File.Open(mkfFile.filePath, FileMode.Open); + dataStream.Seek(0, SeekOrigin.Begin); + + byte[] bytes = new byte[mkfFile.endPosition]; + dataStream.Read(bytes, 0, bytes.Length); + List lsBytes = new List(bytes); + + int offset = 0; + _ChangeTrackElements(mkfFile.tracks, lsBytes, ref offset); + ByteHelper.ChangeLength(lsBytes, mkfFile.tracksPosition, MatroskaElements.Tracks, offset); + + _ChangeVoidLengthAndHeaders(mkfFile.seekList, mkfFile.seekHeadCheckSum, mkfFile.tracksCheckSum, mkfFile.voidPosition, mkfFile.beginHeaderPosition, + offset, lsBytes); + + // Write modified changes to file + dataStream.Seek(0, SeekOrigin.Begin); + dataStream.Write(lsBytes.ToArray(), 0, lsBytes.Count); + } + + private static void _ChangeTrackElements(List tracks, List lsBytes, ref int offset) + { + foreach (Track t in tracks.Where(x => x.type is TrackTypeEnum.audio or TrackTypeEnum.subtitle)) + { + byte defaultFlag = (byte)(t.flagDefault ? 0x1 : 0x0); + if (t.flagDefaultByteNumber != 0) + { + // Default flag is present, change it + lsBytes[offset + t.flagDefaultByteNumber] = defaultFlag; + } + else if (t.flagTypebytenumber != 0) + { + // Default flag is not present, add it after the track entry element + ByteHelper.ChangeLength(lsBytes, offset + t.trackLengthByteNumber, TrackElements.Entry, 3); + lsBytes.InsertRange(offset + t.flagTypebytenumber + 1, + new byte[] { 0x88, 0x81, defaultFlag }); + offset += 3; + } + + // Set forced flag to 0 if present + if (t.flagForcedByteNumber != 0) + { + int correction = t.flagForcedByteNumber < t.flagTypebytenumber ? 3 : 0; + lsBytes[offset + t.flagForcedByteNumber - correction] = 0x0; + } + } + } + + private static void _ChangeVoidLengthAndHeaders(List seekList, int? seekHeadCheckSum, int? tracksCheckSum, + int voidPosition, int beginHeaderPosition, int offset, List lsBytes) + { + if (beginHeaderPosition != 0 && offset != 0) + { + // Void is before the header, change the length of the void element + lsBytes.RemoveRange(beginHeaderPosition - offset, offset); + ByteHelper.ChangeVoidLength(lsBytes, voidPosition); + + // In the Segment Information part, change the position of the tracks and segmentinfo + // elements as they have been changed. + 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"); + + lsBytes.RemoveRange(s.seekPositionByteNumber, lsNewBytes.Count); + lsBytes.InsertRange(s.seekPositionByteNumber, lsNewBytes); + } + + // Remove checksums + if (seekHeadCheckSum.HasValue) + ByteHelper.ReplaceHashWithVoid(lsBytes, seekHeadCheckSum.Value); + if (tracksCheckSum.HasValue) + ByteHelper.ReplaceHashWithVoid(lsBytes, tracksCheckSum.Value - offset); + } + else if (beginHeaderPosition == 0 && offset != 0) + { + // Void is after the header, change the length of the void element + var lsVoidLength = lsBytes.GetRange(voidPosition + offset + 1, 8); + ByteHelper.ChangeLength(lsBytes, voidPosition + offset + 1, lsVoidLength, offset * -1); + } + } +} diff --git a/MatroskaLib/MatroskaLib/MkvFilesContainer.cs b/MatroskaLib/MatroskaLib/MkvFilesContainer.cs index 821bad0..9499db6 100644 --- a/MatroskaLib/MatroskaLib/MkvFilesContainer.cs +++ b/MatroskaLib/MatroskaLib/MkvFilesContainer.cs @@ -1,61 +1,56 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json; +using MatroskaLib.Types; -namespace MatroskaLib +namespace MatroskaLib; + +public class MkvFilesContainer { - public class MkvFilesContainer - { - public List lsMkvFiles = new(); - public List lsMkFilesRejected = new(); + public readonly List MkvFiles = new(); + public readonly List MkFilesRejected = new(); - public MkvFilesContainer(string[] filePaths) + public MkvFilesContainer(string[] filePaths) + { + var files = MatroskaReader.ReadMkvFiles(filePaths); + MkvFiles.Add(files[0]); + for (int i = 1; i < files.Count; i++) { - var lsMkvFiles = MatroskaIO.ReadMkvFiles(filePaths); - this.lsMkvFiles.Add(lsMkvFiles[0]); - for (int i = 1; i < lsMkvFiles.Count; i++) - { - if (lsMkvFiles[0].CompareTo(lsMkvFiles[i]) == 0) - this.lsMkvFiles.Add(lsMkvFiles[i]); - else - this.lsMkFilesRejected.Add(lsMkvFiles[i]); - } + if (files[0].CompareTo(files[i]) == 0) + MkvFiles.Add(files[i]); + else + MkFilesRejected.Add(files[i]); } + } - public void WriteChanges(Action setDefaultIfSelected) + public void WriteChanges(Action setDefaultIfSelected) + { + foreach (MkvFile file in MkvFiles) { - foreach (MkvFile file in lsMkvFiles) - { - file.tracks.ForEach(setDefaultIfSelected); - MatroskaIO.WriteMkvFile(file.filePath, file.seekList, file.tracks, file.seekHeadCheckSum, - file.tracksCheckSum, file.voidPosition, file.endPosition, file.tracksPosition, - file.beginHeaderPosition); - } + file.tracks.ForEach(setDefaultIfSelected); + MatroskaWriter.WriteMkvFile(file); } + } - public List GetSubtitleTracks() - { - var lsAudioTracks = this.lsMkvFiles.First() - .tracks - .Where(x => x.type == TrackTypeEnum.subtitle) - .ToList(); - - lsAudioTracks.Insert(0, new TrackDisable()); - return lsAudioTracks; - } + public List GetSubtitleTracks() + { + var lsAudioTracks = MkvFiles.First() + .tracks + .Where(x => x.type == TrackTypeEnum.subtitle) + .ToList(); - public List GetAudioTracks() - { - return this.lsMkvFiles.First() - .tracks - .Where(x => x.type == TrackTypeEnum.audio) - .ToList(); - } + lsAudioTracks.Insert(0, new TrackDisable()); + return lsAudioTracks; + } - public override string ToString() - { - return this.lsMkvFiles.Any() ? this.lsMkvFiles.First().ToString() : "No MKV files."; - } + public List GetAudioTracks() + { + return MkvFiles.First() + .tracks + .Where(x => x.type == TrackTypeEnum.audio) + .ToList(); } -} \ No newline at end of file + + public override string ToString() => + MkvFiles.Any() ? MkvFiles.First().ToString() : "No MKV files."; +} diff --git a/MatroskaLib/MatroskaLib/Types/MkvFile.cs b/MatroskaLib/MatroskaLib/Types/MkvFile.cs index 1542ef3..aa538ab 100644 --- a/MatroskaLib/MatroskaLib/Types/MkvFile.cs +++ b/MatroskaLib/MatroskaLib/Types/MkvFile.cs @@ -1,56 +1,43 @@ -using System.Collections.Generic; using System; +using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; -using MatroskaLib; -namespace MatroskaLib +namespace MatroskaLib.Types; + +public record MkvFile : IComparable { - public class MkvFile : IComparable - { - [JsonIgnore] public string filePath { get; } - public List tracks { get; } - public List seekList { get; } - public int? seekHeadCheckSum { get; } - public int? tracksCheckSum { get; } - public int voidPosition { get; } - public int endPosition { get; } - public int tracksPosition { get; } - public int beginHeaderPosition { get; } + public required string filePath { get; init; } + public required List tracks { get; init; } + public required List seekList { get; init; } + public required int? seekHeadCheckSum { get; init; } + public required int? tracksCheckSum { get; init; } + public required int voidPosition { get; init; } + public required int endPosition { get; init; } + public required int tracksPosition { get; init; } + public required int beginHeaderPosition { get; init; } - public MkvFile(string filePath, List tracks, List seekList, int? seekHeadCheckSum, - int? tracksCheckSum, int voidPosition, - int endPosition, - int tracksPosition, - int beginHeaderPosition) - { - this.filePath = filePath; - this.tracks = tracks; - this.seekList = seekList; - this.seekHeadCheckSum = seekHeadCheckSum; - this.tracksCheckSum = tracksCheckSum; - this.voidPosition = voidPosition; - this.endPosition = endPosition; - this.tracksPosition = tracksPosition; - this.beginHeaderPosition = beginHeaderPosition; - } + public int CompareTo(MkvFile? other) + { + if (other is null) + throw new ArgumentNullException(nameof(other)); - public int CompareTo(MkvFile other) + for (int i = 0; i < tracks.Count; i++) { - for (int i = 0; i < this.tracks.Count; i++) - { - var track = this.tracks[i]; - var trackOther = other.tracks.ElementAtOrDefault(i); - if (trackOther is null || track.number != trackOther.number || track.language != trackOther.language) - return -1; - } - return 0; + var track = tracks[i]; + var trackOther = other.tracks.ElementAtOrDefault(i); + if (trackOther is null || track.number != trackOther.number || track.language != trackOther.language) + return -1; } - public override string ToString() - { - return JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true}); - } + return 0; } + + public override string ToString() => + JsonSerializer.Serialize(this with { filePath = string.Empty }, SourceGeneratedMkvFile.Default.MkvFile); } + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(MkvFile))] +internal partial class SourceGeneratedMkvFile : JsonSerializerContext { } diff --git a/MatroskaLib/MatroskaLib/Types/Seek.cs b/MatroskaLib/MatroskaLib/Types/Seek.cs index 19f1499..19abe44 100644 --- a/MatroskaLib/MatroskaLib/Types/Seek.cs +++ b/MatroskaLib/MatroskaLib/Types/Seek.cs @@ -1,33 +1,28 @@ using System.IO; using NEbml.Core; -namespace MatroskaLib +namespace MatroskaLib.Types; + +public class Seek { - public class Seek - { - private readonly EbmlReader _reader; - - - public ulong seekID { get; private set; } - public ulong seekPosition { get; private set; } - public int seekPositionByteNumber { get; private set; } + private readonly EbmlReader _reader; + public ulong seekId { get; private set; } + public ulong seekPosition { get; private set; } + public int seekPositionByteNumber { get; private set; } - public Seek(EbmlReader reader) + public Seek(EbmlReader reader) => + _reader = reader; + + public void ApplyElement(FileStream fileStream) + { + if (_reader.ElementId.EncodedValue == MatroskaElements.SeekId) { - this._reader = reader; + seekId = _reader.ReadUInt(); } - - public void applyElement(FileStream datastream) + else if (_reader.ElementId.EncodedValue == MatroskaElements.SeekPosition) { - if (this._reader.ElementId.EncodedValue == MatroskaElements.seekID) - { - this.seekID = this._reader.ReadUInt(); - } - else if (this._reader.ElementId.EncodedValue == MatroskaElements.seekPosition) - { - this.seekPositionByteNumber = (int)datastream.Position; - this.seekPosition = this._reader.ReadUInt(); - } + seekPositionByteNumber = (int)fileStream.Position; + seekPosition = _reader.ReadUInt(); } } -} \ No newline at end of file +} diff --git a/MatroskaLib/MatroskaLib/Types/Track.cs b/MatroskaLib/MatroskaLib/Types/Track.cs index 834ead2..d9f36a7 100644 --- a/MatroskaLib/MatroskaLib/Types/Track.cs +++ b/MatroskaLib/MatroskaLib/Types/Track.cs @@ -1,113 +1,105 @@ -using System; -using NEbml.Core; using System.IO; using System.Text.Json.Serialization; +using NEbml.Core; -namespace MatroskaLib +namespace MatroskaLib.Types; + +// https://www.matroska.org/technical/elements.html +public static class TrackElements { - // https://www.matroska.org/technical/elements.html - public class TrackElements - { - public const ulong entry = 0xae; - public const ulong number = 0xd7; - public const ulong type = 0x83; - public const ulong name = 0x536e; - public const ulong flagDefault = 0x88; - public const ulong flagForced = 0x55AA; - public const ulong language = 0x22b59c; - } + public const ulong Entry = 0xae; + public const ulong Number = 0xd7; + public const ulong Type = 0x83; + public const ulong Name = 0x536e; + public const ulong FlagDefault = 0x88; + public const ulong FlagForced = 0x55AA; + public const ulong Language = 0x22b59c; +} - public class MatroskaElements - { - public const ulong seekHead = 0x114D9B74; - public const ulong seekEntry = 0x4DBB; - public const ulong seekID = 0x53AB; - public const ulong seekPosition = 0x53AC; +public static class MatroskaElements +{ + public const ulong SeekHead = 0x114D9B74; + public const ulong SeekEntry = 0x4DBB; + public const ulong SeekId = 0x53AB; + public const ulong SeekPosition = 0x53AC; - public const ulong segmentInfo = 0x1549A966; - public const ulong tracks = 0x1654ae6b; - public const ulong segment = 0x18538067; + public const ulong SegmentInfo = 0x1549A966; + public const ulong Tracks = 0x1654ae6b; + public const ulong Segment = 0x18538067; - public const ulong voidElement = 0xEC; - public const ulong checkSum = 0xBF; - } + public const ulong VoidElement = 0xEC; + public const ulong CheckSum = 0xBF; +} - [JsonConverter(typeof(JsonStringEnumConverter))] - public enum TrackTypeEnum - { - video = 1, - audio = 2, - complex = 3, - logo = 16, - subtitle = 17, - buttons = 18, - control = 32, - metadata = 33 - } - public class Track - { - private EbmlReader _reader { get; } - public int trackLengthByteNumber { get; set; } +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum TrackTypeEnum +{ + video = 1, + audio = 2, + complex = 3, + logo = 16, + subtitle = 17, + buttons = 18, + control = 32, + metadata = 33 +} +public class Track +{ + private EbmlReader _reader { get; } + public int trackLengthByteNumber { get; set; } - public ulong number { get; set; } - public bool flagDefault { get; set; } - public int flagDefaultByteNumber { get; set; } - public bool flagForced { get; set; } - public int flagForcedByteNumber { get; set; } - public int flagTypebytenumber { get; set; } - public TrackTypeEnum type { get; set; } + public ulong number { get; set; } + public bool flagDefault { get; set; } + public int flagDefaultByteNumber { get; set; } + public bool flagForced { get; set; } + public int flagForcedByteNumber { get; set; } + public int flagTypebytenumber { get; set; } + public TrackTypeEnum type { get; set; } - public string? name { get; set; } = ""; - public string language { get; set; } = "eng"; + public string? name { get; set; } = string.Empty; + public string language { get; set; } = "eng"; - public Track(EbmlReader reader) - { - this._reader = reader; - } + public Track(EbmlReader reader) => + _reader = reader; - public void ApplyElement(FileStream datastream) + public void ApplyElement(FileStream fileStream) + { + switch (_reader.ElementId.EncodedValue) { - switch (this._reader.ElementId.EncodedValue) - { - case TrackElements.number: - this.number = this._reader.ReadUInt(); - break; - case TrackElements.name: - this.name = this._reader.ReadUtf(); - break; - case TrackElements.flagForced: - this.flagForcedByteNumber = (int)datastream.Position; - this.flagForced = this._reader.ReadUInt() == 1; - break; - case TrackElements.flagDefault: - this.flagDefaultByteNumber = (int)datastream.Position; - this.flagDefault = this._reader.ReadUInt() == 1; - break; - case TrackElements.language: - this.language = this._reader.ReadUtf(); - break; - case TrackElements.type: - this.flagTypebytenumber = (int)datastream.Position; - this.type = (TrackTypeEnum)this._reader.ReadUInt(); - break; - } + case TrackElements.Number: + number = _reader.ReadUInt(); + break; + case TrackElements.Name: + name = _reader.ReadUtf(); + break; + case TrackElements.FlagForced: + flagForcedByteNumber = (int)fileStream.Position; + flagForced = _reader.ReadUInt() == 1; + break; + case TrackElements.FlagDefault: + flagDefaultByteNumber = (int)fileStream.Position; + flagDefault = _reader.ReadUInt() == 1; + break; + case TrackElements.Language: + language = _reader.ReadUtf(); + break; + case TrackElements.Type: + flagTypebytenumber = (int)fileStream.Position; + type = (TrackTypeEnum)_reader.ReadUInt(); + break; } + } - public override string ToString() - { - return $"{this.number} ({this.language }) default={this.flagDefault}\t forced={this.flagForced}\t {this.name}"; - } + public override string ToString() => + $"{number} ({language}) default={flagDefault}\t forced={flagForced}\t {name}"; - public virtual string ToUiString() - { - return $"({this.language}) {this.name}"; - } - } + public virtual string ToUiString() => + $"({language}) {name}"; +} - public class TrackDisable : Track - { - public TrackDisable() : base(null) { } - public override string ToString() => "Disable"; - public override string ToUiString() => "Disable"; - } +public class TrackDisable : Track +{ + public TrackDisable() : base(null!) { } + public override string ToString() => "Disable"; + public override string ToUiString() => "Disable"; } diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/MkvDefaultTrackChanger.Gtk.csproj b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/MkvDefaultTrackChanger.Gtk.csproj index 5fc80bc..cbb6672 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/MkvDefaultTrackChanger.Gtk.csproj +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/MkvDefaultTrackChanger.Gtk.csproj @@ -2,7 +2,7 @@ WinExe - net5.0 + net8.0 MkvDefaultTrackChanger.GtkSharp @@ -11,7 +11,7 @@ - + diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/Program.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/Program.cs index 0bfeec0..5da44db 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/Program.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Gtk/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using Eto.Forms; namespace MkvDefaultTrackChanger.GtkSharp @@ -12,4 +12,4 @@ public static void Main(string[] args) new Application(platform).Run(new MainForm()); } } -} \ No newline at end of file +} diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Icon.icns b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Icon.icns index 8f385bb..d386dbc 100644 Binary files a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Icon.icns and b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Icon.icns differ diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist index 00f93fd..f603d14 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Info.plist @@ -1,20 +1,18 @@ - - CFBundleName - MkvDefaultTrackChanger - CFBundleIdentifier - com.mikemoolenaar.MkvDefaultTrackChanger - CFBundleShortVersionString - 1.0 - LSMinimumSystemVersion - 10.12 - CFBundleDevelopmentRegion - en - NSHumanReadableCopyright - - CFBundleIconFile - Icon.icns - + + CFBundleName + MkvDefaultTrackChanger + CFBundleIdentifier + com.mikemoolenaar.MkvDefaultTrackChanger + CFBundleShortVersionString + 1.1.0 + LSMinimumSystemVersion + 10.12 + CFBundleDevelopmentRegion + en + CFBundleIconFile + Icon.icns + diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/MkvDefaultTrackChanger.Mac.csproj b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/MkvDefaultTrackChanger.Mac.csproj index f19e3d7..980e9fc 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/MkvDefaultTrackChanger.Mac.csproj +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/MkvDefaultTrackChanger.Mac.csproj @@ -2,19 +2,17 @@ Exe - net5.0 - + net8.0 osx-x64 - MkvDefaultTrackChanger.Mac - + - + - + + - diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Program.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Program.cs index 3541e47..f10dc83 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Program.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.Mac/Program.cs @@ -1,15 +1,14 @@ -using Eto.Forms; using System; +using Eto.Forms; -namespace MkvDefaultTrackChanger.Mac +namespace MkvDefaultTrackChanger.Mac; + +static class MainClass { - class MainClass + [STAThread] + public static void Main(string[] args) { - [STAThread] - public static void Main(string[] args) - { - var platform = new Eto.Mac.Platform(); - new Application(platform).Run(new MainForm()); - } + var platform = new Eto.Mac.Platform(); + new Application(platform).Run(new MainForm()); } -} \ No newline at end of file +} diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/MkvDefaultTrackChanger.WinForms.csproj b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/MkvDefaultTrackChanger.WinForms.csproj index 350301f..a0f304a 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/MkvDefaultTrackChanger.WinForms.csproj +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/MkvDefaultTrackChanger.WinForms.csproj @@ -2,16 +2,22 @@ WinExe - net5.0-Windows + net8.0-Windows MkvDefaultTrackChanger.WinForms + ..\MkvDefaultTrackChanger\logo.ico - + - + - + + + + + + logo.ico + - diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/Program.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/Program.cs index e245985..acd7681 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/Program.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger.WinForms/Program.cs @@ -1,23 +1,22 @@ -using Eto.Forms; using System; +using Eto.Forms; using swf = System.Windows.Forms; -namespace MkvDefaultTrackChanger.WinForms -{ +namespace MkvDefaultTrackChanger.WinForms; - class MainClass +static class MainClass +{ + [STAThread] + public static void Main(string[] args) { - [STAThread] - public static void Main(string[] args) + var platform = new Eto.WinForms.Platform(); + Eto.Style.Add(null, control => { - var platform = new Eto.WinForms.Platform(); - Eto.Style.Add(null, control => { - var dropdown = control.ControlObject as swf.ComboBox; - dropdown.DrawMode = swf.DrawMode.Normal; - }); - - new Application(platform).Run(new MainForm()); - - } + var dropdown = control.ControlObject as swf.ComboBox; + dropdown.DrawMode = swf.DrawMode.Normal; + }); + + new Application(platform).Run(new MainForm()); + } } diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/CustomExtensions.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/CustomExtensions.cs index 98c0c73..0df9cc1 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/CustomExtensions.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/CustomExtensions.cs @@ -1,19 +1,18 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Eto.Forms; -using MatroskaLib; +using MatroskaLib.Types; -namespace MkvDefaultTrackChanger +namespace MkvDefaultTrackChanger; + +public static class CustomExtensions { - public static class CustomExtensions + public static IEnumerable ToEnoListItems(this IEnumerable ls) { - public static IEnumerable ToEnoListItems(this List ls) + return ls.Select(track => new ListItem { - return ls.Select((Track track) => new ListItem() - { - Key = track.number.ToString(), - Text = track.ToUiString() - }); - } + Key = track.number.ToString(), + Text = track.ToUiString() + }); } -} \ No newline at end of file +} diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto index 7ce829f..670a827 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto @@ -3,8 +3,8 @@ xmlns="http://schema.picoe.ca/eto.forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MkvDefaultTrackChanger" - MinimumSize="370,500" - Size="370,500" + MinimumSize="370,530" + Size="370,530" Padding="10" > diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto.cs index 4f188ed..e3da98e 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/ErrorForm.xeto.cs @@ -1,47 +1,47 @@ using System; using System.Runtime.InteropServices; using System.Text; -using System.Text.Json; using Eto.Drawing; using Eto.Forms; using Eto.Serialization.Xaml; -namespace MkvDefaultTrackChanger +namespace MkvDefaultTrackChanger; + +public class ErrorForm : Form { - public class ErrorForm : Form + TextArea txaExceptionMessage; + private Label lblTitle; + + public ErrorForm(Exception ex, string? mkvFileInfo, Icon icon) + { + Icon = icon; + XamlReader.Load(this); + txaExceptionMessage!.Text = new StringBuilder() + .Append(GetPlatformInfo()) + .AppendLine() + .AppendLine(mkvFileInfo ?? "No mkv file info.") + .AppendLine() + .AppendLine(ex.ToString()) + .ToString(); + lblTitle!.Font = new Font(lblTitle.Font.Family, 20); + } + + private string GetPlatformInfo() { - TextArea txaExceptionMessage; - private Label lblTitle; - public ErrorForm(Exception ex, string? mkvFileInfo) - { - XamlReader.Load(this); - txaExceptionMessage.Text = new StringBuilder() - .Append(GetPlatformInfo()) - .AppendLine() - .AppendLine(mkvFileInfo ?? "No mkv file info.") - .AppendLine() - .AppendLine(ex.ToString()) - .ToString(); - lblTitle.Font = new Font(lblTitle.Font.Family, 20); - } + return new StringBuilder() + .AppendLine($"Framework: {RuntimeInformation.FrameworkDescription}") + .AppendLine($"RuntimeIdentifier: {RuntimeInformation.RuntimeIdentifier}") + .AppendLine($"OS: {RuntimeInformation.OSDescription}") + .ToString(); + } - private string GetPlatformInfo() - { - return new StringBuilder() - .AppendLine($"Framework: {RuntimeInformation.FrameworkDescription}") - .AppendLine($"RuntimeIdentifier: {RuntimeInformation.RuntimeIdentifier}") - .AppendLine($"OS: {RuntimeInformation.OSDescription}") - .ToString(); - } + protected void BtnCloseClicked(object sender, EventArgs e) + { + Close(); + } - protected void BtnCloseClicked(object sender, EventArgs e) - { - this.Close(); - } - - protected void BtnCreateIssueClicked(object sender, EventArgs e) - { - Application.Instance.Open("https://github.com/MikeMoolenaar/MkvDefaultTrackChanger/issues/new"); - } + protected void BtnCreateIssueClicked(object sender, EventArgs e) + { + Application.Instance.Open("https://github.com/MikeMoolenaar/MkvDefaultTrackChanger/issues/new"); } -} \ No newline at end of file +} diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto index bf2bd20..ee3febb 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto @@ -5,23 +5,27 @@ Title="MkvDefaultTrackChanger" MinimumSize="300,450" Size="300, 450" - Padding="10" - > - - - - - - - - - - - - - - - + Padding="10"> + + + + + + + + + + + + + + + + + + + + diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs index a821495..381c32f 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MainForm.xeto.cs @@ -1,129 +1,149 @@ -using Eto.Forms; -using Eto.Serialization.Xaml; using System; using System.Collections.Generic; -using MatroskaLib; -using System.Linq; using System.IO; +using System.Linq; +using Eto.Drawing; +using Eto.Forms; +using Eto.Serialization.Xaml; +using MatroskaLib; +using MatroskaLib.Types; + +namespace MkvDefaultTrackChanger; -namespace MkvDefaultTrackChanger +public class MainForm : Form { - public class MainForm : Form + Label lblFilesSelected; + DropDown dropdownAudio; + DropDown dropdownSubtitles; + Button btnApply; + Label lblStatus; + + MkvFilesContainer mkvContainer; + OpenFileDialog fileDialog; + private (string audio, string subtitles)? appliedConfig; + + public MainForm() { - Label lblFilesSelected; - DropDown dropdownAudio; - DropDown dropdownSubtitles; - Button btnApply; - Label lblStatus; - - MkvFilesContainer mkvContainer; - OpenFileDialog fileDialog; + Icon = Icon.FromResource("MkvDefaultTrackChanger.logo.ico"); + XamlReader.Load(this); - public MainForm() + fileDialog = new OpenFileDialog(); + fileDialog.Filters.Add(new FileFilter("MKV files", "*.mkv")); + fileDialog.MultiSelect = true; + } + + private void BtnBrowseFilesClick(object sender, EventArgs e) + { + var dialogResult = fileDialog.ShowDialog(this); + if (dialogResult != DialogResult.Ok) return; + + try { - XamlReader.Load(this); + LoadFiles(); - fileDialog = new OpenFileDialog(); - fileDialog.Filters.Add(new FileFilter("MKV files", "*.mkv")); - fileDialog.MultiSelect = true; + btnApply.Enabled = true; + lblStatus.Text = string.Empty; + appliedConfig = null; + } + catch (Exception exception) + { + HandleException(exception); } + } + + private void LoadFiles() + { + string[] filePaths = fileDialog.Filenames.ToArray(); - private void BtnBrowseFilesClick(object sender, EventArgs e) + mkvContainer = new MkvFilesContainer(filePaths); + if (mkvContainer.MkFilesRejected.Count > 0) { - var dialogResult = fileDialog.ShowDialog(this); - - if (dialogResult == DialogResult.Ok) + string rejectedFiles = Environment.NewLine + Environment.NewLine; + mkvContainer.MkFilesRejected.ForEach((x) => { - try - { - this.LoadFiles(); - } - catch (Exception exception) - { - HandleException(exception); - } - } + rejectedFiles += Path.GetFileName(x.filePath) + Environment.NewLine + Environment.NewLine; + }); + MessageBox.Show("The following files were rejected: " + rejectedFiles, MessageBoxType.Warning); } + + var lsSubtitleTracks = mkvContainer.GetSubtitleTracks(); + var lsAudioTracks = mkvContainer.GetAudioTracks(); + + FillDropdown(dropdownSubtitles, lsSubtitleTracks); + FillDropdown(dropdownAudio, lsAudioTracks); - private void LoadFiles() - { - string[] filePaths = fileDialog.Filenames.ToArray(); - this.lblFilesSelected.Text = $"{filePaths.Length} files selected"; + string files = filePaths.Length == 1 ? "file" : "files"; + lblFilesSelected.Text = $"{filePaths.Length} {files} selected"; + } + + private void FillDropdown(DropDown dropDown, List lsTracks) + { + dropDown.Items.Clear(); + dropDown.Items.AddRange(lsTracks.ToEnoListItems()); + dropDown.SelectedKey = lsTracks + .FirstOrDefault(x => x.flagDefault || x.flagForced) + ?.number.ToString(); + dropDown.Enabled = true; + if (dropDown.SelectedKey is null) + dropDown.SelectedKey = lsTracks[0].number.ToString(); + } - this.mkvContainer = new MkvFilesContainer(filePaths); - if (this.mkvContainer.lsMkFilesRejected.Count > 0) + protected void BtnApplyClicked(object sender, EventArgs e) + { + try + { + btnApply.Enabled = false; + mkvContainer.WriteChanges(track => { - string rejectedFiles = Environment.NewLine + Environment.NewLine; - this.mkvContainer.lsMkFilesRejected.ForEach((x) => - { - rejectedFiles += Path.GetFileName(x.filePath) + Environment.NewLine + Environment.NewLine; - }); - MessageBox.Show("The following files were rejected: " + rejectedFiles, MessageBoxType.Warning); - } - - var lsSubtitleTracks = this.mkvContainer.GetSubtitleTracks(); - var lsAudioTracks = this.mkvContainer.GetAudioTracks(); - - this.FillDropdown(this.dropdownSubtitles, lsSubtitleTracks); - this.FillDropdown(this.dropdownAudio, lsAudioTracks); - - this.btnApply.Enabled = true; - this.lblStatus.Text = ""; + string key = track.number.ToString(); + track.flagDefault = dropdownAudio.SelectedKey == key || dropdownSubtitles.SelectedKey == key; + }); + + LoadFiles(); + + appliedConfig = (dropdownAudio.SelectedKey, dropdownSubtitles.SelectedKey); + lblStatus.Text = "Done!"; } - - private void FillDropdown(DropDown dropDown, List lsTracks) + catch (Exception exception) { - dropDown.Items.Clear(); - dropDown.Items.AddRange(lsTracks.ToEnoListItems()); - dropDown.SelectedKey = lsTracks - .FirstOrDefault(x => x.flagDefault || x.flagForced) - ?.number.ToString(); - if (dropDown.SelectedKey is null) - dropDown.SelectedKey = lsTracks[0].number.ToString(); + HandleException(exception); + btnApply.Enabled = true; } + } - protected void BtnApplyClicked(object sender, EventArgs e) + private void OnDropdownSelectionChanged(object? sender, EventArgs e) + { + if (appliedConfig != (dropdownAudio.SelectedKey, dropdownSubtitles.SelectedKey)) { - try - { - this.btnApply.Enabled = false; - this.mkvContainer.WriteChanges((Track t) => { t.flagDefault = this.IsSelectedTrack(t); }); - this.btnApply.Enabled = true; - this.LoadFiles(); - this.lblStatus.Text = "Done!"; - } - catch (Exception exception) - { - HandleException(exception); - } + btnApply.Enabled = true; + lblStatus.Text = string.Empty; } - - private bool IsSelectedTrack(Track t) + else { - string key = t.number.ToString(); - return this.dropdownAudio.SelectedKey == key || this.dropdownSubtitles.SelectedKey == key; + btnApply.Enabled = false; + lblStatus.Text = "Done!"; } + + } - protected void HandleAbout(object sender, EventArgs e) + protected void HandleAbout(object sender, EventArgs e) + { + var aboutDialog = new AboutDialog { - var aboutDialog = new AboutDialog() - { - // TODO logo - // Logo = - Website = new Uri("https://github.com/MikeMoolenaar/MkvDefaultTrackChanger"), - WebsiteLabel = "Github page", - ProgramDescription = - @"MkvDefaultTrackChanger is a small application to change the default subtitle and audio tracks in MKV video files. ", - License = @"Copyright (C) 2021 Mike Moolenaar + Logo = Icon.WithSize(100, 200), + Website = new Uri("https://github.com/MikeMoolenaar/MkvDefaultTrackChanger"), + WebsiteLabel = "Github", + ProgramDescription = + "MkvDefaultTrackChanger is a small application to change the default subtitle and audio tracks in MKV video files. ", + License = @"Copyright (C) 2021 Mike Moolenaar MkvDefaultTrackChanger is licensed under the terms of the GNU General Public License version 3. A copy of this license can be obtained from .", - Developers = new[] {"Mike Moolenaar"} - }; - aboutDialog.ShowDialog(this); - } + Developers = ["Mike Moolenaar"] + }; + aboutDialog.ShowDialog(this); + } - private void HandleException(Exception ex) - { - new ErrorForm(ex, this.mkvContainer?.ToString()).Show(); - } + private void HandleException(Exception ex) + { + new ErrorForm(ex, mkvContainer?.ToString(), Icon).Show(); } -} \ No newline at end of file +} diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MkvDefaultTrackChanger.csproj b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MkvDefaultTrackChanger.csproj index 162858a..9d35933 100644 --- a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MkvDefaultTrackChanger.csproj +++ b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/MkvDefaultTrackChanger.csproj @@ -1,16 +1,24 @@  - net5.0 + net8.0 + CS8618;CS0649 + true + enable - - + + - + + + + + + \ No newline at end of file diff --git a/MkvDefaultTrackChanger/MkvDefaultTrackChanger/logo.ico b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/logo.ico new file mode 100644 index 0000000..bd13665 Binary files /dev/null and b/MkvDefaultTrackChanger/MkvDefaultTrackChanger/logo.ico differ diff --git a/MkvReadCrawler/MkvReadCrawler.csproj b/MkvReadCrawler/MkvReadCrawler.csproj index 43797b1..78a6861 100644 --- a/MkvReadCrawler/MkvReadCrawler.csproj +++ b/MkvReadCrawler/MkvReadCrawler.csproj @@ -1,12 +1,15 @@ - - Exe - net5.0 - + + Exe + net8.0 + - - - + + + + + + diff --git a/MkvReadCrawler/Program.cs b/MkvReadCrawler/Program.cs index 2b74dbd..cb0110e 100644 --- a/MkvReadCrawler/Program.cs +++ b/MkvReadCrawler/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Text; using System.Threading.Tasks; @@ -6,41 +6,41 @@ // Program to crawl through all MKV files and get the track count. // Crawls through specified folder and its subfolders. -namespace MkvReadCrawler +namespace MkvReadCrawler; + +static class Program { - class Program + static void Main(string[] args) { - static void Main(string[] args) + string path = args[0]; + + Console.WriteLine("Processing..."); + string[] mkvFiles = Directory.GetFiles(path, "*.mkv", SearchOption.AllDirectories); + var mainStringBuilder = new StringBuilder(); + + int x = 0; + Parallel.For(0, mkvFiles.Length, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, i => { - string path = args[0]; - - Console.WriteLine("Processing..."); - string[] mkvFiles = Directory.GetFiles(path, "*.mkv", SearchOption.AllDirectories); - var mainStringBuilder = new StringBuilder(); + var mkvFile = mkvFiles[i]; + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine(Path.GetFileName(mkvFile)); + + try + { + var lsFiles = MatroskaReader.ReadMkvFiles(new[] { mkvFile }); + stringBuilder.AppendLine("b" + lsFiles[0].tracks.Count); + } + catch (Exception e) + { + stringBuilder.AppendLine(e.GetType().ToString()); + } + stringBuilder.AppendLine(); - int x = 0; - Parallel.For(0, mkvFiles.Length, new ParallelOptions(){MaxDegreeOfParallelism = 4}, i => { - var mkvFile = mkvFiles[i]; - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine(Path.GetFileName(mkvFile)); + mainStringBuilder.Append(stringBuilder.ToString()); + Console.WriteLine($"{x++}/{mkvFiles.Length} {Path.GetFileName(mkvFile)}"); + }); - try - { - var lsFiles = MatroskaIO.ReadMkvFiles(new[] {mkvFile}); - stringBuilder.AppendLine("b" + lsFiles[0].tracks.Count); - } - catch (Exception e) - { - stringBuilder.AppendLine(e.GetType().ToString()); - } - stringBuilder.AppendLine(); - - mainStringBuilder.Append(stringBuilder.ToString()); - Console.WriteLine($"{x++}/{mkvFiles.Length} {Path.GetFileName(mkvFile)}"); - }); - - File.WriteAllText("OutputkvDefault.txt", mainStringBuilder.ToString()); - Console.WriteLine("Done!"); - } + File.WriteAllText("OutputkvDefault.txt", mainStringBuilder.ToString()); + Console.WriteLine("Done!"); } -} \ No newline at end of file +}