Implement QR pairing window and mDNS announcements; update documentation and tests

This commit is contained in:
2026-09-09 09:53:37 +03:00
parent d5e827112a
commit 2da3c9544b
7 changed files with 321 additions and 61 deletions
+26 -18
View File
@@ -2,6 +2,7 @@ using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using MusicBridge.Agent;
using MusicBridge.Agent.Media;
using MusicBridge.Agent.Network;
using Windows.Media.Control;
@@ -39,16 +40,9 @@ try
using var hostStopping = server.Stopping.Register(() => shutdown.Cancel());
var fingerprint = certificate.GetCertHashString(HashAlgorithmName.SHA256);
var addresses = new List<string> { $"https://127.0.0.1:{port}" };
if (lan)
{
try
{
addresses.AddRange(Dns.GetHostAddresses(Dns.GetHostName())
.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip))
.Select(ip => $"https://{ip}:{port}"));
}
catch (SocketException) { }
}
Uri[] qrEndpoints = [];
using var mdns = new MdnsPublisher(fingerprint, port);
using var qrWindow = new PairingWindowHost(pairing, fingerprint, () => Volatile.Read(ref qrEndpoints));
var message = "Готов к управлению.";
IReadOnlyList<TrustedDevice> displayedDevices = [];
var nextRefresh = DateTimeOffset.MinValue;
@@ -73,13 +67,8 @@ try
}
else if (key == ConsoleKey.I)
{
var invitationTime = DateTimeOffset.UtcNow;
var window = pairing.Current;
var endpoints = addresses.Select(a => new Uri(a)).Where(ConnectionInvitation.IsLocalEndpoint).ToArray();
message = window is null ? "Сначала нажми B, чтобы открыть сопряжение."
: endpoints.Length == 0 ? "Для данных подключения запусти агент с --lan в домашней сети."
: string.Join(Environment.NewLine, endpoints.Select(endpoint =>
$"Данные для будущего QR ({endpoint.Host}): {ConnectionInvitation.Create(endpoint, fingerprint, window, invitationTime).ToQrPayload()}"));
qrWindow.Open();
message = "Окно QR открыто. Для нового кода нажми B.";
}
else if (key == ConsoleKey.B)
{
@@ -107,6 +96,23 @@ try
}
if (DateTimeOffset.UtcNow >= nextRefresh)
{
if (lan)
{
try
{
var ips = LocalAddresses.Read();
Volatile.Write(ref qrEndpoints, ips.Select(ip => new Uri($"https://{ip}:{port}")).ToArray());
addresses = new List<string> { $"https://127.0.0.1:{port}" };
addresses.AddRange(qrEndpoints.Select(u => u.GetLeftPart(UriPartial.Authority)));
mdns.Refresh(ips);
}
catch (Exception ex)
{
Volatile.Write(ref qrEndpoints, []);
mdns.Refresh([]);
message = $"Не удалось обновить адреса: {ex.Message}";
}
}
if (!Console.IsOutputRedirected) Console.Clear();
var state = server.Latest;
Console.WriteLine("MUSICBRIDGE\n");
@@ -126,6 +132,8 @@ try
else Console.WriteLine($"Громкость недоступна: {state.VolumeError}");
Console.WriteLine($"\nПодключение: {string.Join(" | ", addresses)}");
Console.WriteLine(lan ? "Режим локальной сети (IPv4)." : "Режим только этого ПК; для Wi-Fi запусти с --lan.");
if (lan) Console.WriteLine(mdns.Status);
if (qrWindow.Error is { } qrError) Console.WriteLine($"Окно QR: {qrError}");
Console.WriteLine($"SHA-256 сертификата: {fingerprint}");
var window = pairing.Current;
Console.WriteLine(window is null ? "Сопряжение закрыто. B — открыть заново."
@@ -136,7 +144,7 @@ try
Console.WriteLine($" {i + 1}. {displayedDevices[i].Name}");
Console.WriteLine("\nПробел: play/pause | N/P: следующий/предыдущий трек");
Console.WriteLine("←/→: ±10 с | ↑/↓: громкость ±5% | M: звук | B: сопряжение");
Console.WriteLine("I: данные для будущего QR | Выход: Q или Ctrl+C");
Console.WriteLine("I: открыть QR-код | Выход: Q или Ctrl+C");
Console.WriteLine($"\n{message}");
nextRefresh = DateTimeOffset.UtcNow.AddSeconds(1);
}