Files
fragalytics/src/Fragalytics.Web/Models/AppState.cs
T
2026-04-21 15:57:09 +00:00

297 lines
10 KiB
C#

namespace Fragalytics.Web.Models;
public static class DemoStatuses
{
public const string Uploaded = "uploaded";
public const string Queued = "queued";
public const string Processing = "processing";
public const string Parsed = "parsed";
public const string Failed = "failed";
}
public static class ClipJobStatuses
{
public const string Pending = "pending";
public const string Rendering = "rendering";
public const string Rendered = "rendered";
public const string Failed = "failed";
}
public static class EventTypes
{
public const string Kill = "kill";
}
public static class ClipMediaFormats
{
public const string Mp4Extension = ".mp4";
public const string WebMExtension = ".webm";
public const string Mp4ContentType = "video/mp4";
public const string WebMContentType = "video/webm";
public static readonly HashSet<string> AllowedExtensions = new(StringComparer.OrdinalIgnoreCase)
{
Mp4Extension,
WebMExtension
};
public static string InferContentType(string extension)
{
return extension.Equals(WebMExtension, StringComparison.OrdinalIgnoreCase)
? WebMContentType
: Mp4ContentType;
}
public static string GetSuggestedFileExtension(string outputFormat)
{
return outputFormat.Equals("webm", StringComparison.OrdinalIgnoreCase)
? WebMExtension
: Mp4Extension;
}
}
public sealed class AppState
{
public int NextDemoId { get; set; } = 1;
public int NextMatchId { get; set; } = 1;
public int NextPlayerId { get; set; } = 1;
public int NextRoundId { get; set; } = 1;
public int NextEventId { get; set; } = 1;
public int NextHighlightId { get; set; } = 1;
public int NextClipJobId { get; set; } = 1;
public int NextClipAssetId { get; set; } = 1;
public int NextScoringProfileId { get; set; } = 1;
public List<DemoRecord> Demos { get; set; } = [];
public List<MatchRecord> Matches { get; set; } = [];
public List<PlayerRecord> Players { get; set; } = [];
public List<RoundRecord> Rounds { get; set; } = [];
public List<EventRecord> Events { get; set; } = [];
public List<HighlightRecord> Highlights { get; set; } = [];
public List<ClipJobRecord> ClipJobs { get; set; } = [];
public List<ClipAssetRecord> ClipAssets { get; set; } = [];
public List<ScoringProfileRecord> ScoringProfiles { get; set; } = [];
}
public sealed class DemoRecord
{
public int Id { get; set; }
public string OriginalFileName { get; set; } = string.Empty;
public string StoredPath { get; set; } = string.Empty;
public string ParserBackend { get; set; } = string.Empty;
public string Status { get; set; } = DemoStatuses.Uploaded;
public string? ErrorMessage { get; set; }
public DateTime UploadedAtUtc { get; set; } = DateTime.UtcNow;
public DateTime? ProcessedAtUtc { get; set; }
public int? MatchId { get; set; }
}
public sealed class MatchRecord
{
public int Id { get; set; }
public int DemoId { get; set; }
public string? MapName { get; set; }
public string? ServerName { get; set; }
public int? TickRate { get; set; }
public int TotalRounds { get; set; }
public string? TeamCtName { get; set; }
public string? TeamTName { get; set; }
public DateTime ParsedAtUtc { get; set; } = DateTime.UtcNow;
}
public sealed class PlayerRecord
{
public int Id { get; set; }
public int MatchId { get; set; }
public string SteamId { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string? TeamName { get; set; }
public string? Side { get; set; }
}
public sealed class RoundRecord
{
public int Id { get; set; }
public int MatchId { get; set; }
public int Number { get; set; }
public int? StartTick { get; set; }
public int? EndTick { get; set; }
public string? WinnerSide { get; set; }
public string? WinnerTeamName { get; set; }
public string? Reason { get; set; }
}
public sealed class EventRecord
{
public int Id { get; set; }
public int MatchId { get; set; }
public int? RoundId { get; set; }
public string EventType { get; set; } = EventTypes.Kill;
public int Tick { get; set; }
public double? Second { get; set; }
public int? ActorPlayerId { get; set; }
public int? TargetPlayerId { get; set; }
public string? Weapon { get; set; }
public bool IsHeadshot { get; set; }
public bool IsWallbang { get; set; }
public bool IsSmokeKill { get; set; }
public int? KillerAliveCount { get; set; }
public int? EnemyAliveCount { get; set; }
public string? KillerNameSnapshot { get; set; }
public string? VictimNameSnapshot { get; set; }
}
public sealed class HighlightRecord
{
public int Id { get; set; }
public int MatchId { get; set; }
public int? RoundId { get; set; }
public int SourceEventId { get; set; }
public double Score { get; set; }
public List<string> Tags { get; set; } = [];
public List<ScoreExplanationRecord> Explanation { get; set; } = [];
public string ScoringVersion { get; set; } = string.Empty;
public int StartTick { get; set; }
public int EndTick { get; set; }
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
}
public sealed class ScoreExplanationRecord
{
public string Reason { get; set; } = string.Empty;
public double Value { get; set; }
public string Detail { get; set; } = string.Empty;
}
public sealed class ClipJobRecord
{
public int Id { get; set; }
public int HighlightId { get; set; }
public string Renderer { get; set; } = RenderBackendNames.FutureRenderer;
public string Status { get; set; } = ClipJobStatuses.Pending;
public int StartTick { get; set; }
public int EndTick { get; set; }
public Dictionary<string, string> Payload { get; set; } = [];
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
public int? ClipAssetId { get; set; }
public int AttemptCount { get; set; }
public int MaxAttempts { get; set; } = 2;
public string? ErrorMessage { get; set; }
public DateTime? LastAttemptedAtUtc { get; set; }
public DateTime? RetryAfterUtc { get; set; }
public DateTime? StartedAtUtc { get; set; }
public DateTime? CompletedAtUtc { get; set; }
}
public sealed class RenderJobContract
{
public const string CurrentVersion = "1";
public const string DefaultCameraMode = "killer_pov";
public const string DefaultOutputFormat = "mp4";
public const string DefaultResolution = "1920x1080";
public const string DefaultFps = "60";
public string Version { get; set; } = CurrentVersion;
public int DemoId { get; set; }
public int MatchId { get; set; }
public int HighlightId { get; set; }
public int SourceEventId { get; set; }
public int StartTick { get; set; }
public int EndTick { get; set; }
public string CameraMode { get; set; } = DefaultCameraMode;
public string OutputFormat { get; set; } = DefaultOutputFormat;
public string Resolution { get; set; } = DefaultResolution;
public string Fps { get; set; } = DefaultFps;
public Dictionary<string, string> ToPayload()
{
return new Dictionary<string, string>
{
["version"] = Version,
["demoId"] = DemoId.ToString(),
["matchId"] = MatchId.ToString(),
["highlightId"] = HighlightId.ToString(),
["sourceEventId"] = SourceEventId.ToString(),
["startTick"] = StartTick.ToString(),
["endTick"] = EndTick.ToString(),
["cameraMode"] = CameraMode,
["outputFormat"] = OutputFormat,
["resolution"] = Resolution,
["fps"] = Fps
};
}
public static RenderJobContract FromPayload(IReadOnlyDictionary<string, string> payload)
{
return new RenderJobContract
{
Version = ReadString(payload, "version", CurrentVersion),
DemoId = ReadInt(payload, "demoId"),
MatchId = ReadInt(payload, "matchId"),
HighlightId = ReadInt(payload, "highlightId"),
SourceEventId = ReadInt(payload, "sourceEventId"),
StartTick = ReadInt(payload, "startTick"),
EndTick = ReadInt(payload, "endTick"),
CameraMode = ReadString(payload, "cameraMode", DefaultCameraMode),
OutputFormat = ReadString(payload, "outputFormat", DefaultOutputFormat),
Resolution = ReadString(payload, "resolution", DefaultResolution),
Fps = ReadString(payload, "fps", DefaultFps)
};
}
public string GetSuggestedFileExtension()
{
return ClipMediaFormats.GetSuggestedFileExtension(OutputFormat);
}
private static int ReadInt(IReadOnlyDictionary<string, string> payload, string key)
{
return payload.TryGetValue(key, out var raw) && int.TryParse(raw, out var value) ? value : 0;
}
private static string ReadString(IReadOnlyDictionary<string, string> payload, string key, string fallback)
{
return payload.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value) ? value : fallback;
}
}
public sealed class ClipAssetRecord
{
public int Id { get; set; }
public int ClipJobId { get; set; }
public string FileName { get; set; } = string.Empty;
public string StoredPath { get; set; } = string.Empty;
public string ContentType { get; set; } = ClipMediaFormats.Mp4ContentType;
public long FileSizeBytes { get; set; }
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
}
public sealed class ScoringProfileRecord
{
public int Id { get; set; }
public string Name { get; set; } = "default";
public string Version { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
public HighlightScoringRules Rules { get; set; } = new();
}
public sealed class HighlightScoringRules
{
public string Name { get; set; } = "default";
public Dictionary<string, double> Weights { get; set; } = new();
public ClipWindowRules Clip { get; set; } = new();
public ScoreThresholdRules Thresholds { get; set; } = new();
}
public sealed class ClipWindowRules
{
public int PreTicks { get; set; } = 160;
public int PostTicks { get; set; } = 96;
}
public sealed class ScoreThresholdRules
{
public double HighlightScoreMin { get; set; } = 18;
}