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:
@@ -65,6 +65,8 @@ namespace TV_Player
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> DownloadXMLProgram(string url)
|
private static async Task<string> DownloadXMLProgram(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
using (var request = new HttpRequestMessage())
|
using (var request = new HttpRequestMessage())
|
||||||
@@ -74,8 +76,18 @@ namespace TV_Player
|
|||||||
request.RequestUri = new Uri(url);
|
request.RequestUri = new Uri(url);
|
||||||
var response = await client.GetAsync(url);
|
var response = await client.GetAsync(url);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
string responseBody = await response.Content.ReadAsStringAsync();
|
return await response.Content.ReadAsStringAsync();
|
||||||
return responseBody;
|
}
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Network error downloading XML from {url}: {ex.Message}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (UriFormatException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Invalid URL: {url} - {ex.Message}");
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,15 +103,22 @@ namespace TV_Player
|
|||||||
using MemoryStream decompressedStream = new MemoryStream();
|
using MemoryStream decompressedStream = new MemoryStream();
|
||||||
await decompressionStream.CopyToAsync(decompressedStream);
|
await decompressionStream.CopyToAsync(decompressedStream);
|
||||||
|
|
||||||
// Step 3: Get the inner XML as a string
|
|
||||||
string xmlContent = Encoding.UTF8.GetString(decompressedStream.ToArray());
|
string xmlContent = Encoding.UTF8.GetString(decompressedStream.ToArray());
|
||||||
|
|
||||||
// Output the XML content
|
|
||||||
return xmlContent;
|
return xmlContent;
|
||||||
}
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Network error downloading gzip from {url}: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Invalid gzip data: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"An error occurred: {ex.Message}");
|
System.Diagnostics.Debug.WriteLine($"Error decompressing gzip: {ex.Message}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +136,12 @@ namespace TV_Player
|
|||||||
string programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
string programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||||
string filePath = Path.Combine(programDataPath, "TVPlayer", fileName);
|
string filePath = Path.Combine(programDataPath, "TVPlayer", fileName);
|
||||||
|
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"EPG file not found: {filePath}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
using (XmlReader reader = XmlReader.Create(filePath, settings))
|
using (XmlReader reader = XmlReader.Create(filePath, settings))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -134,7 +159,6 @@ namespace TV_Player
|
|||||||
};
|
};
|
||||||
reader.Read();
|
reader.Read();
|
||||||
channel.DisplayName = reader.ReadElementContentAsString();
|
channel.DisplayName = reader.ReadElementContentAsString();
|
||||||
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -145,22 +169,33 @@ namespace TV_Player
|
|||||||
|
|
||||||
var id = reader.GetAttribute("channel");
|
var id = reader.GetAttribute("channel");
|
||||||
if (id != channelId) continue;
|
if (id != channelId) continue;
|
||||||
program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
|
||||||
program.EndTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
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();
|
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")
|
}
|
||||||
|
}
|
||||||
|
catch (XmlException ex)
|
||||||
{
|
{
|
||||||
// break;
|
System.Diagnostics.Debug.WriteLine($"XML parsing error in EPG file: {ex.Message}");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (FileNotFoundException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"EPG file not found: {ex.Message}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Unexpected error parsing EPG: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return channel;
|
return channel;
|
||||||
@@ -168,7 +203,8 @@ namespace TV_Player
|
|||||||
|
|
||||||
private static async Task<string> ReadFile(string url)
|
private static async Task<string> ReadFile(string url)
|
||||||
{
|
{
|
||||||
string responseBody;
|
try
|
||||||
|
{
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
using (var request = new HttpRequestMessage())
|
using (var request = new HttpRequestMessage())
|
||||||
{
|
{
|
||||||
@@ -177,9 +213,19 @@ namespace TV_Player
|
|||||||
request.RequestUri = new Uri(url);
|
request.RequestUri = new Uri(url);
|
||||||
var response = await client.GetAsync(url);
|
var response = await client.GetAsync(url);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
responseBody = await response.Content.ReadAsStringAsync();
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Network error reading file 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)
|
public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
|
||||||
@@ -220,6 +266,12 @@ namespace TV_Player
|
|||||||
string programGuideLink = string.Empty;
|
string programGuideLink = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine("M3U content is empty");
|
||||||
|
return (playlistItems, programGuideLink);
|
||||||
|
}
|
||||||
|
|
||||||
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
||||||
|
|
||||||
foreach (var line in m3u)
|
foreach (var line in m3u)
|
||||||
@@ -238,9 +290,13 @@ namespace TV_Player
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Invalid M3U format: {ex.Message}");
|
||||||
|
}
|
||||||
catch (Exception ex)
|
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);
|
||||||
|
|||||||
@@ -69,7 +69,14 @@ namespace TV_Player
|
|||||||
|
|
||||||
public void OnCloseAppButtonClick()
|
public void OnCloseAppButtonClick()
|
||||||
{
|
{
|
||||||
Environment.Exit(0);
|
if (Application.Current?.MainWindow is Window window)
|
||||||
|
{
|
||||||
|
window.Close(); // Allows proper shutdown sequence
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Environment.Exit(0); // Fallback if window not available
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnButtonBackClick()
|
private void OnButtonBackClick()
|
||||||
|
|||||||
@@ -1,74 +1,6 @@
|
|||||||
using Microsoft.Maui.Handlers;
|
namespace TV_Player.MAUI
|
||||||
using static Microsoft.Maui.ApplicationModel.Permissions;
|
|
||||||
|
|
||||||
namespace TV_Player.MAUI
|
|
||||||
{
|
{
|
||||||
//public partial class MediaViewerHandler : ViewHandler<MediaViewer, VideoView>
|
// AndroidHandler - Platform-specific implementation for Android video player
|
||||||
//{
|
// Currently using built-in media player implementation
|
||||||
// VideoView _videoView;
|
// Reference: LibVLC implementation was considered but currently not in use
|
||||||
// LibVLC _libVLC;
|
|
||||||
// LibVLCSharp.Shared.MediaPlayer _mediaPlayer;
|
|
||||||
|
|
||||||
// protected override VideoView CreatePlatformView() => new VideoView(Context);
|
|
||||||
|
|
||||||
// protected override void ConnectHandler(VideoView nativeView)
|
|
||||||
// {
|
|
||||||
// base.ConnectHandler(nativeView);
|
|
||||||
|
|
||||||
// _libVLC = new LibVLC(enableDebugLogs: true);
|
|
||||||
// _mediaPlayer = new LibVLCSharp.Shared.MediaPlayer(_libVLC)
|
|
||||||
// {
|
|
||||||
// EnableHardwareDecoding = true
|
|
||||||
// };
|
|
||||||
|
|
||||||
// _videoView = nativeView ?? new VideoView(Context);
|
|
||||||
// _videoView.MediaPlayer = _mediaPlayer;
|
|
||||||
|
|
||||||
// HandleUrl(VirtualView.StreamUrl);
|
|
||||||
|
|
||||||
// base.ConnectHandler(nativeView);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected override void DisconnectHandler(VideoView nativeView)
|
|
||||||
// {
|
|
||||||
// nativeView.Dispose();
|
|
||||||
// base.DisconnectHandler(nativeView);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private void HandleUrl(string url)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
|
|
||||||
// if (url.EndsWith("/"))
|
|
||||||
// {
|
|
||||||
// url = url.TrimEnd('/');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
|
|
||||||
|
|
||||||
// if (!string.IsNullOrEmpty(url))
|
|
||||||
// {
|
|
||||||
// var media = new Media(_libVLC, url, FromType.FromLocation);
|
|
||||||
|
|
||||||
// _mediaPlayer.NetworkCaching = 1500;
|
|
||||||
|
|
||||||
// if (_mediaPlayer.Media != null)
|
|
||||||
// {
|
|
||||||
// _mediaPlayer.Stop();
|
|
||||||
// _mediaPlayer.Media.Dispose();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// _mediaPlayer.Media = media;
|
|
||||||
// _mediaPlayer.Mute = true;
|
|
||||||
|
|
||||||
// _videoView.MediaPlayer.Play();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,5 @@
|
|||||||
using System;
|
namespace TV_Player.MAUI
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace TV_Player.MAUI
|
|
||||||
{
|
{
|
||||||
// public partial class MediaViewerHandler
|
// MediaViewerHandler is currently not implemented
|
||||||
// {
|
// This is reserved for custom platform-specific media player implementations
|
||||||
// public static IPropertyMapper<MediaViewer, MediaViewerHandler> PropertyMapper =
|
|
||||||
// new PropertyMapper<MediaViewer, MediaViewerHandler>()
|
|
||||||
// {
|
|
||||||
// };
|
|
||||||
|
|
||||||
// public static CommandMapper<MediaViewer, MediaViewerHandler> CommandMapper = new(ViewCommandMapper)
|
|
||||||
// {
|
|
||||||
// };
|
|
||||||
|
|
||||||
// public MediaViewerHandler() : base(PropertyMapper, CommandMapper)
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ namespace TV_Player.MAUI
|
|||||||
|
|
||||||
public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url)
|
public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url)
|
||||||
{
|
{
|
||||||
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ;
|
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
|
||||||
|
try
|
||||||
|
{
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
using (var request = new HttpRequestMessage())
|
using (var request = new HttpRequestMessage())
|
||||||
{
|
{
|
||||||
@@ -40,6 +42,19 @@ namespace TV_Player.MAUI
|
|||||||
string responseBody = await response.Content.ReadAsStringAsync();
|
string responseBody = await response.Content.ReadAsStringAsync();
|
||||||
epgChannels = ParseEpg(responseBody);
|
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;
|
return epgChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +88,19 @@ namespace TV_Player.MAUI
|
|||||||
|
|
||||||
var id = reader.GetAttribute("channel");
|
var id = reader.GetAttribute("channel");
|
||||||
var channel = epgChannels.FirstOrDefault(x => x.Id == id);
|
var channel = epgChannels.FirstOrDefault(x => x.Id == id);
|
||||||
program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
if (channel == null) continue;
|
||||||
program.EndTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
|
||||||
|
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();
|
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")
|
||||||
@@ -88,14 +109,22 @@ 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;
|
return epgChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> ReadFile(string url)
|
private static async Task<string> ReadFile(string url)
|
||||||
{
|
{
|
||||||
string responseBody;
|
try
|
||||||
|
{
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
using (var request = new HttpRequestMessage())
|
using (var request = new HttpRequestMessage())
|
||||||
{
|
{
|
||||||
@@ -104,9 +133,19 @@ namespace TV_Player.MAUI
|
|||||||
request.RequestUri = new Uri(url);
|
request.RequestUri = new Uri(url);
|
||||||
var response = await client.GetAsync(url);
|
var response = await client.GetAsync(url);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
responseBody = await response.Content.ReadAsStringAsync();
|
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)
|
public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
|
||||||
@@ -139,6 +178,12 @@ namespace TV_Player.MAUI
|
|||||||
string programGuideLink = string.Empty;
|
string programGuideLink = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine("M3U content is empty");
|
||||||
|
return (playlistItems, programGuideLink);
|
||||||
|
}
|
||||||
|
|
||||||
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
||||||
|
|
||||||
foreach (var line in m3u)
|
foreach (var line in m3u)
|
||||||
@@ -152,16 +197,20 @@ namespace TV_Player.MAUI
|
|||||||
}
|
}
|
||||||
if (line.StartsWith("#EXTM3U"))
|
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)
|
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)
|
private static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
|
||||||
namespace TV_Player.MAUI
|
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;
|
private List<GroupInfo> _programs;
|
||||||
public List<GroupInfo> Programs
|
public List<GroupInfo> Programs
|
||||||
{
|
{
|
||||||
@@ -18,24 +19,39 @@ namespace TV_Player.MAUI
|
|||||||
public MainViewModel()
|
public MainViewModel()
|
||||||
{
|
{
|
||||||
ItemSelectedCommand = new Command(OnItemSelected);
|
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()
|
private void OnItemSelected()
|
||||||
{
|
{
|
||||||
var navigation = (INavigation)Application.Current.MainPage.Navigation;
|
try
|
||||||
|
{
|
||||||
|
if (Application.Current?.MainPage?.Navigation == null)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine("Navigation context is not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var programPageViewModel = new ProgramViewModel(SelectedItem);
|
var programPageViewModel = new ProgramViewModel(SelectedItem);
|
||||||
|
|
||||||
// Create a new SecondPage and set its BindingContext to the ViewModel
|
|
||||||
var programPage = new ProgramPage
|
var programPage = new ProgramPage
|
||||||
{
|
{
|
||||||
BindingContext = programPageViewModel
|
BindingContext = programPageViewModel
|
||||||
};
|
};
|
||||||
// Navigate to the OtherPage
|
|
||||||
navigation.PushAsync(programPage);
|
|
||||||
|
|
||||||
_groupsSubscriber.Dispose();
|
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
|
try
|
||||||
{
|
{
|
||||||
URLSource = _currentProgram.Url;
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
// Handle exceptions
|
System.Diagnostics.Debug.WriteLine("Invalid URL for playback");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
URLSource = url;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
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; }
|
public Action ButtonBackAction { get; set; }
|
||||||
|
|
||||||
private static TVPlayerViewModel _instance;
|
private static readonly Lazy<TVPlayerViewModel> _instance =
|
||||||
public static TVPlayerViewModel Instance
|
new Lazy<TVPlayerViewModel>(
|
||||||
{
|
() => new TVPlayerViewModel(),
|
||||||
get
|
LazyThreadSafetyMode.ExecutionAndPublication);
|
||||||
{
|
|
||||||
if (_instance == null)
|
public static TVPlayerViewModel Instance => _instance.Value;
|
||||||
_instance = new TVPlayerViewModel();
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TVPlayerViewModel()
|
public TVPlayerViewModel()
|
||||||
{
|
{
|
||||||
PlaylistData = new ProgramsData();
|
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