diff --git a/Readme.md b/Readme.md index 0806d10c..1644b84e 100644 --- a/Readme.md +++ b/Readme.md @@ -4,3 +4,15 @@ A simple, zero-config DLNA media server, that you can just fire up and be done w See [the github page](http://nmaier.github.io/simpleDLNA/) for more details and downloads. + + +What's new +---------- + +- Added `--uuid` command-line option to set a static server UUID, e.g.: + + ``` + sdlna.exe --uuid 12345678-1234-1234-1234-123456789abc D:\Media + ``` + +- Device UUIDs are now derived deterministically from the server UUID and the IP address. This keeps device identifiers stable across restarts as long as the server UUID and IP remain the same. \ No newline at end of file diff --git a/fsserver/FileServer.cs b/fsserver/FileServer.cs index 4c7d49d4..7aadce70 100644 --- a/fsserver/FileServer.cs +++ b/fsserver/FileServer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.IO; using System.Linq; @@ -57,6 +57,8 @@ public sealed class FileServer private FileStore store; + private Guid uuid; + public FileServer(DlnaMediaTypes types, Identifiers ids, params DirectoryInfo[] directories) { @@ -80,6 +82,13 @@ public FileServer(DlnaMediaTypes types, Identifiers ids, UUID = DeriveUUID(); } + public FileServer(DlnaMediaTypes types, Identifiers ids, Guid fixedUuid, + params DirectoryInfo[] directories) + : this(types, ids, directories) + { + UUID = fixedUuid; + } + internal ExtensionFilter Filter { get; } public void Dispose() @@ -98,7 +107,7 @@ public void Dispose() public string FriendlyName { get; set; } // ReSharper disable once MemberInitializerValueIgnored - public Guid UUID { get; } = Guid.NewGuid(); + public Guid UUID { get; private set; } = Guid.NewGuid(); public IMediaItem GetItem(string id) { diff --git a/sdlna/Options.cs b/sdlna/Options.cs index 1494c026..8b542513 100644 --- a/sdlna/Options.cs +++ b/sdlna/Options.cs @@ -65,6 +65,30 @@ internal class Options : GetOpt [Argument("view", HelpText = "Apply a view (default: no views applied)", HelpVar = "view")] [ShortArgument('v')] public string[] Views = new string[0]; + private Guid? uuid; + + [Argument("uuid", HelpText = "Static server UUID (e.g. 12345678-1234-1234-1234-123456789abc)", HelpVar = "uuid")] + public string Uuid + { + get { return uuid?.ToString(); } + set { + if (string.IsNullOrWhiteSpace(value)) { + uuid = null; + return; + } + Guid parsed; + if (!Guid.TryParse(value, out parsed)) { + throw new GetOptException($"Not a valid UUID: {value}"); + } + uuid = parsed; + } + } + + public Guid? ParsedUuid + { + get { return uuid; } + } + [Argument("ip", HelpText = "Allow only specified IPs", HelpVar = "IP")] [ShortArgument('i')] public string[] Ips diff --git a/sdlna/Program.cs b/sdlna/Program.cs index 78a39522..5995e166 100644 --- a/sdlna/Program.cs +++ b/sdlna/Program.cs @@ -189,7 +189,13 @@ private static FileServer SetupFileServer(Options options, throw new GetOptException("Invalid view " + v); } } - var fs = new FileServer(types, ids, d); + FileServer fs; + if (options.ParsedUuid.HasValue) { + fs = new FileServer(types, ids, options.ParsedUuid.Value, d); + } + else { + fs = new FileServer(types, ids, d); + } try { if (!string.IsNullOrEmpty(options.FriendlyName)) { fs.FriendlyName = options.FriendlyName; diff --git a/server/Http/HTTPServer.cs b/server/Http/HTTPServer.cs index b18ba497..0db10fd5 100644 --- a/server/Http/HTTPServer.cs +++ b/server/Http/HTTPServer.cs @@ -257,7 +257,7 @@ public void RegisterMediaServer(IMediaServer server) foreach (var address in IP.ExternalIPAddresses) { DebugFormat("Registering device for {0}", address); - var deviceGuid = Guid.NewGuid(); + var deviceGuid = DeriveDeviceUuid(guid, address); var list = devicesForServers.GetOrAdd(guid, new List()); lock (list) { list.Add(deviceGuid); @@ -271,6 +271,23 @@ public void RegisterMediaServer(IMediaServer server) } } + private static Guid DeriveDeviceUuid(Guid serverUuid, IPAddress address) + { + // Стабильный UUID на основе UUID сервера и адреса устройства (per-IP) + var serverBytes = serverUuid.ToByteArray(); + var addrBytes = address.GetAddressBytes(); + var combined = new byte[16]; + for (var i = 0; i < 16; i++) { + var sb = serverBytes[i]; + var ab = addrBytes[i % addrBytes.Length]; + combined[i] = (byte)(sb ^ ab); + } + // Установка версии 4 и варианта RFC 4122 для корректной формы UUID + combined[6] = (byte)((combined[6] & 0x0F) | 0x40); + combined[8] = (byte)((combined[8] & 0x3F) | 0x80); + return new Guid(combined); + } + public void UnregisterMediaServer(IMediaServer server) { if (server == null) {