parse list from another source, make file guide
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Security.Policy;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
@@ -28,9 +31,36 @@ namespace TV_Player
|
|||||||
public static class M3UParser
|
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 client = new HttpClient())
|
||||||
using (var request = new HttpRequestMessage())
|
using (var request = new HttpRequestMessage())
|
||||||
{
|
{
|
||||||
@@ -40,53 +70,82 @@ namespace TV_Player
|
|||||||
var response = await client.GetAsync(url);
|
var response = await client.GetAsync(url);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
string responseBody = await response.Content.ReadAsStringAsync();
|
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();
|
XmlReaderSettings settings = new XmlReaderSettings();
|
||||||
settings.IgnoreWhitespace = true;
|
settings.IgnoreWhitespace = true;
|
||||||
settings.IgnoreComments = true;
|
settings.IgnoreComments = true;
|
||||||
|
settings.DtdProcessing = DtdProcessing.Parse;
|
||||||
using (XmlReader reader = XmlReader.Create(new System.IO.StringReader(epgData), settings))
|
settings.Async = true;
|
||||||
|
ProgramGuide channel = null;
|
||||||
|
using (XmlReader reader = XmlReader.Create("guide.xml", settings))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (reader.Read())
|
while (await reader.ReadAsync())
|
||||||
{
|
{
|
||||||
if (reader.NodeType == XmlNodeType.Element && reader.Name == "channel")
|
if (reader.NodeType == XmlNodeType.Element && reader.Name == "channel")
|
||||||
{
|
{
|
||||||
ProgramGuide channel = new ProgramGuide();
|
var id = reader.GetAttribute("id");
|
||||||
channel.Id = reader.GetAttribute("id");
|
if (id == channelId)
|
||||||
reader.Read();
|
{
|
||||||
channel.DisplayName = reader.ReadElementContentAsString();
|
channel = new ProgramGuide
|
||||||
|
{
|
||||||
|
Id = id
|
||||||
|
};
|
||||||
|
reader.Read();
|
||||||
|
channel.DisplayName = reader.ReadElementContentAsString();
|
||||||
|
|
||||||
epgChannels.Add(channel);
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.NodeType == XmlNodeType.Element && reader.Name == "programme")
|
if (channel != null && reader.NodeType == XmlNodeType.Element && reader.Name == "programme")
|
||||||
{
|
{
|
||||||
ProgramInfo program = new ProgramInfo();
|
ProgramInfo program = new ProgramInfo();
|
||||||
|
|
||||||
var id = reader.GetAttribute("channel");
|
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.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
||||||
program.EndTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
program.EndTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
||||||
|
|
||||||
reader.Read();
|
reader.Read();
|
||||||
program.Title = reader.ReadElementContentAsString();
|
program.Title = reader.ReadElementContentAsString();
|
||||||
|
|
||||||
|
|
||||||
channel.Programs.Add(program);
|
channel.Programs.Add(program);
|
||||||
}
|
}
|
||||||
else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "channel")
|
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)
|
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)
|
public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
|
||||||
{
|
{
|
||||||
string fileData;
|
string fileData;
|
||||||
if (Uri.IsWellFormedUriString(url,UriKind.Absolute))
|
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||||
{
|
{
|
||||||
fileData = await ReadFile(url);
|
fileData = await ReadFile(url);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fileData= File.ReadAllText(url);
|
fileData = File.ReadAllText(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseM3UFromString(fileData);
|
return ParseM3UFromString(fileData);
|
||||||
}
|
}
|
||||||
private static string[] SplitStringBeforeSeparator(string input, string separator)
|
private static string[] SplitStringBeforeSeparator(string input, string separator)
|
||||||
@@ -164,7 +223,7 @@ namespace TV_Player
|
|||||||
}
|
}
|
||||||
if (line.StartsWith("#EXTM3U"))
|
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);
|
Console.WriteLine("Error reading M3U file: " + ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (playlistItems,programGuideLink);
|
return (playlistItems, programGuideLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
|
private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
|
||||||
{
|
{
|
||||||
info = null;
|
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+ 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);
|
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
Match match = regex.Match(m3uLine);
|
Match match = regex.Match(m3uLine);
|
||||||
@@ -206,7 +265,7 @@ namespace TV_Player
|
|||||||
private static string ExtractXtvgUrl(string m3uEntry)
|
private static string ExtractXtvgUrl(string m3uEntry)
|
||||||
{
|
{
|
||||||
// Define a regular expression pattern to match x-tvg-url attribute
|
// 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
|
// Use Regex.Match to find the first match
|
||||||
Match match = Regex.Match(m3uEntry, pattern);
|
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
|
// Check if a match is found and get the value from the capturing group
|
||||||
if (match.Success && match.Groups.Count > 1)
|
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
|
// Return null or an empty string if no match is found
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ namespace TV_Player
|
|||||||
TopPanelTitle = _currentProgram.Name;
|
TopPanelTitle = _currentProgram.Name;
|
||||||
SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
|
SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
|
||||||
|
|
||||||
_programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(x =>
|
_programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(async x =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_currentGuide = x.FirstOrDefault(p => p.Id == _currentProgram.TvgID);
|
_currentGuide = await TVPlayerViewModel.Instance.PlaylistData.GetGuideByProgram(_currentProgram.TvgID);
|
||||||
|
|
||||||
UpdateScreenInfo();
|
UpdateScreenInfo();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reactive.Subjects;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Subjects;
|
||||||
|
|
||||||
namespace TV_Player
|
namespace TV_Player
|
||||||
{
|
{
|
||||||
@@ -6,11 +7,11 @@ namespace TV_Player
|
|||||||
{
|
{
|
||||||
private readonly ReplaySubject<List<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>();
|
private readonly ReplaySubject<List<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>();
|
||||||
private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>();
|
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<M3UInfo>> AllPrograms => programsSubject;
|
||||||
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
|
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
|
||||||
|
|
||||||
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
|
public IObservable<Unit> ProgramGuideInfo => programGuideSubject;
|
||||||
public ProgramsData()
|
public ProgramsData()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -20,17 +21,6 @@ namespace TV_Player
|
|||||||
//string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
|
//string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
|
||||||
var result = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
|
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);
|
programsSubject.OnNext(result.programList);
|
||||||
|
|
||||||
var groupping = result.programList.GroupBy(item => item.GroupTitle)
|
var groupping = result.programList.GroupBy(item => item.GroupTitle)
|
||||||
@@ -41,11 +31,16 @@ namespace TV_Player
|
|||||||
await Task.Run(() => GetProgramGuide(result.programGuide));
|
await Task.Run(() => GetProgramGuide(result.programGuide));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<ProgramGuide> GetGuideByProgram(string channelID)
|
||||||
|
{
|
||||||
|
return M3UParser.ParseEpg(channelID);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task GetProgramGuide(string guideLink)
|
private async Task GetProgramGuide(string guideLink)
|
||||||
{
|
{
|
||||||
//string guideLink = "http://epg.da-tv.vip/107-light.xml";
|
//guideLink = "http://epg.da-tv.vip/107-light.xml";
|
||||||
var programGuide = await M3UParser.DownloadGuideFromWebAsync(guideLink);
|
await M3UParser.DownloadGuideFromWebAsync(guideLink);
|
||||||
programGuideSubject.OnNext(programGuide);
|
programGuideSubject.OnNext(Unit.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void GetData(string playlistURL)
|
internal void GetData(string playlistURL)
|
||||||
|
|||||||
Reference in New Issue
Block a user