d5e827112a
- Created PROTOCOL.md to outline the MusicBridge protocol v1, detailing setup, TLS, pairing, state management, and command structure. - Implemented SystemVolume class for managing system audio levels, including methods for setting, reading, changing, and muting volume. - Added unit tests for the MusicBridge agent, covering pairing, identity storage, command validation, and media state management. - Included tests for real-time media reading from Windows and ensured proper handling of artwork caching and retrieval.
59 lines
2.7 KiB
C#
59 lines
2.7 KiB
C#
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<MediaState> ReadAsync(CancellationToken cancellationToken);
|
|
Task<CommandResult> 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);
|
|
}
|