Enhance error handling and logging in M3U and XML downloading processes; implement proper application shutdown in MainViewModel; introduce PlaylistSettings for configurable URLs and timeout settings.
This commit is contained in:
@@ -28,17 +28,32 @@ namespace TV_Player.MAUI
|
||||
|
||||
public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url)
|
||||
{
|
||||
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ;
|
||||
using (var client = new HttpClient())
|
||||
using (var request = new HttpRequestMessage())
|
||||
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
|
||||
try
|
||||
{
|
||||
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();
|
||||
epgChannels = ParseEpg(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();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
epgChannels = ParseEpg(responseBody);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Network error downloading EPG from {url}: {ex.Message}");
|
||||
}
|
||||
catch (UriFormatException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Invalid EPG URL: {url} - {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Unexpected error downloading EPG: {ex.Message}");
|
||||
}
|
||||
return epgChannels;
|
||||
}
|
||||
@@ -73,13 +88,19 @@ namespace TV_Player.MAUI
|
||||
|
||||
var id = reader.GetAttribute("channel");
|
||||
var channel = epgChannels.FirstOrDefault(x => x.Id == id);
|
||||
program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
||||
program.EndTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
||||
if (channel == null) continue;
|
||||
|
||||
if (!DateTime.TryParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var startTime))
|
||||
continue;
|
||||
if (!DateTime.TryParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var endTime))
|
||||
continue;
|
||||
|
||||
program.StartTime = startTime;
|
||||
program.EndTime = endTime;
|
||||
|
||||
reader.Read();
|
||||
program.Title = reader.ReadElementContentAsString();
|
||||
|
||||
|
||||
channel.Programs.Add(program);
|
||||
}
|
||||
else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "channel")
|
||||
@@ -88,25 +109,43 @@ namespace TV_Player.MAUI
|
||||
}
|
||||
}
|
||||
}
|
||||
catch{}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"XML parsing error: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Unexpected error parsing EPG: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return epgChannels;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadFile(string url)
|
||||
{
|
||||
string responseBody;
|
||||
using (var client = new HttpClient())
|
||||
using (var request = new HttpRequestMessage())
|
||||
try
|
||||
{
|
||||
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();
|
||||
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();
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Network error downloading from {url}: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (UriFormatException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Invalid URL: {url} - {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
|
||||
@@ -139,6 +178,12 @@ namespace TV_Player.MAUI
|
||||
string programGuideLink = string.Empty;
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("M3U content is empty");
|
||||
return (playlistItems, programGuideLink);
|
||||
}
|
||||
|
||||
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
||||
|
||||
foreach (var line in m3u)
|
||||
@@ -152,16 +197,20 @@ namespace TV_Player.MAUI
|
||||
}
|
||||
if (line.StartsWith("#EXTM3U"))
|
||||
{
|
||||
programGuideLink=ExtractXtvgUrl(line);
|
||||
programGuideLink = ExtractXtvgUrl(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Invalid M3U format: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error reading M3U file: " + ex.Message);
|
||||
System.Diagnostics.Debug.WriteLine($"Error parsing M3U file: {ex.Message}");
|
||||
}
|
||||
|
||||
return (playlistItems,programGuideLink);
|
||||
return (playlistItems, programGuideLink);
|
||||
}
|
||||
|
||||
private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Windows.Input;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
public class MainViewModel : ObservableViewModelBase
|
||||
public class MainViewModel : ObservableViewModelBase, IDisposable
|
||||
{
|
||||
private IDisposable _groupsSubscriber;
|
||||
private CompositeDisposable _disposables = new CompositeDisposable();
|
||||
private List<GroupInfo> _programs;
|
||||
public List<GroupInfo> Programs
|
||||
{
|
||||
@@ -18,24 +19,39 @@ namespace TV_Player.MAUI
|
||||
public MainViewModel()
|
||||
{
|
||||
ItemSelectedCommand = new Command(OnItemSelected);
|
||||
_groupsSubscriber = TVPlayerViewModel.Instance.PlaylistData.GroupsInformation.Subscribe(x => Programs = x);
|
||||
_disposables.Add(
|
||||
TVPlayerViewModel.Instance.PlaylistData.GroupsInformation.Subscribe(x => Programs = x)
|
||||
);
|
||||
}
|
||||
|
||||
private void OnItemSelected()
|
||||
{
|
||||
var navigation = (INavigation)Application.Current.MainPage.Navigation;
|
||||
|
||||
var programPageViewModel = new ProgramViewModel(SelectedItem);
|
||||
|
||||
// Create a new SecondPage and set its BindingContext to the ViewModel
|
||||
var programPage = new ProgramPage
|
||||
try
|
||||
{
|
||||
BindingContext = programPageViewModel
|
||||
};
|
||||
// Navigate to the OtherPage
|
||||
navigation.PushAsync(programPage);
|
||||
if (Application.Current?.MainPage?.Navigation == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Navigation context is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
_groupsSubscriber.Dispose();
|
||||
var programPageViewModel = new ProgramViewModel(SelectedItem);
|
||||
|
||||
var programPage = new ProgramPage
|
||||
{
|
||||
BindingContext = programPageViewModel
|
||||
};
|
||||
|
||||
Application.Current.MainPage.Navigation.PushAsync(programPage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Navigation error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,17 @@ namespace TV_Player.MAUI
|
||||
{
|
||||
try
|
||||
{
|
||||
URLSource = _currentProgram.Url;
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Invalid URL for playback");
|
||||
return;
|
||||
}
|
||||
|
||||
URLSource = url;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle exceptions
|
||||
System.Diagnostics.Debug.WriteLine($"Error playing M3U8: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration settings for playlist and EPG sources
|
||||
/// </summary>
|
||||
public class PlaylistSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// URL to the M3U playlist file
|
||||
/// </summary>
|
||||
public string M3UUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// URL to the EPG (Electronic Program Guide) XML file
|
||||
/// Can be overridden by x-tvg-url in M3U file
|
||||
/// </summary>
|
||||
public string EpgUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection timeout in seconds
|
||||
/// </summary>
|
||||
public int TimeoutSeconds { get; set; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to cache EPG data locally
|
||||
/// </summary>
|
||||
public bool CacheEpgLocally { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Cache validity period in days
|
||||
/// </summary>
|
||||
public int CacheValidityDays { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Load default settings
|
||||
/// </summary>
|
||||
public static PlaylistSettings Default => new PlaylistSettings
|
||||
{
|
||||
M3UUrl = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u",
|
||||
EpgUrl = string.Empty // Will be extracted from M3U file
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,21 +7,20 @@ namespace TV_Player.MAUI
|
||||
|
||||
public Action ButtonBackAction { get; set; }
|
||||
|
||||
private static TVPlayerViewModel _instance;
|
||||
public static TVPlayerViewModel Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = new TVPlayerViewModel();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
private static readonly Lazy<TVPlayerViewModel> _instance =
|
||||
new Lazy<TVPlayerViewModel>(
|
||||
() => new TVPlayerViewModel(),
|
||||
LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
|
||||
public static TVPlayerViewModel Instance => _instance.Value;
|
||||
|
||||
public TVPlayerViewModel()
|
||||
{
|
||||
PlaylistData = new ProgramsData();
|
||||
PlaylistData.GetData("http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u");
|
||||
|
||||
// Load settings - can be overridden with custom configuration
|
||||
var settings = PlaylistSettings.Default;
|
||||
PlaylistData.GetData(settings.M3UUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user