diff --git a/API/Core.cs b/API/Core.cs
deleted file mode 100644
index 5a82c7a..0000000
--- a/API/Core.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace ADOFAIRenderer.API;
-
-public class Core
-{
-
-}
\ No newline at end of file
diff --git a/API/RenderSession.cs b/API/RenderSession.cs
new file mode 100644
index 0000000..95e4d91
--- /dev/null
+++ b/API/RenderSession.cs
@@ -0,0 +1,49 @@
+using System;
+using ADOFAIRenderer.Core;
+
+namespace ADOFAIRenderer.API;
+
+///
+/// Fluent builder for configuring and starting a render session.
+///
+public class RenderSessionBuilder
+{
+ private bool _auto = true;
+
+ public enum PatchType { Time, Screen }
+ private bool _timePatch = true;
+ private bool _screenPatch = true;
+
+ ///
+ /// Sets whether automatic rendering is enabled.
+ ///
+ /// true to enable automatic rendering; otherwise, false. Default is true.
+ /// The current instance for method chaining.
+ public RenderSessionBuilder WithAuto(bool enabled = true)
+ {
+ _auto = enabled;
+ return this;
+ }
+
+ public RenderSessionBuilder WithPatch(PatchType patchType, bool enabled = true)
+ {
+ switch (patchType)
+ {
+ case PatchType.Time: _timePatch = enabled; break;
+ case PatchType.Screen: _screenPatch = enabled; break;
+ default: throw new ArgumentOutOfRangeException(nameof(patchType), patchType, null);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Applies the configured flags and initiates the render session.
+ ///
+ public void Start()
+ {
+ RenderFlags.Auto = _auto;
+ RenderFlags.TimePatch = _timePatch;
+ RenderFlags.ScreenPatch = _screenPatch;
+ }
+}
\ No newline at end of file
diff --git a/Data/PresetManager.cs b/Data/PresetManager.cs
index ea12f0f..344213a 100644
--- a/Data/PresetManager.cs
+++ b/Data/PresetManager.cs
@@ -55,102 +55,101 @@ namespace ADOFAIRenderer.Data
public static List GetPresets()
{
var presets = new List();
- string dir = GetPresetsDirectory();
- if (!Directory.Exists(dir))
- {
- return presets;
- }
+ var dir = GetPresetsDirectory();
+
+ if (!Directory.Exists(dir)) return presets;
try
{
- string[] files = Directory.GetFiles(dir, "*.json");
- foreach (string file in files)
+ var files = Directory.GetFiles(dir, "*.json");
+ foreach (var file in files)
{
try
{
- string json = File.ReadAllText(file);
+ var json = File.ReadAllText(file);
var jObject = JsonConvert.DeserializeObject(json);
- if (jObject != null)
+ if (jObject == null) continue;
+
+ var configToken = jObject["Config"];
+ if (configToken is JObject configObj)
{
- var configToken = jObject["Config"];
- if (configToken is JObject configObj)
- {
- // 1. legacy EncodingConfig -> VideoConfig
- if (configObj["EncodingConfig"] is JObject oldEncodingConfigObj)
- {
- configObj["VideoConfig"] ??= oldEncodingConfigObj;
- configObj.Remove("EncodingConfig");
- }
-
- // 2. legacy top-level Codec -> VideoConfig.Codec
- var legacyCodecToken = configObj["Codec"];
- if (legacyCodecToken != null && legacyCodecToken.Type != JTokenType.Null)
- {
- if (configObj["VideoConfig"] == null || configObj["VideoConfig"]!.Type == JTokenType.Null)
- {
- configObj["VideoConfig"] = new JObject();
- }
-
- var videoConfigObj = (JObject)configObj["VideoConfig"]!;
- videoConfigObj["Codec"] ??= legacyCodecToken;
-
- configObj.Remove("Codec");
- }
-
- // 3. legacy top-level Audio properties -> AudioConfig
- string[] legacyAudioProps = { "RenderAudio", "AudioSampleRate", "AudioChannels", "AudioBitrate" };
- foreach (var prop in legacyAudioProps)
- {
- var legacyToken = configObj[prop];
- if (legacyToken != null && legacyToken.Type != JTokenType.Null)
- {
- if (configObj["AudioConfig"] == null || configObj["AudioConfig"]!.Type == JTokenType.Null)
- {
- configObj["AudioConfig"] = new JObject();
- }
-
- var audioConfigObj = (JObject)configObj["AudioConfig"]!;
- audioConfigObj[prop] ??= legacyToken;
- }
-
- configObj.Remove(prop);
- }
-
- if (configObj["AudioConfig"] != null && configObj["AudioConfig"]!.Type == JTokenType.Null)
- {
- configObj.Remove("AudioConfig");
- }
-
- // 4. legacy top-level Advanced properties -> AdvancedConfig
- string[] legacyAdvancedProps = { "RandomSeed", "ProcessAudio", "NonTile" };
- foreach (var prop in legacyAdvancedProps)
- {
- var legacyToken = configObj[prop];
- if (legacyToken != null && legacyToken.Type != JTokenType.Null)
- {
- if (configObj["AdvancedConfig"] == null || configObj["AdvancedConfig"]!.Type == JTokenType.Null)
- {
- configObj["AdvancedConfig"] = new JObject();
- }
-
- var advancedConfigObj = (JObject)configObj["AdvancedConfig"]!;
- advancedConfigObj[prop] ??= legacyToken;
- }
-
- configObj.Remove(prop);
- }
-
- if (configObj["AdvancedConfig"] != null && configObj["AdvancedConfig"]!.Type == JTokenType.Null)
- {
- configObj.Remove("AdvancedConfig");
- }
- }
+ TryChangeKey(configObj, "DelayAfterLevelEnd", "DelayAfterEnd");
- var preset = jObject.ToObject();
- if (preset != null && !string.IsNullOrEmpty(preset.Name))
+ // 1. legacy EncodingConfig -> VideoConfig
+ if (configObj["EncodingConfig"] is JObject oldEncodingConfigObj)
{
- presets.Add(preset);
+ configObj["VideoConfig"] ??= oldEncodingConfigObj;
+ configObj.Remove("EncodingConfig");
}
+
+ // 2. legacy top-level Codec -> VideoConfig.Codec
+ var legacyCodecToken = configObj["Codec"];
+ if (legacyCodecToken != null && legacyCodecToken.Type != JTokenType.Null)
+ {
+ if (configObj["VideoConfig"] == null || configObj["VideoConfig"]!.Type == JTokenType.Null)
+ {
+ configObj["VideoConfig"] = new JObject();
+ }
+
+ var videoConfigObj = (JObject)configObj["VideoConfig"]!;
+ videoConfigObj["Codec"] ??= legacyCodecToken;
+
+ configObj.Remove("Codec");
+ }
+
+ // 3. legacy top-level Audio properties -> AudioConfig
+ string[] legacyAudioProps = { "RenderAudio", "AudioSampleRate", "AudioChannels", "AudioBitrate" };
+ foreach (var prop in legacyAudioProps)
+ {
+ var legacyToken = configObj[prop];
+ if (legacyToken != null && legacyToken.Type != JTokenType.Null)
+ {
+ if (configObj["AudioConfig"] == null || configObj["AudioConfig"]!.Type == JTokenType.Null)
+ {
+ configObj["AudioConfig"] = new JObject();
+ }
+
+ var audioConfigObj = (JObject)configObj["AudioConfig"]!;
+ audioConfigObj[prop] ??= legacyToken;
+ }
+
+ configObj.Remove(prop);
+ }
+
+ if (configObj["AudioConfig"] != null && configObj["AudioConfig"]!.Type == JTokenType.Null)
+ {
+ configObj.Remove("AudioConfig");
+ }
+
+ // 4. legacy top-level Advanced properties -> AdvancedConfig
+ string[] legacyAdvancedProps = { "RandomSeed", "ProcessAudio", "NonTile" };
+ foreach (var prop in legacyAdvancedProps)
+ {
+ var legacyToken = configObj[prop];
+ if (legacyToken != null && legacyToken.Type != JTokenType.Null)
+ {
+ if (configObj["AdvancedConfig"] == null || configObj["AdvancedConfig"]!.Type == JTokenType.Null)
+ {
+ configObj["AdvancedConfig"] = new JObject();
+ }
+
+ var advancedConfigObj = (JObject)configObj["AdvancedConfig"]!;
+ advancedConfigObj[prop] ??= legacyToken;
+ }
+
+ configObj.Remove(prop);
+ }
+
+ if (configObj["AdvancedConfig"] != null && configObj["AdvancedConfig"]!.Type == JTokenType.Null)
+ {
+ configObj.Remove("AdvancedConfig");
+ }
+ }
+
+ var preset = jObject.ToObject();
+ if (preset != null && !string.IsNullOrEmpty(preset.Name))
+ {
+ presets.Add(preset);
}
}
catch (Exception ex)
@@ -167,6 +166,15 @@ namespace ADOFAIRenderer.Data
return presets;
}
+ private static void TryChangeKey(JObject configObj, string keyBefore, string targetKey)
+ {
+ var legacyToken = configObj[keyBefore];
+ if (legacyToken != null && legacyToken.Type != JTokenType.Null)
+ configObj[targetKey] = legacyToken;
+
+ configObj.Remove(keyBefore);
+ }
+
public static void DeletePreset(string name)
{
string dir = GetPresetsDirectory();
diff --git a/Data/RenderConfig.cs b/Data/RenderConfig.cs
index a8ce42b..7181ac8 100644
--- a/Data/RenderConfig.cs
+++ b/Data/RenderConfig.cs
@@ -2,7 +2,7 @@ using ADOFAIRenderer.Util.FFmpeg;
namespace ADOFAIRenderer.Data
{
- internal class RenderConfig
+ public class RenderConfig
{
public double RenderFPS { get; set; }
public double VideoFPS { get; set; }
@@ -14,18 +14,18 @@ namespace ADOFAIRenderer.Data
public AudioEncodingConfig AudioConfig { get; set; } = new();
public AdvancedConfig AdvancedConfig { get; set; } = new();
- public double DelayAfterLevelEnd { get; set; }
+ public double DelayAfterEnd { get; set; }
public bool ShowJudgementText { get; set; }
}
- internal class AdvancedConfig
+ public class AdvancedConfig
{
public int? RandomSeed { get; set; } = null;
public bool ProcessAudio { get; set; } = true;
public bool NonTile { get; set; } = false;
}
- internal class VideoEncodingConfig
+ public class VideoEncodingConfig
{
public Codec Codec { get; set; } = Codec.H264;
public RateControlMode RateControlMode { get; set; } = RateControlMode.CBR;
@@ -35,10 +35,10 @@ namespace ADOFAIRenderer.Data
// ReSharper disable once InconsistentNaming
public int QP { get; set; } = 18;
public double KeyframeInterval { get; set; } = 2.0;
- public bool ForceSoftwareEncoder { get; set; } = false;
+ public bool ForceSoftwareEncoder { get; set; }
}
- internal class AudioEncodingConfig
+ public class AudioEncodingConfig
{
private int _audioSampleRate = 44100;
private int _audioChannels = 2;