From 4e2263c5907e4639d317cc34e5c4146becbd1754 Mon Sep 17 00:00:00 2001 From: Jonas Rabenstein Date: Sat, 20 Sep 2025 00:14:21 +0200 Subject: [PATCH] plugin: dlna-8.0.0.0 --- plugin/dlna/default.nix | 21 ++++ plugin/dlna/deps.json | 192 +++++++++++++++++++++++++++++++++ plugin/dlna/xml-sanitize.patch | 125 +++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 plugin/dlna/default.nix create mode 100644 plugin/dlna/deps.json create mode 100644 plugin/dlna/xml-sanitize.patch diff --git a/plugin/dlna/default.nix b/plugin/dlna/default.nix new file mode 100644 index 0000000..62c1bd1 --- /dev/null +++ b/plugin/dlna/default.nix @@ -0,0 +1,21 @@ +let + v."8.0.0.0" = { + hash = "sha256-5YUX+w4n3nBhAkdgjF9D5yY/jzRKxpW+mTQCBluzsVI="; + rev = "v8"; + }; + current = + lib: + lib.lists.fold (acc: v: if lib.strings.versionOlder acc v then v else acc) "0.0.0" ( + builtins.attrNames v + ); +in +{ + lib, + version ? current lib, + hash ? v.${version}.hash or "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + rev ? v.${version}.rev or "v${version}", + ... +}: +{ + inherit version hash rev; +} diff --git a/plugin/dlna/deps.json b/plugin/dlna/deps.json new file mode 100644 index 0000000..e6de4d7 --- /dev/null +++ b/plugin/dlna/deps.json @@ -0,0 +1,192 @@ +[ + { + "pname": "Diacritics", + "version": "3.3.29", + "hash": "sha256-sIbdJ3yMthnmJHly3WheUdYjtwPakcczTJx9ycxtgrY=" + }, + { + "pname": "ICU4N", + "version": "60.1.0-alpha.356", + "hash": "sha256-1QyOgO7pNMeoEgBtl6o8IG4o91wD2hFUgRI0jM0ltxY=" + }, + { + "pname": "ICU4N.Transliterator", + "version": "60.1.0-alpha.356", + "hash": "sha256-RLNwQNVqNz8Omfb/mC/rzQWVq8c7uCnNdG2qi4xJmds=" + }, + { + "pname": "J2N", + "version": "2.0.0", + "hash": "sha256-YvtIWErlm2O49hg3lIRm5Ha8/owkQkfMudzuldC3EhA=" + }, + { + "pname": "Jellyfin.Common", + "version": "10.10.7", + "hash": "sha256-9EIigrDheob4vRP+UBAoIPHH4fyz6Cl27GUpelEGpBg=" + }, + { + "pname": "Jellyfin.Controller", + "version": "10.10.7", + "hash": "sha256-/obWAuxWpSn+NlMES+fjyrf1g+qbxmSYQUBvgXQYltQ=" + }, + { + "pname": "Jellyfin.Data", + "version": "10.10.7", + "hash": "sha256-lRQjg/HkFAtCN0woL1gX6j5dMfVLP/fzQTKjFTcRth4=" + }, + { + "pname": "Jellyfin.Extensions", + "version": "10.10.7", + "hash": "sha256-AOGJ2IoT2v+LnlEqnb2O4FnXapcQiH0V9ny8GUysNHg=" + }, + { + "pname": "Jellyfin.Model", + "version": "10.10.7", + "hash": "sha256-ubsClGTLq/aFnMiBCaHVUX2b88beuL07Yn5VXNbTCjQ=" + }, + { + "pname": "Jellyfin.Naming", + "version": "10.10.7", + "hash": "sha256-/jlIS0X4FuBo6Ac1cjLGzgdJ3vR6VotBspxpNrqfMzo=" + }, + { + "pname": "Microsoft.AspNetCore.Authorization", + "version": "8.0.10", + "hash": "sha256-VeUAe/OoV2zNDaiSKSv7tXR5barJzLbxS96DUb9bAz8=" + }, + { + "pname": "Microsoft.AspNetCore.Metadata", + "version": "8.0.10", + "hash": "sha256-SxnMOWJGgUUQyKaRezJQwMUt4eMfWjnhmfk8pldYGNA=" + }, + { + "pname": "Microsoft.Extensions.Caching.Abstractions", + "version": "2.0.0", + "hash": "sha256-Eg1MES40kzkGW9tZmjaKtbWI00Kbv7fLJQmjrigjxqk=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "2.0.0", + "hash": "sha256-1fnNvp62KrviVwYlqVl1CbdaZVpCDah9eCZeNDGDbWM=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "8.0.0", + "hash": "sha256-4eBpDkf7MJozTZnOwQvwcfgRKQGcNXe0K/kF+h5Rl8o=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Binder", + "version": "8.0.2", + "hash": "sha256-aGB0VuoC34YadAEqrwoaXLc5qla55pswDV2xLSmR7SE=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "8.0.1", + "hash": "sha256-O9g0jWS+jfGoT3yqKwZYJGL+jGSIeSbwmvomKDC3hTU=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "2.0.0", + "hash": "sha256-H1rEnq/veRWvmp8qmUsrQkQIcVlKilUNzmmKsxJ0md8=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "8.0.0", + "hash": "sha256-75KzEGWjbRELczJpCiJub+ltNUMMbz5A/1KQU+5dgP8=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "8.0.2", + "hash": "sha256-UfLfEQAkXxDaVPC7foE/J3FVEXd31Pu6uQIhTic3JgY=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "8.0.1", + "hash": "sha256-vkfVw4tQEg86Xg18v6QO0Qb4Ysz0Njx57d1XcNuj6IU=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "8.0.2", + "hash": "sha256-cHpe8X2BgYa5DzulZfq24rg8O2K5Lmq2OiLhoyAVgJc=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "2.0.0", + "hash": "sha256-EMvaXxGzueI8lT97bYJQr0kAj1IK0pjnAcWN82hTnzw=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "8.0.2", + "hash": "sha256-AjcldddddtN/9aH9pg7ClEZycWtFHLi9IPe1GGhNQys=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "2.0.0", + "hash": "sha256-q44LtMvyNEKSvgERvA+BrasKapP92Sc91QR4u2TJ9/Y=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "8.0.0", + "hash": "sha256-FU8qj3DR8bDdc1c+WeGZx/PCZeqqndweZM9epcpXjSo=" + }, + { + "pname": "Microsoft.NETCore.Platforms", + "version": "1.1.0", + "hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM=" + }, + { + "pname": "Microsoft.NETCore.Targets", + "version": "1.1.0", + "hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ=" + }, + { + "pname": "runtime.any.System.Globalization", + "version": "4.3.0", + "hash": "sha256-PaiITTFI2FfPylTEk7DwzfKeiA/g/aooSU1pDcdwWLU=" + }, + { + "pname": "runtime.any.System.Runtime", + "version": "4.3.0", + "hash": "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM=" + }, + { + "pname": "runtime.native.System", + "version": "4.3.0", + "hash": "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y=" + }, + { + "pname": "runtime.unix.System.Private.Uri", + "version": "4.3.0", + "hash": "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs=" + }, + { + "pname": "System.Globalization", + "version": "4.3.0", + "hash": "sha256-caL0pRmFSEsaoeZeWN5BTQtGrAtaQPwFi8YOZPZG5rI=" + }, + { + "pname": "System.Private.Uri", + "version": "4.3.0", + "hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM=" + }, + { + "pname": "System.Runtime", + "version": "4.3.0", + "hash": "sha256-51813WXpBIsuA6fUtE5XaRQjcWdQ2/lmEokJt97u0Rg=" + }, + { + "pname": "System.Runtime.CompilerServices.Unsafe", + "version": "4.4.0", + "hash": "sha256-SeTI4+yVRO2SmAKgOrMni4070OD+Oo8L1YiEVeKDyig=" + }, + { + "pname": "System.Text.Json", + "version": "8.0.5", + "hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68=" + }, + { + "pname": "System.Threading.Tasks.Dataflow", + "version": "8.0.1", + "hash": "sha256-hgCfF91BDd/eOtLEd5jhjzgJdvwmVv4/b42fXRr3nvo=" + } +] diff --git a/plugin/dlna/xml-sanitize.patch b/plugin/dlna/xml-sanitize.patch new file mode 100644 index 0000000..cc8db5f --- /dev/null +++ b/plugin/dlna/xml-sanitize.patch @@ -0,0 +1,125 @@ +diff --git a/src/Jellyfin.Plugin.Dlna/Didl/DidlBuilder.cs b/src/Jellyfin.Plugin.Dlna/Didl/DidlBuilder.cs +index 150fb49..9160d8a 100644 +--- a/src/Jellyfin.Plugin.Dlna/Didl/DidlBuilder.cs ++++ b/src/Jellyfin.Plugin.Dlna/Didl/DidlBuilder.cs +@@ -239,6 +239,17 @@ public class DidlBuilder + writer.WriteFullEndElement(); + } + ++ private string sanitize(string input) ++ { ++ var valid = input.Where(ch => System.Xml.XmlConvert.IsXmlChar(ch)).ToArray(); ++ if (valid.Length == input.Length) ++ { ++ return input; ++ } ++ _logger.LogInformation("Sanitized: {0} (was: {1})", valid, input.Where(ch => true).ToArray()); ++ return new string(valid); ++ } ++ + private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo? streamInfo = null) + { + if (streamInfo is null) +@@ -1009,7 +1020,7 @@ public class DidlBuilder + writer.WriteStartElement("upnp", "artist", NsUpnp); + writer.WriteAttributeString("role", "AlbumArtist"); + +- writer.WriteString(name); ++ writer.WriteString(sanitize(name)); + + writer.WriteFullEndElement(); + } +@@ -1023,7 +1034,7 @@ public class DidlBuilder + { + try + { +- writer.WriteElementString(prefix, name, namespaceUri, value); ++ writer.WriteElementString(prefix, name, namespaceUri, sanitize(value)); + } + catch (XmlException ex) + { +diff --git a/src/Jellyfin.Plugin.Dlna/PlayTo/PlayToController.cs b/src/Jellyfin.Plugin.Dlna/PlayTo/PlayToController.cs +index 4d822e2..9105d46 100644 +--- a/src/Jellyfin.Plugin.Dlna/PlayTo/PlayToController.cs ++++ b/src/Jellyfin.Plugin.Dlna/PlayTo/PlayToController.cs +@@ -386,34 +386,38 @@ public class PlayToController : ISessionController, IDisposable + ? null : + _userManager.GetUserById(command.ControllingUserId); + +- var items = new List(); +- foreach (var id in command.ItemIds) +- { +- AddItemFromId(id, items); +- } +- + var startIndex = command.StartIndex ?? 0; +- int len = items.Count - startIndex; +- if (startIndex > 0) +- { +- items = items.GetRange(startIndex, len); +- } +- +- var playlist = new PlaylistItem[len]; +- +- // Not nullable enabled - so this is required. +- playlist[0] = CreatePlaylistItem( +- items[0], +- user, +- command.StartPositionTicks ?? 0, +- command.MediaSourceId ?? string.Empty, +- command.AudioStreamIndex, +- command.SubtitleStreamIndex); ++ var playlist = new List(); ++ var first = true; ++ foreach (var id in command.ItemIds) { ++ var item = _libraryManager.GetItemById(id); ++ if (item?.MediaType != MediaType.Audio && item?.MediaType != MediaType.Video) ++ { ++ continue; ++ } + +- for (int i = 1; i < len; i++) +- { +- playlist[i] = CreatePlaylistItem(items[i], user, 0, string.Empty, null, null); +- } ++ if (startIndex > 0) { ++ startIndex -= 1; ++ continue; ++ } ++ ++ PlaylistItem? playlistItem = null; ++ try { ++ playlistItem = CreatePlaylistItem(item, user, ++ first ? (command.StartPositionTicks ?? 0) : 0, ++ first ? (command.MediaSourceId ?? string.Empty) : string.Empty, ++ first ? command.AudioStreamIndex : null, ++ first ? command.SubtitleStreamIndex : null); ++ } catch (System.NullReferenceException) { ++ _logger.LogError("{0}: could not create playlist item.", item.Path ?? ""); ++ } ++ first = false; ++ ++ if (playlistItem != null) ++ { ++ playlist.Add(playlistItem); ++ } ++ } + + _logger.LogDebug("{0} - Playlist created", _session.DeviceName); + +@@ -509,15 +513,6 @@ public class PlayToController : ISessionController, IDisposable + return info.IsDirectStream; + } + +- private void AddItemFromId(Guid id, List list) +- { +- var item = _libraryManager.GetItemById(id); +- if (item?.MediaType == MediaType.Audio || item?.MediaType == MediaType.Video) +- { +- list.Add(item); +- } +- } +- + private PlaylistItem CreatePlaylistItem( + BaseItem item, + User? user,