parse list from another source, make file guide

This commit is contained in:
2024-09-24 19:57:07 +03:00
parent af83f6c696
commit ff78d7a062
3 changed files with 100 additions and 46 deletions
+86 -27
View File
@@ -1,6 +1,9 @@
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;
@@ -28,9 +31,36 @@ namespace TV_Player
public static class M3UParser
{
public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url)
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<string> DownloadXMLProgram(string url)
{
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ;
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
@@ -40,53 +70,82 @@ namespace TV_Player
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
epgChannels = ParseEpg(responseBody);
return responseBody;
}
return epgChannels;
}
private static List<ProgramGuide> ParseEpg(string epgData)
public static async Task<string> 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<ProgramGuide> ParseEpg(string channelId)
{
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;
using (XmlReader reader = XmlReader.Create(new System.IO.StringReader(epgData), settings))
settings.DtdProcessing = DtdProcessing.Parse;
settings.Async = true;
ProgramGuide channel = null;
using (XmlReader reader = XmlReader.Create("guide.xml", settings))
{
try
{
while (reader.Read())
while (await reader.ReadAsync())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "channel")
{
ProgramGuide channel = new ProgramGuide();
channel.Id = reader.GetAttribute("id");
reader.Read();
channel.DisplayName = reader.ReadElementContentAsString();
var id = reader.GetAttribute("id");
if (id == channelId)
{
channel = new ProgramGuide
{
Id = id
};
reader.Read();
channel.DisplayName = reader.ReadElementContentAsString();
epgChannels.Add(channel);
}
continue;
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "programme")
if (channel != null && reader.NodeType == XmlNodeType.Element && reader.Name == "programme")
{
ProgramInfo program = new ProgramInfo();
var id = reader.GetAttribute("channel");
var channel = epgChannels.FirstOrDefault(x => x.Id == id);
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;
// break;
}
}
}
@@ -94,7 +153,7 @@ namespace TV_Player
{
}
}
return epgChannels;
return channel;
}
private static async Task<string> ReadFile(string url)
@@ -116,15 +175,15 @@ namespace TV_Player
public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
{
string fileData;
if (Uri.IsWellFormedUriString(url,UriKind.Absolute))
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
fileData = await ReadFile(url);
}
else
{
fileData= File.ReadAllText(url);
fileData = File.ReadAllText(url);
}
return ParseM3UFromString(fileData);
}
private static string[] SplitStringBeforeSeparator(string input, string separator)
@@ -164,7 +223,7 @@ namespace TV_Player
}
if (line.StartsWith("#EXTM3U"))
{
programGuideLink=ExtractXtvgUrl(line);
programGuideLink = ExtractXtvgUrl(line);
}
}
}
@@ -173,14 +232,14 @@ namespace TV_Player
Console.WriteLine("Error reading M3U file: " + ex.Message);
}
return (playlistItems,programGuideLink);
return (playlistItems, programGuideLink);
}
private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
{
info = null;
//string pattern = @"#EXTINF:\d+ CUID=""(?<CUID>.*?)"" number=""(?<Number>.*?)"" tvg-id=""(?<TvgID>.*?)"" tvg-name=""(?<TvgName>.*?)"".*?tvg-logo=""(?<Logo>.*?)"" group-title=""(?<GroupTitle>.*?)""[^,]*,(?<Name>.*)[^\r](?<URL>.*)$";
string pattern = @"#EXTINF:(?:-?\d+)\s+?(?:CUID=""(?<CUID>.*?)""\s+)?(?:number=""(?<Number>.*?)""\s+)?(?:tvg-id=""(?<TvgID>.*?)""\s+)?(?:tvg-name=""(?<TvgName>.*?)""\s+)?(?:tvg-logo=""(?<Logo>.*?)""\s+)?(?:group-title=""(?<GroupTitle>.*?)"")[^,]*,(?<Name>.*)[^\r](?<URL>.*)$";
string pattern = @"#EXTINF:(-?\d+)\s+?(?:timeshift=""(?<Timeshift>.*?)""\s+)?(?:catchup-days=""(?<CatchupDays>.*?)""\s+)?(?:catchup-type=""(?<CatchupType>.*?)""\s+)?(?:CUID=""(?<CUID>.*?)""\s+)?(?:number=""(?<Number>.*?)""\s+)?(?:tvg-id=""(?<TvgID>.*?)""\s+)?(?:tvg-name=""(?<TvgName>.*?)""\s+)?(?:group-title=""(?<GroupTitle>.*?)""\s+)?(?:tvg-logo=""(?<Logo>.*?)"")?,(?<Name>.*?)\s*\r?\n(?<URL>.*)";
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
Match match = regex.Match(m3uLine);
@@ -206,7 +265,7 @@ namespace TV_Player
private static string ExtractXtvgUrl(string m3uEntry)
{
// Define a regular expression pattern to match x-tvg-url attribute
string pattern = @"x-tvg-url=""(.*?)""";
string pattern = @"(x-tvg-url=""(.*?)"")?(url-tvg=""(.*?)"")";
// Use Regex.Match to find the first match
Match match = Regex.Match(m3uEntry, pattern);
@@ -214,7 +273,7 @@ namespace TV_Player
// Check if a match is found and get the value from the capturing group
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value;
return match.Groups[4].Value;
}
// Return null or an empty string if no match is found
+2 -2
View File
@@ -116,11 +116,11 @@ namespace TV_Player
TopPanelTitle = _currentProgram.Name;
SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
_programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(x =>
_programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(async x =>
{
try
{
_currentGuide = x.FirstOrDefault(p => p.Id == _currentProgram.TvgID);
_currentGuide = await TVPlayerViewModel.Instance.PlaylistData.GetGuideByProgram(_currentProgram.TvgID);
UpdateScreenInfo();
+12 -17
View File
@@ -1,4 +1,5 @@
using System.Reactive.Subjects;
using System.Reactive;
using System.Reactive.Subjects;
namespace TV_Player
{
@@ -6,11 +7,11 @@ namespace TV_Player
{
private readonly ReplaySubject<List<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>();
private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>();
private readonly ReplaySubject<List<ProgramGuide>> programGuideSubject = new ReplaySubject<List<ProgramGuide>>();
private readonly ReplaySubject<Unit> programGuideSubject = new ReplaySubject<Unit>();
public IObservable<List<M3UInfo>> AllPrograms => programsSubject;
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
public IObservable<Unit> ProgramGuideInfo => programGuideSubject;
public ProgramsData()
{
}
@@ -20,17 +21,6 @@ namespace TV_Player
//string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
var result = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
// For how hardcoded add custom channel
result.programList.Add(new M3UInfo()
{
GroupTitle = "Познавательные",
Name = "Discovery science",
Logo = "http://ip.viks.tv/posts/2018-11/1543603622_discovery_science.png",
Number = Convert.ToString(result.programList.Count+1),
Url= "https://s2.viks.tv/571/index.m3u8?k=1715093638p791i991i86i78S9b9c0d8fefdcO74ff3ed2f4710c95b07"
});;
programsSubject.OnNext(result.programList);
var groupping = result.programList.GroupBy(item => item.GroupTitle)
@@ -41,11 +31,16 @@ namespace TV_Player
await Task.Run(() => GetProgramGuide(result.programGuide));
}
public Task<ProgramGuide> GetGuideByProgram(string channelID)
{
return M3UParser.ParseEpg(channelID);
}
private async Task GetProgramGuide(string guideLink)
{
//string guideLink = "http://epg.da-tv.vip/107-light.xml";
var programGuide = await M3UParser.DownloadGuideFromWebAsync(guideLink);
programGuideSubject.OnNext(programGuide);
//guideLink = "http://epg.da-tv.vip/107-light.xml";
await M3UParser.DownloadGuideFromWebAsync(guideLink);
programGuideSubject.OnNext(Unit.Default);
}
internal void GetData(string playlistURL)