Files
windows/Network/ConnectionInvitation.cs
ros d5e827112a 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.
2026-09-09 01:05:52 +03:00

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);
}
}