From 89b68a26e736cc8ac60b22933baab3b53c117b21 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Sun, 2 Aug 2026 13:18:55 +0300 Subject: [PATCH] Refactor IptvEndpoints to improve XML generation and encoding handling Updated the RenderGuide method to return a byte array instead of a string, enhancing performance and ensuring proper UTF-8 encoding without a BOM. Adjusted the XML writing process to use a MemoryStream for direct byte output, improving compatibility with XMLTV parsers. This change streamlines the generation of the EPG XML file and ensures accurate encoding in the response. --- .../TeleWave.Api/Endpoints/IptvEndpoints.cs | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/backend/src/TeleWave.Api/Endpoints/IptvEndpoints.cs b/backend/src/TeleWave.Api/Endpoints/IptvEndpoints.cs index 14f46c0..bd5b475 100644 --- a/backend/src/TeleWave.Api/Endpoints/IptvEndpoints.cs +++ b/backend/src/TeleWave.Api/Endpoints/IptvEndpoints.cs @@ -82,8 +82,8 @@ public static class IptvEndpoints ); return Results.File( - Encoding.UTF8.GetBytes(RenderGuide(channels, Origin(request))), - "application/xml", + RenderGuide(channels, Origin(request)), + "application/xml; charset=utf-8", fileDownloadName: "telewave-epg.xml" ); } @@ -137,35 +137,45 @@ public static class IptvEndpoints private static string Attribute(string value) => value.Replace("\"", string.Empty).ReplaceLineEndings(" ").Trim(); - private static string RenderGuide(IReadOnlyList channels, string origin) + private static byte[] RenderGuide(IReadOnlyList channels, string origin) { - var output = new StringBuilder(); + using var output = new MemoryStream(); + // Пишем сразу в поток в UTF-8, а не в StringBuilder: XmlWriter поверх строки объявил бы + // encoding="utf-16" (строки .NET — UTF-16), тогда как байты мы всё равно отдаём в UTF-8. + // Часть плееров (Go encoding/xml) отвергает такое расхождение декларации и реальных байт. + // BOM не пишем: XMLTV его не ждёт, и он мешает строгим парсерам. // Через XmlWriter, а не склейкой строк: в названия и описания приходит текст из библиотеки, // и экранировать его руками — верный способ однажды отдать битый XML. - using var writer = XmlWriter.Create( - output, - new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8 } - ); - - writer.WriteStartDocument(); - writer.WriteStartElement("tv"); - writer.WriteAttributeString("generator-info-name", "TeleWave"); - - // Сначала все каналы, потом все передачи: XMLTV требует именно такого порядка секций. - foreach (var channel in channels) - WriteChannel(writer, channel, origin); - - foreach (var channel in channels) + using ( + var writer = XmlWriter.Create( + output, + new XmlWriterSettings + { + Indent = true, + Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + } + ) + ) { - foreach (var programme in channel.Programmes) - WriteProgramme(writer, channel.Slug, programme, origin); + writer.WriteStartDocument(); + writer.WriteStartElement("tv"); + writer.WriteAttributeString("generator-info-name", "TeleWave"); + + // Сначала все каналы, потом все передачи: XMLTV требует именно такого порядка секций. + foreach (var channel in channels) + WriteChannel(writer, channel, origin); + + foreach (var channel in channels) + { + foreach (var programme in channel.Programmes) + WriteProgramme(writer, channel.Slug, programme, origin); + } + + writer.WriteEndElement(); + writer.WriteEndDocument(); } - writer.WriteEndElement(); - writer.WriteEndDocument(); - writer.Flush(); - - return output.ToString(); + return output.ToArray(); } private static void WriteChannel(XmlWriter writer, IptvChannelDto channel, string origin)