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.
36 lines
1.9 KiB
C#
36 lines
1.9 KiB
C#
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);
|
|
}
|
|
}
|