namespace MusicBridge.Agent.Media; internal sealed record MediaCommand(string Type, double? Value = null, string? Id = null); internal sealed record CommandResult(bool Success, string Code, string Message, string? Id = null); internal sealed record PlayerCapabilities(bool Play = false, bool Pause = false, bool Toggle = false, bool Next = false, bool Previous = false, bool Seek = false); internal sealed record MediaState { public int ProtocolVersion { get; init; } = 1; public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow; public bool HasSession { get; init; } public string Title { get; init; } = ""; public string Artist { get; init; } = ""; public string Source { get; init; } = ""; public string PlaybackStatus { get; init; } = "Closed"; public double PositionSeconds { get; init; } public double StartSeconds { get; init; } public double EndSeconds { get; init; } public double MinSeekSeconds { get; init; } public double MaxSeekSeconds { get; init; } public double PlaybackRate { get; init; } = 1; public PlayerCapabilities Capabilities { get; init; } = new(); public float? Volume { get; init; } public bool? Muted { get; init; } public string? MediaError { get; init; } public string? VolumeError { get; init; } public string? ArtworkId { get; init; } public string? ArtworkError { get; init; } } internal interface IMediaService { event Action? Changed; Artwork? GetArtwork(string id); Task ReadAsync(CancellationToken cancellationToken); Task ExecuteAsync(MediaCommand command, CancellationToken cancellationToken); } internal static class CommandValidation { public static CommandResult? Validate(MediaCommand command) { if (command.Id?.Length > 64) return Error("invalid_command", "Id длиннее 64 символов."); var valid = command.Type switch { "play" or "pause" or "toggle" or "next" or "previous" or "toggleMute" => command.Value is null, "seek" => command.Value is double s && double.IsFinite(s) && s >= 0 && s <= TimeSpan.MaxValue.TotalSeconds / 2, "seekRelative" => command.Value is double d && double.IsFinite(d) && Math.Abs(d) <= 3600, "volume" => command.Value is double v && double.IsFinite(v) && v >= 0 && v <= 1, "volumeRelative" => command.Value is double d && double.IsFinite(d) && Math.Abs(d) <= 1, _ => false }; return valid ? null : Error("invalid_command", "Неизвестная команда или недопустимое значение."); } public static CommandResult Error(string code, string message) => new(false, code, message); }