Files
ADOFAIRenderer/API/RenderResult.cs
T
2026-10-15 16:17:13 +09:00

133 lines
6.3 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ADOFAIRenderer.API;
/// <summary>
/// Represents the result and metadata of a completed or canceled render session.
/// </summary>
/// <remarks>
/// Passed to the completion callback registered with <see cref="RenderSession.OnComplete"/>. Immutable.
/// </remarks>
[SuppressMessage("ReSharper", "UnusedType.Global")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class RenderResult
{
/// <summary>
/// Indicates whether the rendering session completed successfully.
/// </summary>
public bool Success { get; }
/// <summary>
/// The output path of the rendered video file: the path given to <see cref="RenderSession.WithOutputPath"/> as passed
/// (relative if it was relative), or the path of the time-stamped file in the output directory chosen in the mod
/// settings (by default &lt;game root&gt;/Render).
/// Empty string if rendering failed or was cancelled (the unfinished file is deleted).
/// </summary>
public string OutputPath { get; }
/// <summary>
/// Total number of video frames sent to the encoder.
/// </summary>
public long TotalFrames { get; }
/// <summary>
/// The calculated video duration in seconds: <see cref="TotalFrames"/> divided by the effective video frame rate
/// (min of the configured VideoFPS and RenderFPS); 0 when the frame rate is not positive.
/// </summary>
public double VideoDuration { get; }
/// <summary>
/// Total wall-clock time spent rendering, from the end of the countdown (start of capture) to the end of the render.
/// </summary>
public TimeSpan RenderDuration { get; }
/// <summary>
/// File size of the output video in bytes (0 if failed or file does not exist).
/// </summary>
public long FileSizeBytes { get; }
/// <summary>
/// Error message or reason if rendering failed or was canceled; <c>null</c> on success.
/// A cancelled render has "Rendering was cancelled by user."; a failed render has "Rendering failed: " followed by
/// the reason. Use <see cref="Success"/> to test the outcome; the texts are meant for display.
/// </summary>
public string? ErrorMessage { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RenderResult"/> class.
/// </summary>
/// <param name="success">Whether the render session completed successfully.</param>
/// <param name="outputPath">The absolute output path of the rendered video file.</param>
/// <param name="totalFrames">Total number of video frames sent to the encoder.</param>
/// <param name="videoDuration">Calculated video duration in seconds.</param>
/// <param name="renderDuration">Total wall-clock time spent rendering.</param>
/// <param name="fileSizeBytes">File size of the output video in bytes.</param>
/// <param name="errorMessage">Error message or reason if failed or canceled; otherwise, <c>null</c>.</param>
public RenderResult(
bool success,
string outputPath,
long totalFrames,
double videoDuration,
TimeSpan renderDuration,
long fileSizeBytes = 0,
string? errorMessage = null)
{
Success = success;
OutputPath = outputPath;
TotalFrames = totalFrames;
VideoDuration = videoDuration;
RenderDuration = renderDuration;
FileSizeBytes = fileSizeBytes;
ErrorMessage = errorMessage;
}
/// <summary>
/// Creates a successful <see cref="RenderResult"/> instance from the specified render metrics.
/// </summary>
/// <param name="outputPath">The path to the rendered video file.</param>
/// <param name="totalFrames">Total number of video frames sent to the encoder.</param>
/// <param name="fps">The effective frames per second used to compute video duration.</param>
/// <param name="renderDuration">Total wall-clock time spent rendering.</param>
/// <returns>A new <see cref="RenderResult"/> representing a successful render session.</returns>
/// <remarks>The file size is read from disk; it is 0 when the file does not exist or cannot be read.</remarks>
internal static RenderResult CreateSuccess(string outputPath, long totalFrames, double fps, TimeSpan renderDuration)
{
var fileSizeBytes = 0L;
if (!string.IsNullOrEmpty(outputPath) && File.Exists(outputPath))
{
try { fileSizeBytes = new FileInfo(outputPath).Length; }
catch { /* ignored */ }
}
var videoDuration = fps > 0 ? totalFrames / fps : 0.0;
return new RenderResult(true, outputPath, totalFrames, videoDuration, renderDuration, fileSizeBytes);
}
/// <summary>
/// Creates a failed or cancelled <see cref="RenderResult"/> instance.
/// </summary>
/// <param name="errorMessage">Error message or reason for failure/cancellation.</param>
/// <param name="totalFrames">Total number of video frames sent to the encoder prior to termination.</param>
/// <param name="fps">The effective frames per second used to compute video duration.</param>
/// <param name="renderDuration">Total wall-clock time spent rendering prior to termination.</param>
/// <returns>A new <see cref="RenderResult"/> representing a failed or canceled render session.</returns>
/// <remarks>OutputPath is empty and FileSizeBytes is 0. A null message is replaced by a generic one.</remarks>
internal static RenderResult CreateFailure(string? errorMessage, long totalFrames, double fps, TimeSpan renderDuration)
{
var videoDuration = fps > 0 ? totalFrames / fps : 0.0;
return new RenderResult(false, string.Empty, totalFrames, videoDuration, renderDuration, 0, errorMessage ?? "Rendering failed or was cancelled.");
}
/// <summary>
/// Returns a string summarizing the render result metrics or failure reason.
/// </summary>
/// <returns>A string representation of the <see cref="RenderResult"/>.</returns>
public override string ToString()
{
return Success
? $"[RenderResult: Success, Output={OutputPath}, Frames={TotalFrames}, VideoDuration={VideoDuration:F2}s, Time={RenderDuration.TotalSeconds:F1}s, Size={FileSizeBytes / 1024.0 / 1024.0:F2}MB]"
: $"[RenderResult: Failed/Cancelled, Error={ErrorMessage ?? "Unknown"}]";
}
}