diff --git a/Emby.Dlna/PlayTo/DlnaHttpClient.cs b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
index 75ff542dd8..4e9903f26a 100644
--- a/Emby.Dlna/PlayTo/DlnaHttpClient.cs
+++ b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
@@ -2,9 +2,11 @@
using System;
using System.Globalization;
+using System.IO;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
@@ -15,7 +17,10 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo
{
- public class DlnaHttpClient
+ ///
+ /// Http client for Dlna PlayTo function.
+ ///
+ public partial class DlnaHttpClient
{
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
@@ -54,15 +59,30 @@ namespace Emby.Dlna.PlayTo
LoadOptions.None,
cancellationToken).ConfigureAwait(false);
}
- catch (XmlException ex)
+ catch (XmlException)
{
- _logger.LogError(ex, "Failed to parse response");
- if (_logger.IsEnabled(LogLevel.Debug))
- {
- _logger.LogDebug("Malformed response: {Content}\n", await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false));
- }
+ // try correcting the Xml response with common errors
+ var xmlString = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
- return null;
+ // find and replace unescaped ampersands (&)
+ xmlString = EscapeAmpersandRegex().Replace(xmlString, "&");
+
+ try
+ {
+ // retry reading Xml
+ var xmlReader = new StringReader(xmlString);
+ return await XDocument.LoadAsync(
+ xmlReader,
+ LoadOptions.None,
+ cancellationToken).ConfigureAwait(false);
+ }
+ catch (XmlException ex)
+ {
+ _logger.LogError(ex, "Failed to parse response");
+ _logger.LogDebug("Malformed response: {Content}\n", xmlString);
+
+ return null;
+ }
}
}
@@ -104,5 +124,12 @@ namespace Emby.Dlna.PlayTo
// Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
return await SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
}
+
+ ///
+ /// Compile-time generated regular expression for escaping ampersands.
+ ///
+ /// Compiled regular expression.
+ [GeneratedRegex("(&(?![a-z]*;))")]
+ private static partial Regex EscapeAmpersandRegex();
}
}