Add MusicBridge protocol documentation and implement system volume control
- 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.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MusicBridge.Agent.Network;
|
||||
|
||||
// QR payload contract only. Rendering/scanning and the native client are separate steps.
|
||||
internal sealed record ConnectionInvitation(int Version, string Endpoint, string CertificateSha256,
|
||||
string Code, DateTimeOffset ExpiresAt)
|
||||
{
|
||||
public static ConnectionInvitation Create(Uri endpoint, string fingerprint, PairingWindow window, DateTimeOffset now)
|
||||
{
|
||||
if (!IsLocalEndpoint(endpoint)) throw new ArgumentException("Нужен HTTPS IPv4-адрес домашней сети без пути и параметров.");
|
||||
if (fingerprint.Length != 64 || !fingerprint.All(Uri.IsHexDigit)) throw new ArgumentException("Неверный отпечаток сертификата.");
|
||||
if (window.Code.Length != 8 || !window.Code.All(char.IsAsciiDigit) || window.ExpiresAt <= now)
|
||||
throw new ArgumentException("Окно сопряжения закрыто или код неверен.");
|
||||
return new(1, endpoint.GetLeftPart(UriPartial.Authority), fingerprint.ToUpperInvariant(), window.Code, window.ExpiresAt);
|
||||
}
|
||||
|
||||
public string ToQrPayload()
|
||||
{
|
||||
var json = JsonSerializer.SerializeToUtf8Bytes(this, new JsonSerializerOptions(JsonSerializerDefaults.Web));
|
||||
var data = Convert.ToBase64String(json).TrimEnd('=').Replace('+', '-').Replace('/', '_');
|
||||
return "musicbridge://pair?data=" + data;
|
||||
}
|
||||
|
||||
public static bool IsLocalEndpoint(Uri endpoint)
|
||||
{
|
||||
if (!endpoint.IsAbsoluteUri || endpoint.Scheme != "https" || endpoint.UserInfo != ""
|
||||
|| endpoint.AbsolutePath != "/" || endpoint.Query != "" || endpoint.Fragment != ""
|
||||
|| !IPAddress.TryParse(endpoint.Host, out var ip)) return false;
|
||||
var bytes = ip.GetAddressBytes();
|
||||
return bytes.Length == 4 && (bytes[0] == 10 || bytes[0] == 172 && bytes[1] is >= 16 and <= 31
|
||||
|| bytes[0] == 192 && bytes[1] == 168);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user