using System.IO; using System.IO.Compression; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Policy; using System.Text; using System.Text.RegularExpressions; using System.Xml; namespace TV_Player { public class ProgramInfo : ObservableViewModelBase { private string _title; private int _durationValue; public string Title { get => _title; set => SetProperty(ref _title, value); } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public int DurationValue { get => _durationValue; set => SetProperty(ref _durationValue, value); } } public class ProgramGuide { public string Id { get; set; } public string DisplayName { get; set; } public List Programs { get; set; } = new List(); } public static class M3UParser { public static async Task DownloadGuideFromWebAsync(string url) { var filePath = "guide.xml"; if (File.Exists(filePath)) { DateTime creationTime = File.GetCreationTime(filePath); DateTime modificationTime = File.GetLastWriteTime(filePath); DateTime currentTime = DateTime.Now; if ((currentTime - creationTime).TotalDays < 3 || (currentTime - modificationTime).TotalDays < 3) { return; } }; var channelsContent = string.Empty; if (url.Contains(".gz")) { channelsContent = await DownloadGzip(url); } else { channelsContent = await DownloadXMLProgram(url); } await File.WriteAllTextAsync(filePath, channelsContent); } private static async Task DownloadXMLProgram(string url) { using (var client = new HttpClient()) using (var request = new HttpRequestMessage()) { client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text")); request.Method = HttpMethod.Get; request.RequestUri = new Uri(url); var response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); return responseBody; } } public static async Task DownloadGzip(string url) { try { using HttpClient httpClient = new HttpClient(); byte[] compressedData = await httpClient.GetByteArrayAsync(url); using MemoryStream compressedStream = new MemoryStream(compressedData); using GZipStream decompressionStream = new GZipStream(compressedStream, CompressionMode.Decompress); using MemoryStream decompressedStream = new MemoryStream(); await decompressionStream.CopyToAsync(decompressedStream); // Step 3: Get the inner XML as a string string xmlContent = Encoding.UTF8.GetString(decompressedStream.ToArray()); // Output the XML content return xmlContent; } catch (Exception ex) { Console.WriteLine($"An error occurred: {ex.Message}"); return null; } } public static async Task ParseEpg(string channelId) { XmlReaderSettings settings = new XmlReaderSettings(); settings.IgnoreWhitespace = true; settings.IgnoreComments = true; settings.DtdProcessing = DtdProcessing.Parse; settings.Async = true; ProgramGuide channel = null; using (XmlReader reader = XmlReader.Create("guide.xml", settings)) { try { while (await reader.ReadAsync()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "channel") { var id = reader.GetAttribute("id"); if (id == channelId) { channel = new ProgramGuide { Id = id }; reader.Read(); channel.DisplayName = reader.ReadElementContentAsString(); } continue; } if (channel != null && reader.NodeType == XmlNodeType.Element && reader.Name == "programme") { ProgramInfo program = new ProgramInfo(); var id = reader.GetAttribute("channel"); if (id != channelId) continue; program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null); program.EndTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null); reader.Read(); program.Title = reader.ReadElementContentAsString(); channel.Programs.Add(program); } else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "channel") { // break; } } } catch (Exception ex) { } } return channel; } private static async Task ReadFile(string url) { string responseBody; using (var client = new HttpClient()) using (var request = new HttpRequestMessage()) { client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text")); request.Method = HttpMethod.Get; request.RequestUri = new Uri(url); var response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); responseBody = await response.Content.ReadAsStringAsync(); } return responseBody; } public static async Task<(List programList, string programGuide)> DownloadM3UFromWebAsync(string url) { string fileData; if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) { fileData = await ReadFile(url); } else { fileData = File.ReadAllText(url); } return ParseM3UFromString(fileData); } private static string[] SplitStringBeforeSeparator(string input, string separator) { string[] parts = input.Split(separator); // Reconstruct the string until the separator is reached int separatorIndex = input.IndexOf(separator); if (separatorIndex != -1) { parts[0] = input.Substring(0, separatorIndex + 1); for (int i = 1; i < parts.Length; i++) { parts[i] = separator + parts[i]; } } return parts; } private static (List programList, string programGuide) ParseM3UFromString(string content) { List playlistItems = new List(); string programGuideLink = string.Empty; try { var m3u = SplitStringBeforeSeparator(content, "#EXT"); foreach (var line in m3u) { if (line.StartsWith("#EXTINF:")) { if (TryParseM3ULine(line, out var m3uInfo)) { playlistItems.Add(m3uInfo); } } if (line.StartsWith("#EXTM3U")) { programGuideLink = ExtractXtvgUrl(line); } } } catch (Exception ex) { Console.WriteLine("Error reading M3U file: " + ex.Message); } return (playlistItems, programGuideLink); } private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info) { info = null; //string pattern = @"#EXTINF:\d+ CUID=""(?.*?)"" number=""(?.*?)"" tvg-id=""(?.*?)"" tvg-name=""(?.*?)"".*?tvg-logo=""(?.*?)"" group-title=""(?.*?)""[^,]*,(?.*)[^\r](?.*)$"; string pattern = @"#EXTINF:(-?\d+)\s+?(?:timeshift=""(?.*?)""\s+)?(?:catchup-days=""(?.*?)""\s+)?(?:catchup-type=""(?.*?)""\s+)?(?:CUID=""(?.*?)""\s+)?(?:number=""(?.*?)""\s+)?(?:tvg-id=""(?.*?)""\s+)?(?:tvg-name=""(?.*?)""\s+)?(?:group-title=""(?.*?)""\s+)?(?:tvg-logo=""(?.*?)"")?,(?.*?)\s*\r?\n(?.*)"; Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); Match match = regex.Match(m3uLine); if (match.Success) { info = new M3UInfo { CUID = match.Groups["CUID"].Value, Number = match.Groups["Number"].Value, TvgID = match.Groups["TvgID"].Value, TvgName = match.Groups["TvgName"].Value, GroupTitle = match.Groups["GroupTitle"].Value, Logo = match.Groups["Logo"].Value, Name = match.Groups["Name"].Value, Url = match.Groups["URL"].Value }; return true; } return false; } private static string ExtractXtvgUrl(string m3uEntry) { // Define a regular expression pattern to match x-tvg-url attribute string pattern = @"(x-tvg-url=""(.*?)"")?(url-tvg=""(.*?)"")"; // Use Regex.Match to find the first match Match match = Regex.Match(m3uEntry, pattern); // Check if a match is found and get the value from the capturing group if (match.Success && match.Groups.Count > 1) { return match.Groups[4].Value; } // Return null or an empty string if no match is found return string.Empty; } } }