diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6b9375a..9f11a41 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -36,10 +36,10 @@ jobs:
with:
fetch-depth: 0 # Required for MinVer to determine the version from Git history
- - name: Setup .NET 9 SDK
+ - name: Setup .NET 10 SDK
uses: actions/setup-dotnet@v4
with:
- dotnet-version: '9.x' # Use the specific .NET 9 SDK
+ dotnet-version: '10.x'
- name: Restore dependencies
run: dotnet restore ObsWebSocket.sln
@@ -47,6 +47,26 @@ jobs:
- name: Build Solution
run: dotnet build ObsWebSocket.sln --configuration Release --no-restore
+ - name: Restore Example for AOT RID graph
+ run: >
+ dotnet restore ObsWebSocket.Example/ObsWebSocket.Example.csproj
+ -p:TargetFramework=net10.0
+ --runtime linux-x64
+ -p:SelfContained=true
+ -p:PublishAot=true
+
+ - name: Native AOT Smoke Publish (ObsWebSocket.Example)
+ run: >
+ dotnet publish ObsWebSocket.Example/ObsWebSocket.Example.csproj
+ --configuration Release
+ --framework net10.0
+ --runtime linux-x64
+ --self-contained true
+ /p:PublishAot=true
+ /p:ILLinkTreatWarningsAsErrors=true
+ /p:IlcTreatWarningsAsErrors=true
+ /p:WarningsNotAsErrors=IL2104%3BIL3053
+
- name: Run Unit Tests
# Run tests specifically for the ObsWebSocket.Tests project
# Exclude integration tests which require a live OBS instance
diff --git a/ObsWebSocket.Codegen.Tasks/GenerateObsWebSocketSourcesTask.cs b/ObsWebSocket.Codegen.Tasks/GenerateObsWebSocketSourcesTask.cs
new file mode 100644
index 0000000..64861e1
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/GenerateObsWebSocketSourcesTask.cs
@@ -0,0 +1,31 @@
+using Microsoft.Build.Framework;
+
+namespace ObsWebSocket.Codegen.Tasks;
+
+public sealed class GenerateObsWebSocketSourcesTask : Microsoft.Build.Utilities.Task
+{
+ [Required]
+ public string ProtocolPath { get; set; } = string.Empty;
+
+ [Required]
+ public string OutputDirectory { get; set; } = string.Empty;
+
+ public bool DownloadIfMissing { get; set; }
+
+ public override bool Execute()
+ {
+ int exitCode = ProtocolCodegenRunner.GenerateAsync(
+ protocolPath: ProtocolPath,
+ outputDirectory: OutputDirectory,
+ downloadIfMissing: DownloadIfMissing,
+ cancellationToken: CancellationToken.None,
+ logInfo: message => Log.LogMessage(MessageImportance.High, message),
+ logWarning: message => Log.LogWarning(message),
+ logError: message => Log.LogError(message)
+ )
+ .GetAwaiter()
+ .GetResult();
+
+ return exitCode == 0 && !Log.HasLoggedErrors;
+ }
+}
diff --git a/ObsWebSocket.SourceGenerators/Diagnostics.cs b/ObsWebSocket.Codegen.Tasks/Generation/Diagnostics.cs
similarity index 95%
rename from ObsWebSocket.SourceGenerators/Diagnostics.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/Diagnostics.cs
index b06af61..cfc6549 100644
--- a/ObsWebSocket.SourceGenerators/Diagnostics.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Diagnostics.cs
@@ -1,16 +1,10 @@
-using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Contains DiagnosticDescriptor constants for reporting errors and warnings during source generation.
///
-[SuppressMessage(
- "StyleCop.CSharp.OrderingRules",
- "SA1202:Elements must be ordered by access",
- Justification = "Constants grouped logically."
-)]
internal static class Diagnostics
{
private const string Category = "ObsWebSocketGenerator";
diff --git a/ObsWebSocket.SourceGenerators/Emitter.DtoGeneration.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.DtoGeneration.cs
similarity index 99%
rename from ObsWebSocket.SourceGenerators/Emitter.DtoGeneration.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/Emitter.DtoGeneration.cs
index e02aa31..6b9d948 100644
--- a/ObsWebSocket.SourceGenerators/Emitter.DtoGeneration.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.DtoGeneration.cs
@@ -1,9 +1,9 @@
-using System.Collections.Concurrent;
+using System.Collections.Concurrent;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Contains logic for generating Data Transfer Object (DTO) records, including handling nested records.
@@ -374,6 +374,7 @@ ProtocolDefinition protocol
mainBuilder.AppendLine("using System.Text.Json;");
mainBuilder.AppendLine("using System.Text.Json.Serialization;");
mainBuilder.AppendLine("using System.Diagnostics.CodeAnalysis;");
+ mainBuilder.AppendLine("using MessagePack;");
mainBuilder.AppendLine($"using {GeneratedCommonNamespace};");
if (!isNestedType)
{
@@ -445,6 +446,7 @@ ProtocolDefinition protocol
mainBuilder.AppendLine("#pragma warning disable CS8618");
// --- Record Definition Start ---
+ mainBuilder.AppendLine("[MessagePackObject]");
mainBuilder.AppendLine($"public sealed partial record {recordName}");
mainBuilder.AppendLine("{");
List propertyInfos = [];
@@ -545,6 +547,7 @@ ProtocolDefinition protocol
}
mainBuilder.AppendLine($" [JsonPropertyName(\"{originalName}\")]");
+ mainBuilder.AppendLine($" [Key(\"{originalName}\")]");
mainBuilder.Append(" public ");
if (isConsideredRequired)
{
diff --git a/ObsWebSocket.SourceGenerators/Emitter.Helpers.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.Helpers.cs
similarity index 99%
rename from ObsWebSocket.SourceGenerators/Emitter.Helpers.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/Emitter.Helpers.cs
index 8475b64..d6f643b 100644
--- a/ObsWebSocket.SourceGenerators/Emitter.Helpers.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.Helpers.cs
@@ -1,9 +1,9 @@
-using System.Text;
+using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Contains helper methods, constants, and internal data structures for the Emitter class.
@@ -238,7 +238,7 @@ string parentDtoName
// Map specifically named 'Object' field to Stub record
// Use the fully qualified name to avoid potential namespace conflicts
return ($"{GeneratedCommonNamespace}.SceneItemTransformStub?", false);
- // Add other specific 'Object' mappings here if needed in the future
+ // Add other specific 'Object' mappings here if needed in the future
}
// If not handled above, it falls through to the general 'Object'/'Any' handling below
}
diff --git a/ObsWebSocket.SourceGenerators/Emitter.Hierarchy.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.Hierarchy.cs
similarity index 98%
rename from ObsWebSocket.SourceGenerators/Emitter.Hierarchy.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/Emitter.Hierarchy.cs
index 1717938..df97d58 100644
--- a/ObsWebSocket.SourceGenerators/Emitter.Hierarchy.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.Hierarchy.cs
@@ -1,6 +1,6 @@
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Contains logic for building the object hierarchy from dot-notated field definitions.
@@ -88,7 +88,7 @@ SourceProductionContext context
currentNode = newNode;
}
}
- NextFieldPass1:
+ NextFieldPass1:
;
}
diff --git a/ObsWebSocket.Codegen.Tasks/Generation/Emitter.JsonContext.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.JsonContext.cs
new file mode 100644
index 0000000..3568b9e
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.JsonContext.cs
@@ -0,0 +1,136 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ObsWebSocket.Codegen.Tasks.Generation;
+
+///
+/// Contains emitter logic for generating a System.Text.Json source generation context.
+///
+internal static partial class Emitter
+{
+ ///
+ /// Generates a JsonSerializerContext containing all fixed and generated protocol types.
+ ///
+ public static void GenerateJsonSerializerContext(
+ SourceProductionContext context,
+ ProtocolDefinition protocol
+ )
+ {
+ try
+ {
+ StringBuilder builder = BuildSourceHeader("// Serialization Context: ObsWebSocketJsonContext");
+
+ _ = builder.AppendLine("using System.Collections.Generic;");
+ _ = builder.AppendLine("using System.Text.Json;");
+ _ = builder.AppendLine("using System.Text.Json.Serialization;");
+ _ = builder.AppendLine("using ObsWebSocket.Core.Protocol;");
+ _ = builder.AppendLine("using ObsWebSocket.Core.Protocol.Common;");
+ _ = builder.AppendLine("using ObsWebSocket.Core.Protocol.Events;");
+ _ = builder.AppendLine("using ObsWebSocket.Core.Protocol.Requests;");
+ _ = builder.AppendLine("using ObsWebSocket.Core.Protocol.Responses;");
+ _ = builder.AppendLine();
+ _ = builder.AppendLine("namespace ObsWebSocket.Core.Serialization;");
+ _ = builder.AppendLine();
+ _ = builder.AppendLine("[JsonSourceGenerationOptions(");
+ _ = builder.AppendLine(" PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,");
+ _ = builder.AppendLine(" DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault)]");
+
+ // Fixed protocol wrapper and payload types.
+ _ = builder.AppendLine("[JsonSerializable(typeof(OutgoingMessage))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(OutgoingMessage))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(OutgoingMessage))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(OutgoingMessage))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(IncomingMessage))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(RequestResponsePayload))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(RequestBatchResponsePayload))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(EventPayloadBase))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(HelloPayload))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(IdentifiedPayload))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(RequestStatus))]");
+
+ // Handwritten stub types.
+ _ = builder.AppendLine("[JsonSerializable(typeof(SceneStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(SceneItemStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(SceneItemTransformStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(FilterStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(InputStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(TransitionStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(OutputStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(MonitorStub))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(PropertyItemStub))]");
+
+ // Common collection payload helpers.
+ _ = builder.AppendLine("[JsonSerializable(typeof(List))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(Dictionary))]");
+ _ = builder.AppendLine("[JsonSerializable(typeof(Dictionary))]");
+ _ = builder.AppendLine(
+ "[JsonSerializable(typeof(List>))]"
+ );
+
+ // Generated request/response/event DTOs.
+ if (protocol.Requests is not null)
+ {
+ foreach (RequestDefinition request in protocol.Requests)
+ {
+ string baseName = SanitizeIdentifier(request.RequestType);
+ if (request.RequestFields?.Count > 0)
+ {
+ _ = builder.AppendLine(
+ $"[JsonSerializable(typeof({GeneratedRequestsNamespace}.{baseName}RequestData))]"
+ );
+ }
+
+ if (request.ResponseFields?.Count > 0)
+ {
+ _ = builder.AppendLine(
+ $"[JsonSerializable(typeof({GeneratedResponsesNamespace}.{baseName}ResponseData))]"
+ );
+ }
+ }
+ }
+
+ if (protocol.Events is not null)
+ {
+ foreach (OBSEvent eventDef in protocol.Events)
+ {
+ if (eventDef.DataFields?.Count > 0)
+ {
+ string payloadType = SanitizeIdentifier(eventDef.EventType) + "Payload";
+ _ = builder.AppendLine(
+ $"[JsonSerializable(typeof({GeneratedEventsNamespace}.{payloadType}))]"
+ );
+ }
+ }
+ }
+
+ foreach (string nestedTypeName in s_generatedNestedTypes.Keys.OrderBy(k => k))
+ {
+ _ = builder.AppendLine(
+ $"[JsonSerializable(typeof({NestedTypesNamespace}.{nestedTypeName}))]"
+ );
+ }
+
+ _ = builder.AppendLine("internal sealed partial class ObsWebSocketJsonContext : JsonSerializerContext");
+ _ = builder.AppendLine("{");
+ _ = builder.AppendLine("}");
+
+ context.AddSource(
+ "ObsWebSocketJsonContext.g.cs",
+ SourceText.From(builder.ToString(), Encoding.UTF8)
+ );
+ }
+ catch (Exception ex)
+ {
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ Diagnostics.IdentifierGenerationError,
+ Location.None,
+ "ObsWebSocketJsonContext",
+ "JsonSerializerContext generation",
+ ex.ToString()
+ )
+ );
+ }
+ }
+}
diff --git a/ObsWebSocket.Codegen.Tasks/Generation/Emitter.MsgPackResolver.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.MsgPackResolver.cs
new file mode 100644
index 0000000..2d1d188
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.MsgPackResolver.cs
@@ -0,0 +1,232 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ObsWebSocket.Codegen.Tasks.Generation;
+
+///
+/// Contains emitter logic for generating a MessagePack resolver for generated protocol types.
+///
+internal static partial class Emitter
+{
+ ///
+ /// Generates a MessagePack formatter resolver for generated request/response/event and nested DTOs.
+ ///
+ public static void GenerateMsgPackResolver(
+ SourceProductionContext context,
+ ProtocolDefinition protocol
+ )
+ {
+ try
+ {
+ List fixedTypeNames = [];
+ List requestTypeNames = [];
+ List responseTypeNames = [];
+ List eventTypeNames = [];
+ List nestedTypeNames = [];
+ HashSet seen = new(StringComparer.Ordinal);
+
+ void AddFixedType(string typeName)
+ {
+ if (seen.Add(typeName))
+ {
+ fixedTypeNames.Add(typeName);
+ }
+ }
+
+ AddFixedType("ObsWebSocket.Core.Protocol.HelloPayload");
+ AddFixedType("ObsWebSocket.Core.Protocol.AuthenticationData");
+ AddFixedType("ObsWebSocket.Core.Protocol.IdentifyPayload");
+ AddFixedType("ObsWebSocket.Core.Protocol.IdentifiedPayload");
+ AddFixedType("ObsWebSocket.Core.Protocol.ReidentifyPayload");
+ AddFixedType("ObsWebSocket.Core.Protocol.RequestPayload");
+ AddFixedType("ObsWebSocket.Core.Protocol.RequestBatchPayload");
+ AddFixedType("ObsWebSocket.Core.Protocol.RequestStatus");
+
+ if (protocol.Requests is not null)
+ {
+ foreach (RequestDefinition request in protocol.Requests)
+ {
+ string baseName = SanitizeIdentifier(request.RequestType);
+ if (request.RequestFields?.Count > 0)
+ {
+ string typeName = $"{GeneratedRequestsNamespace}.{baseName}RequestData";
+ if (seen.Add(typeName))
+ {
+ requestTypeNames.Add(typeName);
+ }
+ }
+
+ if (request.ResponseFields?.Count > 0)
+ {
+ string typeName = $"{GeneratedResponsesNamespace}.{baseName}ResponseData";
+ if (seen.Add(typeName))
+ {
+ responseTypeNames.Add(typeName);
+ }
+ }
+ }
+ }
+
+ if (protocol.Events is not null)
+ {
+ foreach (OBSEvent eventDef in protocol.Events)
+ {
+ if (eventDef.DataFields?.Count > 0)
+ {
+ string payloadType = SanitizeIdentifier(eventDef.EventType) + "Payload";
+ string typeName = $"{GeneratedEventsNamespace}.{payloadType}";
+ if (seen.Add(typeName))
+ {
+ eventTypeNames.Add(typeName);
+ }
+ }
+ }
+ }
+
+ foreach (string nestedTypeName in s_generatedNestedTypes.Keys.OrderBy(k => k))
+ {
+ string typeName = $"{NestedTypesNamespace}.{nestedTypeName}";
+ if (seen.Add(typeName))
+ {
+ nestedTypeNames.Add(typeName);
+ }
+ }
+
+ context.AddSource(
+ "ObsWebSocketMsgPackResolver.g.cs",
+ SourceText.From(BuildResolverRootSource(), Encoding.UTF8)
+ );
+
+ context.AddSource(
+ "ObsWebSocketMsgPackResolver.FixedTypes.g.cs",
+ SourceText.From(BuildKnownTypeSource("IsFixedType", fixedTypeNames), Encoding.UTF8)
+ );
+
+ context.AddSource(
+ "ObsWebSocketMsgPackResolver.RequestTypes.g.cs",
+ SourceText.From(BuildKnownTypeSource("IsRequestType", requestTypeNames), Encoding.UTF8)
+ );
+
+ context.AddSource(
+ "ObsWebSocketMsgPackResolver.ResponseTypes.g.cs",
+ SourceText.From(BuildKnownTypeSource("IsResponseType", responseTypeNames), Encoding.UTF8)
+ );
+
+ context.AddSource(
+ "ObsWebSocketMsgPackResolver.EventTypes.g.cs",
+ SourceText.From(BuildKnownTypeSource("IsEventType", eventTypeNames), Encoding.UTF8)
+ );
+
+ context.AddSource(
+ "ObsWebSocketMsgPackResolver.NestedTypes.g.cs",
+ SourceText.From(BuildKnownTypeSource("IsNestedType", nestedTypeNames), Encoding.UTF8)
+ );
+ }
+ catch (Exception ex)
+ {
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ Diagnostics.IdentifierGenerationError,
+ Location.None,
+ "ObsWebSocketMsgPackResolver",
+ "MessagePack resolver generation",
+ ex.ToString()
+ )
+ );
+ }
+ }
+
+ private static string BuildResolverRootSource()
+ {
+ StringBuilder builder = BuildSourceHeader("// Serialization Resolver: ObsWebSocketMsgPackResolver");
+ builder.AppendLine("using System;");
+ builder.AppendLine("using MessagePack;");
+ builder.AppendLine("using MessagePack.Formatters;");
+ builder.AppendLine("using MessagePack.Resolvers;");
+ builder.AppendLine();
+ builder.AppendLine("namespace ObsWebSocket.Core.Serialization;");
+ builder.AppendLine();
+ builder.AppendLine("/// ");
+ builder.AppendLine("/// MessagePack resolver for OBS WebSocket protocol DTOs.");
+ builder.AppendLine("/// ");
+ builder.AppendLine("public sealed class ObsWebSocketMsgPackResolver : IFormatterResolver");
+ builder.AppendLine("{");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Singleton resolver instance.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(
+ " public static readonly IFormatterResolver Instance = new ObsWebSocketMsgPackResolver();"
+ );
+ builder.AppendLine();
+ builder.AppendLine(" private ObsWebSocketMsgPackResolver() { }");
+ builder.AppendLine();
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Gets a formatter for when this resolver supports it.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(
+ " public IMessagePackFormatter? GetFormatter() => ObsWebSocketMsgPackResolverCore.GetFormatter();"
+ );
+ builder.AppendLine("}");
+ builder.AppendLine();
+ builder.AppendLine("internal static partial class ObsWebSocketMsgPackResolverCore");
+ builder.AppendLine("{");
+ builder.AppendLine(" public static IMessagePackFormatter? GetFormatter()");
+ builder.AppendLine(" {");
+ builder.AppendLine(" Type type = typeof(T);");
+ builder.AppendLine(" if (!IsKnownType(type))");
+ builder.AppendLine(" {");
+ builder.AppendLine(" return null;");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(" return SourceGeneratedFormatterResolver.Instance.GetFormatter();");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(" private static bool IsKnownType(Type type)");
+ builder.AppendLine(" {");
+ builder.AppendLine(
+ " return IsFixedType(type) || IsRequestType(type) || IsResponseType(type) || IsEventType(type) || IsNestedType(type);"
+ );
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(" private static partial bool IsFixedType(Type type);");
+ builder.AppendLine(" private static partial bool IsRequestType(Type type);");
+ builder.AppendLine(" private static partial bool IsResponseType(Type type);");
+ builder.AppendLine(" private static partial bool IsEventType(Type type);");
+ builder.AppendLine(" private static partial bool IsNestedType(Type type);");
+ builder.AppendLine("}");
+
+ return builder.ToString();
+ }
+
+ private static string BuildKnownTypeSource(string methodName, List typeNames)
+ {
+ StringBuilder builder = BuildSourceHeader("// Serialization Resolver Type Map");
+ builder.AppendLine("using System;");
+ builder.AppendLine();
+ builder.AppendLine("namespace ObsWebSocket.Core.Serialization;");
+ builder.AppendLine();
+ builder.AppendLine("internal static partial class ObsWebSocketMsgPackResolverCore");
+ builder.AppendLine("{");
+ builder.AppendLine($" private static partial bool {methodName}(Type type)");
+ builder.AppendLine(" {");
+
+ if (typeNames.Count == 0)
+ {
+ builder.AppendLine(" return false;");
+ }
+ else
+ {
+ builder.AppendLine(" return");
+ for (int i = 0; i < typeNames.Count; i++)
+ {
+ string suffix = i == typeNames.Count - 1 ? ";" : " ||";
+ builder.AppendLine($" type == typeof({typeNames[i]}){suffix}");
+ }
+ }
+
+ builder.AppendLine(" }");
+ builder.AppendLine("}");
+ return builder.ToString();
+ }
+}
diff --git a/ObsWebSocket.SourceGenerators/Emitter.WaitForEvent.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.WaitForEvent.cs
similarity index 99%
rename from ObsWebSocket.SourceGenerators/Emitter.WaitForEvent.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/Emitter.WaitForEvent.cs
index 30bd219..5a30eba 100644
--- a/ObsWebSocket.SourceGenerators/Emitter.WaitForEvent.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.WaitForEvent.cs
@@ -1,9 +1,9 @@
-// ObsWebSocket.SourceGenerators/Emitter.WaitForEvent.cs
+// ObsWebSocket.Codegen.Tasks/Generation/Emitter.WaitForEvent.cs
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Contains emitter logic specifically for generating the WaitForEventAsync helper method.
diff --git a/ObsWebSocket.SourceGenerators/Emitter.cs b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.cs
similarity index 99%
rename from ObsWebSocket.SourceGenerators/Emitter.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/Emitter.cs
index c9c3b80..e27ea9c 100644
--- a/ObsWebSocket.SourceGenerators/Emitter.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/Emitter.cs
@@ -1,9 +1,9 @@
-using System.Text;
+using System.Text;
using System.Text.Json;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text; // Required for SourceText
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Main entry point for the Emitter logic. Combines helpers and generation methods.
diff --git a/ObsWebSocket.Codegen.Tasks/Generation/GenerationContext.cs b/ObsWebSocket.Codegen.Tasks/Generation/GenerationContext.cs
new file mode 100644
index 0000000..5176b21
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/Generation/GenerationContext.cs
@@ -0,0 +1,66 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ObsWebSocket.Codegen.Tasks.Generation;
+
+internal sealed class GenerationContext
+{
+ private readonly List _diagnostics = [];
+ private readonly Dictionary _sources = new(StringComparer.OrdinalIgnoreCase);
+
+ public IReadOnlyList Diagnostics => _diagnostics;
+
+ public IReadOnlyDictionary Sources => _sources;
+
+ public void AddSource(string hintName, SourceText sourceText)
+ {
+ ArgumentException.ThrowIfNullOrEmpty(hintName);
+ ArgumentNullException.ThrowIfNull(sourceText);
+
+ string relativePath = ResolveOutputPath(hintName, sourceText.ToString());
+ _sources[relativePath] = sourceText.ToString();
+ }
+
+ public void ReportDiagnostic(Diagnostic diagnostic) => _diagnostics.Add(diagnostic);
+
+ private static string ResolveOutputPath(string hintName, string source)
+ {
+ string fileName = Path.GetFileName(hintName);
+ string namespaceLine = source
+ .Split('\n')
+ .Select(line => line.Trim())
+ .FirstOrDefault(line => line.StartsWith("namespace ", StringComparison.Ordinal))
+ ?? string.Empty;
+
+ if (namespaceLine.Contains("ObsWebSocket.Core.Protocol.Common.NestedTypes", StringComparison.Ordinal))
+ {
+ return Path.Combine("Protocol", "Common", "NestedTypes", fileName);
+ }
+
+ if (namespaceLine.Contains("ObsWebSocket.Core.Protocol.Requests", StringComparison.Ordinal))
+ {
+ return Path.Combine("Protocol", "Requests", fileName);
+ }
+
+ if (namespaceLine.Contains("ObsWebSocket.Core.Protocol.Responses", StringComparison.Ordinal))
+ {
+ return Path.Combine("Protocol", "Responses", fileName);
+ }
+
+ if (namespaceLine.Contains("ObsWebSocket.Core.Protocol.Events", StringComparison.Ordinal))
+ {
+ return Path.Combine("Protocol", "Events", fileName);
+ }
+
+ if (namespaceLine.Contains("ObsWebSocket.Core.Protocol.Generated", StringComparison.Ordinal))
+ {
+ return Path.Combine("Protocol", "Generated", fileName);
+ }
+
+ return namespaceLine.Contains("ObsWebSocket.Core.Events.Generated", StringComparison.Ordinal)
+ ? Path.Combine("Events", "Generated", fileName)
+ : namespaceLine.Contains("ObsWebSocket.Core.Serialization", StringComparison.Ordinal)
+ ? Path.Combine("Serialization", fileName)
+ : Path.Combine("Client", fileName);
+ }
+}
diff --git a/ObsWebSocket.Codegen.Tasks/Generation/GlobalUsings.cs b/ObsWebSocket.Codegen.Tasks/Generation/GlobalUsings.cs
new file mode 100644
index 0000000..91c1a2e
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/Generation/GlobalUsings.cs
@@ -0,0 +1 @@
+global using SourceProductionContext = ObsWebSocket.Codegen.Tasks.Generation.GenerationContext;
diff --git a/ObsWebSocket.Codegen.Tasks/Generation/ProtocolCodeGenerator.cs b/ObsWebSocket.Codegen.Tasks/Generation/ProtocolCodeGenerator.cs
new file mode 100644
index 0000000..237c1e9
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/Generation/ProtocolCodeGenerator.cs
@@ -0,0 +1,41 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.CodeAnalysis;
+
+namespace ObsWebSocket.Codegen.Tasks.Generation;
+
+internal static class ProtocolCodeGenerator
+{
+ private static readonly JsonSerializerOptions s_jsonOptions = new()
+ {
+ PropertyNameCaseInsensitive = true,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString,
+ };
+
+ public static (IReadOnlyDictionary Sources, IReadOnlyList Diagnostics) Generate(string protocolJson)
+ {
+ ArgumentException.ThrowIfNullOrEmpty(protocolJson);
+
+ GenerationContext context = new();
+ ProtocolDefinition? protocol = JsonSerializer.Deserialize(protocolJson, s_jsonOptions);
+ if (protocol is null)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(Diagnostics.ProtocolJsonParseError, Location.None, "Deserialization returned null."));
+ return (context.Sources, context.Diagnostics);
+ }
+
+ Emitter.PreGenerateNestedDtos(context, protocol);
+ Emitter.GenerateEnums(context, protocol);
+ Emitter.GenerateRequestDtos(context, protocol);
+ Emitter.GenerateResponseDtos(context, protocol);
+ Emitter.GenerateClientExtensions(context, protocol);
+ Emitter.GenerateEventPayloads(context, protocol);
+ Emitter.GenerateEventArgs(context, protocol);
+ Emitter.GenerateClientEventInfrastructure(context, protocol);
+ Emitter.GenerateWaitForEventHelper(context, protocol);
+ Emitter.GenerateJsonSerializerContext(context, protocol);
+ Emitter.GenerateMsgPackResolver(context, protocol);
+
+ return (context.Sources, context.Diagnostics);
+ }
+}
diff --git a/ObsWebSocket.SourceGenerators/ProtocolModel.cs b/ObsWebSocket.Codegen.Tasks/Generation/ProtocolModel.cs
similarity index 99%
rename from ObsWebSocket.SourceGenerators/ProtocolModel.cs
rename to ObsWebSocket.Codegen.Tasks/Generation/ProtocolModel.cs
index f73c2e4..cf6b912 100644
--- a/ObsWebSocket.SourceGenerators/ProtocolModel.cs
+++ b/ObsWebSocket.Codegen.Tasks/Generation/ProtocolModel.cs
@@ -1,7 +1,7 @@
-using System.Text.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
-namespace ObsWebSocket.SourceGenerators;
+namespace ObsWebSocket.Codegen.Tasks.Generation;
///
/// Represents the definition of a single event type in the protocol.
diff --git a/ObsWebSocket.Codegen.Tasks/ObsWebSocket.Codegen.Tasks.csproj b/ObsWebSocket.Codegen.Tasks/ObsWebSocket.Codegen.Tasks.csproj
new file mode 100644
index 0000000..a48436b
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/ObsWebSocket.Codegen.Tasks.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net9.0
+ enable
+ enable
+ $(NoWarn);RS2008
+
+
+
+
+
+
+
+
+
diff --git a/ObsWebSocket.Codegen.Tasks/ProtocolCodegenRunner.cs b/ObsWebSocket.Codegen.Tasks/ProtocolCodegenRunner.cs
new file mode 100644
index 0000000..cb4bbeb
--- /dev/null
+++ b/ObsWebSocket.Codegen.Tasks/ProtocolCodegenRunner.cs
@@ -0,0 +1,121 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using ObsWebSocket.Codegen.Tasks.Generation;
+
+namespace ObsWebSocket.Codegen.Tasks;
+
+internal static class ProtocolCodegenRunner
+{
+ private const string ProtocolUrl = "https://raw.githubusercontent.com/obsproject/obs-websocket/master/docs/generated/protocol.json";
+
+ public static async Task GenerateAsync(
+ string protocolPath,
+ string outputDirectory,
+ bool downloadIfMissing,
+ CancellationToken cancellationToken,
+ Action? logInfo = null,
+ Action? logWarning = null,
+ Action? logError = null
+ )
+ {
+ ArgumentException.ThrowIfNullOrEmpty(protocolPath);
+ ArgumentException.ThrowIfNullOrEmpty(outputDirectory);
+
+ try
+ {
+ string fullProtocolPath = Path.GetFullPath(protocolPath);
+ string fullOutputDirectory = Path.GetFullPath(outputDirectory);
+
+ if (!File.Exists(fullProtocolPath))
+ {
+ if (!downloadIfMissing)
+ {
+ logError?.Invoke($"Protocol file not found: {fullProtocolPath}");
+ return 2;
+ }
+
+ await DownloadProtocolAsync(fullProtocolPath, cancellationToken).ConfigureAwait(false);
+ logInfo?.Invoke($"Downloaded protocol.json to '{fullProtocolPath}'.");
+ }
+
+ string protocolJson = await File.ReadAllTextAsync(fullProtocolPath, cancellationToken).ConfigureAwait(false);
+ (IReadOnlyDictionary sources, IReadOnlyList diagnostics) = ProtocolCodeGenerator.Generate(protocolJson);
+
+ Diagnostic[] errors = [.. diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)];
+ Diagnostic[] warnings = [.. diagnostics.Where(d => d.Severity == DiagnosticSeverity.Warning)];
+ Diagnostic[] infos = [.. diagnostics.Where(d => d.Severity == DiagnosticSeverity.Info)];
+ if (errors.Length > 0)
+ {
+ StringBuilder builder = new();
+ _ = builder.AppendLine("Code generation failed:");
+ foreach (Diagnostic diagnostic in errors)
+ {
+ _ = builder.AppendLine(diagnostic.ToString());
+ }
+
+ logError?.Invoke(builder.ToString());
+ return 1;
+ }
+
+ foreach (Diagnostic warning in warnings)
+ {
+ logWarning?.Invoke(warning.ToString());
+ }
+
+ foreach (Diagnostic info in infos)
+ {
+ logInfo?.Invoke(info.ToString());
+ }
+
+ WriteSources(fullOutputDirectory, sources);
+ logInfo?.Invoke($"Generated {sources.Count} source files to '{fullOutputDirectory}'.");
+
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ logError?.Invoke(ex.ToString());
+ return 1;
+ }
+ }
+
+ private static async Task DownloadProtocolAsync(string protocolPath, CancellationToken cancellationToken)
+ {
+ _ = Directory.CreateDirectory(Path.GetDirectoryName(protocolPath)!);
+ using HttpClient http = new();
+ using HttpResponseMessage response = await http.GetAsync(ProtocolUrl, cancellationToken).ConfigureAwait(false);
+ _ = response.EnsureSuccessStatusCode();
+ string protocolJson = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
+ await File.WriteAllTextAsync(protocolPath, protocolJson, new UTF8Encoding(false), cancellationToken).ConfigureAwait(false);
+ }
+
+ private static void WriteSources(string outputDirectory, IReadOnlyDictionary sources)
+ {
+ _ = Directory.CreateDirectory(outputDirectory);
+
+ HashSet generatedRelativePaths = sources.Keys
+ .Select(NormalizeRelativePath)
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (string existingFile in Directory.GetFiles(outputDirectory, "*.g.cs", SearchOption.AllDirectories))
+ {
+ string relativePath = NormalizeRelativePath(Path.GetRelativePath(outputDirectory, existingFile));
+ if (!generatedRelativePaths.Contains(relativePath))
+ {
+ File.Delete(existingFile);
+ }
+ }
+
+ foreach ((string relativePath, string source) in sources)
+ {
+ string normalizedRelativePath = NormalizeRelativePath(relativePath);
+ string outputPath = Path.Combine(outputDirectory, normalizedRelativePath);
+ _ = Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
+ File.WriteAllText(outputPath, source, new UTF8Encoding(false));
+ }
+ }
+
+ private static string NormalizeRelativePath(string path) => path
+ .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)
+ .TrimStart(Path.DirectorySeparatorChar);
+}
diff --git a/ObsWebSocket.Core/Generated/Client/ObsWebSocketClient.Events.g.cs b/ObsWebSocket.Core/Generated/Client/ObsWebSocketClient.Events.g.cs
new file mode 100644
index 0000000..fff5187
--- /dev/null
+++ b/ObsWebSocket.Core/Generated/Client/ObsWebSocketClient.Events.g.cs
@@ -0,0 +1,1076 @@
+//
+#nullable enable
+
+using System;
+using ObsWebSocket.Core.Events.Generated;
+
+namespace ObsWebSocket.Core;
+
+///
+/// Contains generated event fields and the corresponding invoker methods
+/// for the , based on the OBS WebSocket protocol definition.
+///
+public sealed partial class ObsWebSocketClient
+{
+
+ ///
+ /// Occurs when the CurrentSceneCollectionChanging event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current scene collection has begun changing.
+ ///
+ /// Note: We recommend using this event to trigger a pause of all polling requests, as performing any requests during a
+ /// scene collection change is considered undefined behavior and can cause crashes!
+ /// Requires the Config subscription.
+ /// Category: config | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentSceneCollectionChanging;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentSceneCollectionChanging(ObsWebSocket.Core.Events.Generated.CurrentSceneCollectionChangingEventArgs e)
+ {
+ CurrentSceneCollectionChanging?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentSceneCollectionChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current scene collection has changed.
+ ///
+ /// Note: If polling has been paused during `CurrentSceneCollectionChanging`, this is the que to restart polling.
+ /// Requires the Config subscription.
+ /// Category: config | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentSceneCollectionChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentSceneCollectionChanged(ObsWebSocket.Core.Events.Generated.CurrentSceneCollectionChangedEventArgs e)
+ {
+ CurrentSceneCollectionChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneCollectionListChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The scene collection list has changed.
+ /// Requires the Config subscription.
+ /// Category: config | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneCollectionListChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneCollectionListChanged(ObsWebSocket.Core.Events.Generated.SceneCollectionListChangedEventArgs e)
+ {
+ SceneCollectionListChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentProfileChanging event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current profile has begun changing.
+ /// Requires the Config subscription.
+ /// Category: config | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentProfileChanging;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentProfileChanging(ObsWebSocket.Core.Events.Generated.CurrentProfileChangingEventArgs e)
+ {
+ CurrentProfileChanging?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentProfileChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current profile has changed.
+ /// Requires the Config subscription.
+ /// Category: config | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentProfileChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentProfileChanged(ObsWebSocket.Core.Events.Generated.CurrentProfileChangedEventArgs e)
+ {
+ CurrentProfileChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the ProfileListChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The profile list has changed.
+ /// Requires the Config subscription.
+ /// Category: config | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? ProfileListChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnProfileListChanged(ObsWebSocket.Core.Events.Generated.ProfileListChangedEventArgs e)
+ {
+ ProfileListChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SourceFilterListReindexed event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A source's filter list has been reindexed.
+ /// Requires the Filters subscription.
+ /// Category: filters | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SourceFilterListReindexed;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSourceFilterListReindexed(ObsWebSocket.Core.Events.Generated.SourceFilterListReindexedEventArgs e)
+ {
+ SourceFilterListReindexed?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SourceFilterCreated event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A filter has been added to a source.
+ /// Requires the Filters subscription.
+ /// Category: filters | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SourceFilterCreated;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSourceFilterCreated(ObsWebSocket.Core.Events.Generated.SourceFilterCreatedEventArgs e)
+ {
+ SourceFilterCreated?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SourceFilterRemoved event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A filter has been removed from a source.
+ /// Requires the Filters subscription.
+ /// Category: filters | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SourceFilterRemoved;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSourceFilterRemoved(ObsWebSocket.Core.Events.Generated.SourceFilterRemovedEventArgs e)
+ {
+ SourceFilterRemoved?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SourceFilterNameChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The name of a source filter has changed.
+ /// Requires the Filters subscription.
+ /// Category: filters | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SourceFilterNameChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSourceFilterNameChanged(ObsWebSocket.Core.Events.Generated.SourceFilterNameChangedEventArgs e)
+ {
+ SourceFilterNameChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SourceFilterSettingsChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An source filter's settings have changed (been updated).
+ /// Requires the Filters subscription.
+ /// Category: filters | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.4.0
+ ///
+ public event EventHandler? SourceFilterSettingsChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSourceFilterSettingsChanged(ObsWebSocket.Core.Events.Generated.SourceFilterSettingsChangedEventArgs e)
+ {
+ SourceFilterSettingsChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SourceFilterEnableStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A source filter's enable state has changed.
+ /// Requires the Filters subscription.
+ /// Category: filters | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SourceFilterEnableStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSourceFilterEnableStateChanged(ObsWebSocket.Core.Events.Generated.SourceFilterEnableStateChangedEventArgs e)
+ {
+ SourceFilterEnableStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the ExitStarted event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// OBS has begun the shutdown process.
+ /// Requires the General subscription.
+ /// Category: general | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? ExitStarted;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnExitStarted(ObsWebSocket.Core.Events.Generated.ExitStartedEventArgs e)
+ {
+ ExitStarted?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputCreated event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input has been created.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputCreated;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputCreated(ObsWebSocket.Core.Events.Generated.InputCreatedEventArgs e)
+ {
+ InputCreated?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputRemoved event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input has been removed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputRemoved;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputRemoved(ObsWebSocket.Core.Events.Generated.InputRemovedEventArgs e)
+ {
+ InputRemoved?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputNameChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The name of an input has changed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputNameChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputNameChanged(ObsWebSocket.Core.Events.Generated.InputNameChangedEventArgs e)
+ {
+ InputNameChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputSettingsChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input's settings have changed (been updated).
+ ///
+ /// Note: On some inputs, changing values in the properties dialog will cause an immediate update. Pressing the "Cancel" button will revert the settings, resulting in another event being fired.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.4.0
+ ///
+ public event EventHandler? InputSettingsChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputSettingsChanged(ObsWebSocket.Core.Events.Generated.InputSettingsChangedEventArgs e)
+ {
+ InputSettingsChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputActiveStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input's active state has changed.
+ ///
+ /// When an input is active, it means it's being shown by the program feed.
+ /// Requires the InputActiveStateChanged subscription.
+ /// Category: inputs | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputActiveStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputActiveStateChanged(ObsWebSocket.Core.Events.Generated.InputActiveStateChangedEventArgs e)
+ {
+ InputActiveStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputShowStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input's show state has changed.
+ ///
+ /// When an input is showing, it means it's being shown by the preview or a dialog.
+ /// Requires the InputShowStateChanged subscription.
+ /// Category: inputs | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputShowStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputShowStateChanged(ObsWebSocket.Core.Events.Generated.InputShowStateChangedEventArgs e)
+ {
+ InputShowStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputMuteStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input's mute state has changed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputMuteStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputMuteStateChanged(ObsWebSocket.Core.Events.Generated.InputMuteStateChangedEventArgs e)
+ {
+ InputMuteStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputVolumeChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An input's volume level has changed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputVolumeChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputVolumeChanged(ObsWebSocket.Core.Events.Generated.InputVolumeChangedEventArgs e)
+ {
+ InputVolumeChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputAudioBalanceChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The audio balance value of an input has changed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputAudioBalanceChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputAudioBalanceChanged(ObsWebSocket.Core.Events.Generated.InputAudioBalanceChangedEventArgs e)
+ {
+ InputAudioBalanceChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputAudioSyncOffsetChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The sync offset of an input has changed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputAudioSyncOffsetChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputAudioSyncOffsetChanged(ObsWebSocket.Core.Events.Generated.InputAudioSyncOffsetChangedEventArgs e)
+ {
+ InputAudioSyncOffsetChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputAudioTracksChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The audio tracks of an input have changed.
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputAudioTracksChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputAudioTracksChanged(ObsWebSocket.Core.Events.Generated.InputAudioTracksChangedEventArgs e)
+ {
+ InputAudioTracksChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputAudioMonitorTypeChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The monitor type of an input has changed.
+ ///
+ /// Available types are:
+ ///
+ /// - `OBS_MONITORING_TYPE_NONE`
+ /// - `OBS_MONITORING_TYPE_MONITOR_ONLY`
+ /// - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
+ /// Requires the Inputs subscription.
+ /// Category: inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputAudioMonitorTypeChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputAudioMonitorTypeChanged(ObsWebSocket.Core.Events.Generated.InputAudioMonitorTypeChangedEventArgs e)
+ {
+ InputAudioMonitorTypeChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the InputVolumeMeters event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A high-volume event providing volume levels of all active inputs every 50 milliseconds.
+ /// Requires the InputVolumeMeters subscription.
+ /// Category: inputs | Complexity: 4
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? InputVolumeMeters;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnInputVolumeMeters(ObsWebSocket.Core.Events.Generated.InputVolumeMetersEventArgs e)
+ {
+ InputVolumeMeters?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the MediaInputPlaybackStarted event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A media input has started playing.
+ /// Requires the MediaInputs subscription.
+ /// Category: media inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? MediaInputPlaybackStarted;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnMediaInputPlaybackStarted(ObsWebSocket.Core.Events.Generated.MediaInputPlaybackStartedEventArgs e)
+ {
+ MediaInputPlaybackStarted?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the MediaInputPlaybackEnded event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A media input has finished playing.
+ /// Requires the MediaInputs subscription.
+ /// Category: media inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? MediaInputPlaybackEnded;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnMediaInputPlaybackEnded(ObsWebSocket.Core.Events.Generated.MediaInputPlaybackEndedEventArgs e)
+ {
+ MediaInputPlaybackEnded?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the MediaInputActionTriggered event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An action has been performed on an input.
+ /// Requires the MediaInputs subscription.
+ /// Category: media inputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? MediaInputActionTriggered;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnMediaInputActionTriggered(ObsWebSocket.Core.Events.Generated.MediaInputActionTriggeredEventArgs e)
+ {
+ MediaInputActionTriggered?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the StreamStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The state of the stream output has changed.
+ /// Requires the Outputs subscription.
+ /// Category: outputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? StreamStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnStreamStateChanged(ObsWebSocket.Core.Events.Generated.StreamStateChangedEventArgs e)
+ {
+ StreamStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the RecordStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The state of the record output has changed.
+ /// Requires the Outputs subscription.
+ /// Category: outputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? RecordStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnRecordStateChanged(ObsWebSocket.Core.Events.Generated.RecordStateChangedEventArgs e)
+ {
+ RecordStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the RecordFileChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The record output has started writing to a new file. For example, when a file split happens.
+ /// Requires the Outputs subscription.
+ /// Category: outputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.5.0
+ ///
+ public event EventHandler? RecordFileChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnRecordFileChanged(ObsWebSocket.Core.Events.Generated.RecordFileChangedEventArgs e)
+ {
+ RecordFileChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the ReplayBufferStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The state of the replay buffer output has changed.
+ /// Requires the Outputs subscription.
+ /// Category: outputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? ReplayBufferStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnReplayBufferStateChanged(ObsWebSocket.Core.Events.Generated.ReplayBufferStateChangedEventArgs e)
+ {
+ ReplayBufferStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the VirtualcamStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The state of the virtualcam output has changed.
+ /// Requires the Outputs subscription.
+ /// Category: outputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? VirtualcamStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnVirtualcamStateChanged(ObsWebSocket.Core.Events.Generated.VirtualcamStateChangedEventArgs e)
+ {
+ VirtualcamStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the ReplayBufferSaved event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The replay buffer has been saved.
+ /// Requires the Outputs subscription.
+ /// Category: outputs | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? ReplayBufferSaved;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnReplayBufferSaved(ObsWebSocket.Core.Events.Generated.ReplayBufferSavedEventArgs e)
+ {
+ ReplayBufferSaved?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemCreated event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene item has been created.
+ /// Requires the SceneItems subscription.
+ /// Category: scene items | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemCreated;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemCreated(ObsWebSocket.Core.Events.Generated.SceneItemCreatedEventArgs e)
+ {
+ SceneItemCreated?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemRemoved event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene item has been removed.
+ ///
+ /// This event is not emitted when the scene the item is in is removed.
+ /// Requires the SceneItems subscription.
+ /// Category: scene items | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemRemoved;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemRemoved(ObsWebSocket.Core.Events.Generated.SceneItemRemovedEventArgs e)
+ {
+ SceneItemRemoved?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemListReindexed event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene's item list has been reindexed.
+ /// Requires the SceneItems subscription.
+ /// Category: scene items | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemListReindexed;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemListReindexed(ObsWebSocket.Core.Events.Generated.SceneItemListReindexedEventArgs e)
+ {
+ SceneItemListReindexed?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemEnableStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene item's enable state has changed.
+ /// Requires the SceneItems subscription.
+ /// Category: scene items | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemEnableStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemEnableStateChanged(ObsWebSocket.Core.Events.Generated.SceneItemEnableStateChangedEventArgs e)
+ {
+ SceneItemEnableStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemLockStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene item's lock state has changed.
+ /// Requires the SceneItems subscription.
+ /// Category: scene items | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemLockStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemLockStateChanged(ObsWebSocket.Core.Events.Generated.SceneItemLockStateChangedEventArgs e)
+ {
+ SceneItemLockStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemSelected event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene item has been selected in the Ui.
+ /// Requires the SceneItems subscription.
+ /// Category: scene items | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemSelected;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemSelected(ObsWebSocket.Core.Events.Generated.SceneItemSelectedEventArgs e)
+ {
+ SceneItemSelected?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneItemTransformChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The transform/crop of a scene item has changed.
+ /// Requires the SceneItemTransformChanged subscription.
+ /// Category: scene items | Complexity: 4
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneItemTransformChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneItemTransformChanged(ObsWebSocket.Core.Events.Generated.SceneItemTransformChangedEventArgs e)
+ {
+ SceneItemTransformChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneCreated event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A new scene has been created.
+ /// Requires the Scenes subscription.
+ /// Category: scenes | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneCreated;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneCreated(ObsWebSocket.Core.Events.Generated.SceneCreatedEventArgs e)
+ {
+ SceneCreated?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneRemoved event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene has been removed.
+ /// Requires the Scenes subscription.
+ /// Category: scenes | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneRemoved;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneRemoved(ObsWebSocket.Core.Events.Generated.SceneRemovedEventArgs e)
+ {
+ SceneRemoved?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneNameChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The name of a scene has changed.
+ /// Requires the Scenes subscription.
+ /// Category: scenes | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneNameChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneNameChanged(ObsWebSocket.Core.Events.Generated.SceneNameChangedEventArgs e)
+ {
+ SceneNameChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentProgramSceneChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current program scene has changed.
+ /// Requires the Scenes subscription.
+ /// Category: scenes | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentProgramSceneChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentProgramSceneChanged(ObsWebSocket.Core.Events.Generated.CurrentProgramSceneChangedEventArgs e)
+ {
+ CurrentProgramSceneChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentPreviewSceneChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current preview scene has changed.
+ /// Requires the Scenes subscription.
+ /// Category: scenes | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentPreviewSceneChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentPreviewSceneChanged(ObsWebSocket.Core.Events.Generated.CurrentPreviewSceneChangedEventArgs e)
+ {
+ CurrentPreviewSceneChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneListChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The list of scenes has changed.
+ ///
+ /// TODO: Make OBS fire this event when scenes are reordered.
+ /// Requires the Scenes subscription.
+ /// Category: scenes | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneListChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneListChanged(ObsWebSocket.Core.Events.Generated.SceneListChangedEventArgs e)
+ {
+ SceneListChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentSceneTransitionChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current scene transition has changed.
+ /// Requires the Transitions subscription.
+ /// Category: transitions | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentSceneTransitionChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentSceneTransitionChanged(ObsWebSocket.Core.Events.Generated.CurrentSceneTransitionChangedEventArgs e)
+ {
+ CurrentSceneTransitionChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CurrentSceneTransitionDurationChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// The current scene transition duration has changed.
+ /// Requires the Transitions subscription.
+ /// Category: transitions | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CurrentSceneTransitionDurationChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCurrentSceneTransitionDurationChanged(ObsWebSocket.Core.Events.Generated.CurrentSceneTransitionDurationChangedEventArgs e)
+ {
+ CurrentSceneTransitionDurationChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneTransitionStarted event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene transition has started.
+ /// Requires the Transitions subscription.
+ /// Category: transitions | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneTransitionStarted;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneTransitionStarted(ObsWebSocket.Core.Events.Generated.SceneTransitionStartedEventArgs e)
+ {
+ SceneTransitionStarted?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneTransitionEnded event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene transition has completed fully.
+ ///
+ /// Note: Does not appear to trigger when the transition is interrupted by the user.
+ /// Requires the Transitions subscription.
+ /// Category: transitions | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneTransitionEnded;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneTransitionEnded(ObsWebSocket.Core.Events.Generated.SceneTransitionEndedEventArgs e)
+ {
+ SceneTransitionEnded?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the SceneTransitionVideoEnded event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A scene transition's video has completed fully.
+ ///
+ /// Useful for stinger transitions to tell when the video *actually* ends.
+ /// `SceneTransitionEnded` only signifies the cut point, not the completion of transition playback.
+ ///
+ /// Note: Appears to be called by every transition, regardless of relevance.
+ /// Requires the Transitions subscription.
+ /// Category: transitions | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? SceneTransitionVideoEnded;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnSceneTransitionVideoEnded(ObsWebSocket.Core.Events.Generated.SceneTransitionVideoEndedEventArgs e)
+ {
+ SceneTransitionVideoEnded?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the StudioModeStateChanged event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// Studio mode has been enabled or disabled.
+ /// Requires the Ui subscription.
+ /// Category: ui | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? StudioModeStateChanged;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnStudioModeStateChanged(ObsWebSocket.Core.Events.Generated.StudioModeStateChangedEventArgs e)
+ {
+ StudioModeStateChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the ScreenshotSaved event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// A screenshot has been saved.
+ ///
+ /// Note: Triggered for the screenshot feature available in `Settings -> Hotkeys -> Screenshot Output` ONLY.
+ /// Applications using `Get/SaveSourceScreenshot` should implement a `CustomEvent` if this kind of inter-client
+ /// communication is desired.
+ /// Requires the Ui subscription.
+ /// Category: ui | Complexity: 2
+ /// RPC Version: 1 | Initial Version: 5.1.0
+ ///
+ public event EventHandler? ScreenshotSaved;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnScreenshotSaved(ObsWebSocket.Core.Events.Generated.ScreenshotSavedEventArgs e)
+ {
+ ScreenshotSaved?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the VendorEvent event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// An event has been emitted from a vendor.
+ ///
+ /// A vendor is a unique name registered by a third-party plugin or script, which allows for custom requests and events to be added to obs-websocket.
+ /// If a plugin or script implements vendor requests or events, documentation is expected to be provided with them.
+ /// Requires the Vendors subscription.
+ /// Category: general | Complexity: 3
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? VendorEvent;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnVendorEvent(ObsWebSocket.Core.Events.Generated.VendorEventEventArgs e)
+ {
+ VendorEvent?.Invoke(this, e);
+ }
+
+ ///
+ /// Occurs when the CustomEvent event is received from the OBS WebSocket server.
+ ///
+ ///
+ /// Custom event emitted by `BroadcastCustomEvent`.
+ /// Requires the General subscription.
+ /// Category: general | Complexity: 1
+ /// RPC Version: 1 | Initial Version: 5.0.0
+ ///
+ public event EventHandler? CustomEvent;
+
+ /// Invokes the event handler safely.
+ /// The containing event data.
+ private void OnCustomEvent(ObsWebSocket.Core.Events.Generated.CustomEventEventArgs e)
+ {
+ CustomEvent?.Invoke(this, e);
+ }
+}
diff --git a/ObsWebSocket.Core/Generated/Client/ObsWebSocketClient.Extensions.g.cs b/ObsWebSocket.Core/Generated/Client/ObsWebSocketClient.Extensions.g.cs
new file mode 100644
index 0000000..82bb37f
--- /dev/null
+++ b/ObsWebSocket.Core/Generated/Client/ObsWebSocketClient.Extensions.g.cs
@@ -0,0 +1,3067 @@
+//
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using ObsWebSocket.Core;
+using ObsWebSocket.Core.Protocol;
+using ObsWebSocket.Core.Protocol.Requests;
+using ObsWebSocket.Core.Protocol.Responses;
+using ObsWebSocket.Core.Protocol.Common;
+
+namespace ObsWebSocket.Core;
+
+///
+/// Provides strongly-typed extension methods for the ,
+/// corresponding to the requests defined in the OBS WebSocket v5 protocol.
+///
+public static partial class ObsWebSocketClientExtensions
+{
+ ///
+ /// Gets the value of a "slot" from the selected persistent data realm.
+ ///
+ /// The instance.
+ /// The data required for the request ().
+ /// A token to cancel the asynchronous operation.
+ /// A task representing the asynchronous operation. Yields the response data, or null if the successful response does not contain data.
+ ///
+ /// OBS WebSocket Protocol Category: config
+ /// Complexity Rating: 2/5
+ /// RPC Version: 1 | Initial OBS WebSocket Version: 5.0.0
+ /// Generated from obs-websocket protocol definition.
+ /// Thrown if the request fails on the OBS side.
+ /// Thrown if the client is not connected.
+ /// Thrown if cancelled.
+ public static async Task GetPersistentDataAsync(this ObsWebSocketClient client, ObsWebSocket.Core.Protocol.Requests.GetPersistentDataRequestData requestData, CancellationToken cancellationToken = default)
+ {
+ return await client.CallAsync("GetPersistentData", requestData, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Sets the value of a "slot" from the selected persistent data realm.
+ ///
+ /// The instance.
+ /// The data required for the request ().
+ /// A token to cancel the asynchronous operation.
+ /// A task representing the asynchronous operation. Completes when the request is processed successfully by the server.
+ ///
+ /// OBS WebSocket Protocol Category: config
+ /// Complexity Rating: 2/5
+ /// RPC Version: 1 | Initial OBS WebSocket Version: 5.0.0
+ /// Generated from obs-websocket protocol definition.
+ /// Thrown if the request fails on the OBS side.
+ /// Thrown if the client is not connected.
+ /// Thrown if cancelled.
+ public static async Task SetPersistentDataAsync(this ObsWebSocketClient client, ObsWebSocket.Core.Protocol.Requests.SetPersistentDataRequestData requestData, CancellationToken cancellationToken = default)
+ {
+ await client.CallAsync