264 lines
8.9 KiB
C#
264 lines
8.9 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace PlotLine.Models;
|
|
|
|
public static class IllustrationLibraryCategories
|
|
{
|
|
public const string Character = "Character";
|
|
public const string Location = "Location";
|
|
public const string Asset = "Asset";
|
|
|
|
public static readonly IReadOnlyList<string> All = [Character, Location, Asset];
|
|
|
|
public static bool IsValid(string? category)
|
|
=> All.Any(item => string.Equals(item, category, StringComparison.OrdinalIgnoreCase));
|
|
|
|
public static string StorageFolder(string category)
|
|
=> category switch
|
|
{
|
|
Character => "characters",
|
|
Location => "locations",
|
|
Asset => "assets",
|
|
_ => throw new ArgumentOutOfRangeException(nameof(category), category, "Unknown illustration category.")
|
|
};
|
|
}
|
|
|
|
public static class IllustrationLibraryStatuses
|
|
{
|
|
public const string Planned = "Planned";
|
|
public const string Queued = "Queued";
|
|
public const string Generating = "Generating";
|
|
public const string Generated = "Generated";
|
|
public const string Approved = "Approved";
|
|
public const string Rejected = "Rejected";
|
|
public const string Failed = "Failed";
|
|
public const string Superseded = "Superseded";
|
|
public const string Disabled = "Disabled";
|
|
|
|
public static readonly IReadOnlySet<string> All = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
Planned,
|
|
Queued,
|
|
Generating,
|
|
Generated,
|
|
Approved,
|
|
Rejected,
|
|
Failed,
|
|
Superseded,
|
|
Disabled
|
|
};
|
|
|
|
public static bool IsValid(string? status)
|
|
=> !string.IsNullOrWhiteSpace(status) && All.Contains(status);
|
|
|
|
public static bool CanTransition(string from, string to)
|
|
=> (from, to) switch
|
|
{
|
|
(Planned, Queued) => true,
|
|
(Queued, Generating) => true,
|
|
(Generating, Generated) => true,
|
|
(Generating, Failed) => true,
|
|
(Generated, Approved) => true,
|
|
(Generated, Rejected) => true,
|
|
(Approved, Superseded) => true,
|
|
(Rejected, Queued) => true,
|
|
(Failed, Queued) => true,
|
|
(Approved, Disabled) => true,
|
|
(Disabled, Queued) => true,
|
|
_ when string.Equals(from, to, StringComparison.OrdinalIgnoreCase) => true,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public sealed class IllustrationGenerationSpecification
|
|
{
|
|
public string Category { get; init; } = string.Empty;
|
|
public string Code { get; init; } = string.Empty;
|
|
public string Title { get; init; } = string.Empty;
|
|
public string Description { get; init; } = string.Empty;
|
|
public string VisualSubject { get; init; } = string.Empty;
|
|
public string Composition { get; init; } = string.Empty;
|
|
public string Mood { get; init; } = string.Empty;
|
|
public string? ApparentAgeBand { get; init; }
|
|
public string? Presentation { get; init; }
|
|
public string? HairColour { get; init; }
|
|
public string? HairLength { get; init; }
|
|
public string? SkinTone { get; init; }
|
|
public string? ClothingEra { get; init; }
|
|
public string? ClothingStyle { get; init; }
|
|
public string? LocationType { get; init; }
|
|
public string? Era { get; init; }
|
|
public string? Setting { get; init; }
|
|
public string? ObjectType { get; init; }
|
|
public string? Colour { get; init; }
|
|
public IReadOnlyList<string> Features { get; init; } = [];
|
|
public IReadOnlyList<string> Palette { get; init; } = [];
|
|
public IReadOnlyList<string> MetadataTags { get; init; } = [];
|
|
public Dictionary<string, string> Metadata { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public IReadOnlyList<string> Validate()
|
|
{
|
|
var errors = new List<string>();
|
|
if (!IllustrationLibraryCategories.IsValid(Category))
|
|
{
|
|
errors.Add("Category must be Character, Location or Asset.");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(Code))
|
|
{
|
|
errors.Add("Stable code is required.");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(Title))
|
|
{
|
|
errors.Add("Title is required.");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(Mood))
|
|
{
|
|
errors.Add("Mood is required.");
|
|
}
|
|
|
|
if (Category == IllustrationLibraryCategories.Character)
|
|
{
|
|
Require(ApparentAgeBand, "Apparent age band is required for characters.");
|
|
Require(Presentation, "Presentation is required for characters.");
|
|
}
|
|
else if (Category == IllustrationLibraryCategories.Location)
|
|
{
|
|
Require(LocationType, "Location type is required for locations.");
|
|
}
|
|
else if (Category == IllustrationLibraryCategories.Asset)
|
|
{
|
|
Require(ObjectType, "Object type is required for assets.");
|
|
}
|
|
|
|
return errors;
|
|
|
|
void Require(string? value, string message)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
errors.Add(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class IllustrationLibraryItem
|
|
{
|
|
public int IllustrationLibraryItemID { get; set; }
|
|
public string Category { get; set; } = string.Empty;
|
|
public string StableCode { get; set; } = string.Empty;
|
|
public string DisplayTitle { get; set; } = string.Empty;
|
|
public string? ShortDescription { get; set; }
|
|
public string? MetadataJson { get; set; }
|
|
public string? SpecificationJson { get; set; }
|
|
public string? FilePath { get; set; }
|
|
public string? ThumbnailPath { get; set; }
|
|
public int? Width { get; set; }
|
|
public int? Height { get; set; }
|
|
public long? FileSizeBytes { get; set; }
|
|
public string? MimeType { get; set; }
|
|
public string Status { get; set; } = IllustrationLibraryStatuses.Planned;
|
|
public bool IsActive { get; set; }
|
|
public bool IsApproved { get; set; }
|
|
public DateTime CreatedUtc { get; set; }
|
|
public DateTime UpdatedUtc { get; set; }
|
|
public DateTime? ApprovedUtc { get; set; }
|
|
public int? ApprovedByUserID { get; set; }
|
|
public string? Provider { get; set; }
|
|
public string? Model { get; set; }
|
|
public string? PromptText { get; set; }
|
|
public string? Prompt { get; set; }
|
|
public string? PromptTemplateVersion { get; set; }
|
|
public string? ProviderRequestID { get; set; }
|
|
public int AttemptCount { get; set; }
|
|
public int GenerationAttemptCount { get; set; }
|
|
public int? ParentItemID { get; set; }
|
|
public string? RejectionReason { get; set; }
|
|
public DateTime? RejectedUtc { get; set; }
|
|
public int? RejectedByUserID { get; set; }
|
|
public string? AdminNotes { get; set; }
|
|
public int UsageCount { get; set; }
|
|
public string? LastError { get; set; }
|
|
public string? ErrorMessage { get; set; }
|
|
public DateTime? QueuedUtc { get; set; }
|
|
public DateTime? StartedUtc { get; set; }
|
|
public DateTime? CompletedUtc { get; set; }
|
|
}
|
|
|
|
public sealed class IllustrationLibrarySummary
|
|
{
|
|
public int TotalCount { get; set; }
|
|
public int PlannedCount { get; set; }
|
|
public int QueuedCount { get; set; }
|
|
public int GeneratingCount { get; set; }
|
|
public int GeneratedCount { get; set; }
|
|
public int ApprovedCount { get; set; }
|
|
public int RejectedCount { get; set; }
|
|
public int FailedCount { get; set; }
|
|
public int SupersededCount { get; set; }
|
|
public int DisabledCount { get; set; }
|
|
}
|
|
|
|
public sealed class IllustrationLibraryFilter
|
|
{
|
|
public string? Category { get; init; }
|
|
public string? Status { get; init; }
|
|
public string? Search { get; init; }
|
|
}
|
|
|
|
public sealed record IllustrationPromptBuildResult(
|
|
string Prompt,
|
|
string TemplateVersion,
|
|
string SpecificationJson,
|
|
string MetadataJson);
|
|
|
|
public sealed record IllustrationImageGenerationRequest(
|
|
int IllustrationLibraryItemID,
|
|
string StableCode,
|
|
string Category,
|
|
string Prompt,
|
|
string TemplateVersion);
|
|
|
|
public sealed record IllustrationImageGenerationResult(
|
|
bool Succeeded,
|
|
byte[]? ImageBytes = null,
|
|
string? MimeType = null,
|
|
string? Provider = null,
|
|
string? Model = null,
|
|
string? ProviderRequestID = null,
|
|
string? ErrorMessage = null)
|
|
{
|
|
[JsonIgnore]
|
|
public bool HasImage => ImageBytes is { Length: > 0 };
|
|
}
|
|
|
|
public sealed record IllustrationStoredImage(
|
|
string FilePath,
|
|
string ThumbnailPath,
|
|
int Width,
|
|
int Height,
|
|
long FileSizeBytes,
|
|
string MimeType);
|
|
|
|
public sealed record IllustrationLibraryActionResult(bool Succeeded, string Message);
|
|
|
|
public sealed record IllustrationBulkRetryResult(
|
|
int QueuedCount,
|
|
int SkippedCount,
|
|
int RetryLimitReachedCount)
|
|
{
|
|
public string Message
|
|
=> $"{QueuedCount:N0} failed illustration(s) queued. {SkippedCount:N0} skipped. {RetryLimitReachedCount:N0} reached the retry limit.";
|
|
}
|
|
|
|
public sealed record IllustrationStarterGenerationPlan(
|
|
int CharactersToGenerate,
|
|
int LocationsToGenerate,
|
|
int AssetsToGenerate)
|
|
{
|
|
public int TotalToGenerate => CharactersToGenerate + LocationsToGenerate + AssetsToGenerate;
|
|
}
|