Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
$coverageFiles = Get-ChildItem -Path . -Recurse -Filter coverage.cobertura.xml
$coveragePaths = $coverageFiles | ForEach-Object { $_.FullName }
$reportsArg = '"' + ($coveragePaths -join ";") + '"'
dotnet tool run reportgenerator -reports:$reportsArg "-targetdir:coverage-report" "-filefilters:-*.g.cs" "-reporttypes:$reportType"
dotnet tool run reportgenerator -reports:$reportsArg "-targetdir:coverage-report" "-filefilters:-*.g.cs" "-reporttypes:Html"

- name: Upload Coverage report
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions AtemSharp.Demo/AtemSharp.Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\AtemSharp.Json\AtemSharp.Json.csproj" />
<ProjectReference Include="..\AtemSharp\AtemSharp.csproj" />
</ItemGroup>

Expand Down
4 changes: 3 additions & 1 deletion AtemSharp.Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System.Diagnostics;
using AtemSharp;
using AtemSharp.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

Expand All @@ -28,7 +29,8 @@
// Serialize state to JSON
var stateJson = JsonConvert.SerializeObject(atem, Formatting.Indented,
new JsonSerializerSettings
{ Converters = [new StringEnumConverter()], TypeNameHandling = TypeNameHandling.Auto });
{ Converters = [new StringEnumConverter()], TypeNameHandling = TypeNameHandling.Auto }
.WithAtemStateSupport());

// Write state to file
await File.WriteAllTextAsync("state.json", stateJson);
Expand Down
34 changes: 34 additions & 0 deletions AtemSharp.Json/AtemSharp.Json.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

<PropertyGroup>
<PackageId>AtemSharp.Json</PackageId>
<Authors>Markus Palcer</Authors>
<Description>Extensions for serializing the AtemSharp state to JSON using Newtonsoft.Json</Description>
<Copyright>MIT</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/MarkusPalcer/AtemSharp</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>atem;blackmagic;switcher;video;json</PackageTags>
<ImplicitUsings>true</ImplicitUsings>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AtemSharp\AtemSharp.csproj" />
</ItemGroup>

<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
</Project>
57 changes: 57 additions & 0 deletions AtemSharp.Json/Converters/ItemCollectionConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Numerics;
using System.Reflection;
using AtemSharp.Types;
using Newtonsoft.Json;

namespace AtemSharp.Json.Converters;

public class ItemCollectionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value is null)
{
serializer.Serialize(writer, null);
return;
}

var objectType = value.GetType();
var keyType = objectType.GetGenericArguments()[0];
var valueType = objectType.GetGenericArguments()[1];

var writeDelegate = typeof(ItemCollectionConverter).GetMethod(nameof(Write), BindingFlags.Static | BindingFlags.NonPublic)!
.MakeGenericMethod(keyType, valueType)
.CreateDelegate<WriteMethod>();

writeDelegate(writer, value, serializer);
}

delegate void WriteMethod(JsonWriter writer, object? value, JsonSerializer serializer);

private static void Write<TKey, TValue>(JsonWriter writer, object? value, JsonSerializer serializer)
where TKey : IIncrementOperators<TKey>,
IComparisonOperators<TKey, TKey, bool>,
IConvertible
{
if (value is not ItemCollection<TKey, TValue> itemCollection)
{
serializer.Serialize(writer, null);
return;
}

var items = itemCollection.AsReadOnly();
var dictionary = items.ToDictionary(x => $"{x.Key}", x => (object?)x.Value);

serializer.Serialize(writer, dictionary);
}

public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
throw new InvalidOperationException("Parsing JSON is not supported.");
}

public override bool CanConvert(Type objectType)
{
return objectType.IsConstructedGenericType && objectType.GetGenericTypeDefinition() == typeof(ItemCollection<,>);
}
}
39 changes: 39 additions & 0 deletions AtemSharp.Json/Converters/MacroSystemConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using AtemSharp.State.Macro;
using Newtonsoft.Json;

namespace AtemSharp.Json.Converters;

public class MacroSystemConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(MacroSystem);

public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
throw new InvalidOperationException("Parsing JSON is not supported.");
}

public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value is null)
{
writer.WriteNull();
}

if (value is not MacroSystem macroSystem)
{
throw new InvalidOperationException("Tried to serialize a non MacroSystem with MacroSystemConverter");
}

var serialized = new Dictionary<string, object>();

foreach (var (key, macro) in macroSystem.AsReadOnly())
{
serialized[key.ToString()] = macro;
}

serialized["Player"] = macroSystem.Player;
serialized["Recorder"] = macroSystem.Recorder;

serializer.Serialize(writer, serialized);
}
}
19 changes: 19 additions & 0 deletions AtemSharp.Json/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using AtemSharp.Json.Converters;
using Newtonsoft.Json;

namespace AtemSharp.Json;

public static class Extensions
{
/// <summary>
/// Adds the converters needed to properly convert the ATEM state to JSON to the given
/// <see cref="JsonSerializerSettings"/>
/// </summary>
public static JsonSerializerSettings WithAtemStateSupport(this JsonSerializerSettings options)
{
options.Converters.Add(new MacroSystemConverter());
options.Converters.Add(new ItemCollectionConverter());

return options;
}
}
29 changes: 29 additions & 0 deletions AtemSharp.Json/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# AtemSharp.Json

This library extends [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json) for use with
[AtemSharp](https://www.nuget.org/packages/AtemSharp/).

**Note:** It does not support deserialization

## Usage:

### Add package

Add the package to your project with the following command

```powershell
dotnet add package AtemSharp.Json
```

make sure to use the same version as the one of the `AtemSharp` package.

### Use package

Where you create your JsonSerializerSettings, just append the following:

```C#
var settings = new JsonSerializerSettings { ... }.WithAtemStateSupport();
```

This helper method adds the converters needed to properly serialize the state.

2 changes: 1 addition & 1 deletion AtemSharp.Tests/AtemSharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<PackageReference Include="JetBrains.Annotations" Version="2025.2.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="4.4.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.4.0"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
public class AudioMixerHeadphonesCommandTests : SerializedCommandTestBase<AudioMixerHeadphonesCommand,
public class AudioMixerHeadphonesCommandTests : TypeScriptLibrarySerializedCommandTestBase<AudioMixerHeadphonesCommand,
AudioMixerHeadphonesCommandTests.CommandData>
{
protected override Range[] GetFloatingPointByteRanges()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
internal class AudioMixerHeadphonesUpdateCommandTests : DeserializedCommandTestBase<AudioMixerHeadphonesUpdateCommand,
internal class AudioMixerHeadphonesUpdateCommandTests : TypeScriptLibraryDeserializedCommandTestBase<AudioMixerHeadphonesUpdateCommand,
AudioMixerHeadphonesUpdateCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
public class AudioMixerInputCommandTests : SerializedCommandTestBase<AudioMixerInputCommand,
public class AudioMixerInputCommandTests : TypeScriptLibrarySerializedCommandTestBase<AudioMixerInputCommand,
AudioMixerInputCommandTests.CommandData>
{
protected override Range[] GetFloatingPointByteRanges()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
internal class AudioMixerInputUpdateCommandTests : DeserializedCommandTestBase<AudioMixerInputUpdateCommand,
internal class AudioMixerInputUpdateCommandTests : TypeScriptLibraryDeserializedCommandTestBase<AudioMixerInputUpdateCommand,
AudioMixerInputUpdateCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
internal class AudioMixerInputUpdateV8CommandTests : DeserializedCommandTestBase<AudioMixerInputUpdateV8Command,
internal class AudioMixerInputUpdateV8CommandTests : TypeScriptLibraryDeserializedCommandTestBase<AudioMixerInputUpdateV8Command,
AudioMixerInputUpdateV8CommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
public class AudioMixerMasterCommandTests : SerializedCommandTestBase<AudioMixerMasterCommand,
public class AudioMixerMasterCommandTests : TypeScriptLibrarySerializedCommandTestBase<AudioMixerMasterCommand,
AudioMixerMasterCommandTests.CommandData>
{
protected override Range[] GetFloatingPointByteRanges()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
internal class AudioMixerMasterUpdateCommandTests : DeserializedCommandTestBase<AudioMixerMasterUpdateCommand,
internal class AudioMixerMasterUpdateCommandTests : TypeScriptLibraryDeserializedCommandTestBase<AudioMixerMasterUpdateCommand,
AudioMixerMasterUpdateCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
public class AudioMixerMonitorCommandTests : SerializedCommandTestBase<AudioMixerMonitorCommand,
public class AudioMixerMonitorCommandTests : TypeScriptLibrarySerializedCommandTestBase<AudioMixerMonitorCommand,
AudioMixerMonitorCommandTests.CommandData>
{
protected override Range[] GetFloatingPointByteRanges()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
internal class AudioMixerMonitorUpdateCommandTests : DeserializedCommandTestBase<AudioMixerMonitorUpdateCommand,
internal class AudioMixerMonitorUpdateCommandTests : TypeScriptLibraryDeserializedCommandTestBase<AudioMixerMonitorUpdateCommand,
AudioMixerMonitorUpdateCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
public class AudioMixerPropertiesCommandTests : SerializedCommandTestBase<AudioMixerPropertiesCommand,
public class AudioMixerPropertiesCommandTests : TypeScriptLibrarySerializedCommandTestBase<AudioMixerPropertiesCommand,
AudioMixerPropertiesCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
internal class AudioMixerPropertiesUpdateCommandTests : DeserializedCommandTestBase<AudioMixerPropertiesUpdateCommand,
internal class AudioMixerPropertiesUpdateCommandTests : TypeScriptLibraryDeserializedCommandTestBase<AudioMixerPropertiesUpdateCommand,
AudioMixerPropertiesUpdateCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.Audio;

[TestFixture]
public class AudioMixerResetPeaksCommandTests : SerializedCommandTestBase<AudioMixerResetPeaksCommand,
public class AudioMixerResetPeaksCommandTests : TypeScriptLibrarySerializedCommandTestBase<AudioMixerResetPeaksCommand,
AudioMixerResetPeaksCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace AtemSharp.Tests.Commands.ColorGenerators;

public class ColorGeneratorCommandTests : SerializedCommandTestBase<ColorGeneratorCommand, ColorGeneratorCommandTests.CommandData>
public class ColorGeneratorCommandTests : TypeScriptLibrarySerializedCommandTestBase<ColorGeneratorCommand, ColorGeneratorCommandTests.CommandData>
{
protected override Range[] GetFloatingPointByteRanges() =>
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace AtemSharp.Tests.Commands.ColorGenerators;

internal class ColorGeneratorUpdateCommandTests : DeserializedCommandTestBase<ColorGeneratorUpdateCommand,
internal class ColorGeneratorUpdateCommandTests : TypeScriptLibraryDeserializedCommandTestBase<ColorGeneratorUpdateCommand,
ColorGeneratorUpdateCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace AtemSharp.Tests.Commands.DataTransfer;

[TestFixture]
public class DataTransferAckCommandTests : SerializedCommandTestBase<DataTransferAckCommand,
public class DataTransferAckCommandTests : TypeScriptLibrarySerializedCommandTestBase<DataTransferAckCommand,
DataTransferAckCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AtemSharp.Tests.Commands.DataTransfer;

[TestFixture]
internal class DataTransferCompleteCommandTests : DeserializedCommandTestBase<DataTransferCompleteCommand,
internal class DataTransferCompleteCommandTests : TypeScriptLibraryDeserializedCommandTestBase<DataTransferCompleteCommand,
DataTransferCompleteCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace AtemSharp.Tests.Commands.DataTransfer;
/// Test deserialization functionality of DataTransferDataCommand using data-driven tests
/// </summary>
[TestFixture]
internal class DataTransferDataReceivedCommandTests : DeserializedCommandTestBase<DataTransferDataReceivedCommand,
internal class DataTransferDataReceivedCommandTests : TypeScriptLibraryDeserializedCommandTestBase<DataTransferDataReceivedCommand,
DataTransferDataReceivedCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace AtemSharp.Tests.Commands.DataTransfer;

[TestFixture]
public class DataTransferDataSendCommandTests : SerializedCommandTestBase<DataTransferDataSendCommand,
public class DataTransferDataSendCommandTests : TypeScriptLibrarySerializedCommandTestBase<DataTransferDataSendCommand,
DataTransferDataSendCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace AtemSharp.Tests.Commands.DataTransfer;

[TestFixture]
public class DataTransferDownloadRequestCommandTests : SerializedCommandTestBase<DataTransferDownloadRequestCommand,
public class DataTransferDownloadRequestCommandTests : TypeScriptLibrarySerializedCommandTestBase<DataTransferDownloadRequestCommand,
DataTransferDownloadRequestCommandTests.CommandData>
{
public class CommandData : CommandDataBase
Expand Down
Loading
Loading