add close button
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="clr-namespace:TV_Player.MAUI"
|
||||
Shell.FlyoutBehavior="Disabled">
|
||||
Shell.FlyoutBehavior="Disabled"
|
||||
Background="DarkGray">
|
||||
|
||||
<ShellContent
|
||||
ContentTemplate="{DataTemplate local:MainPage}"
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
{
|
||||
public partial class AppShell : Shell
|
||||
{
|
||||
private TVPlayerViewModel _tvPlayer;
|
||||
public AppShell()
|
||||
{
|
||||
InitializeComponent();
|
||||
_tvPlayer = new TVPlayerViewModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-11
@@ -15,20 +15,24 @@
|
||||
SelectedItem="{Binding SelectedItem}"
|
||||
SelectionChangedCommand="{Binding ItemSelectedCommand}">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:M3UInfo">
|
||||
<Label Text="{Binding Name}"/>
|
||||
<DataTemplate x:DataType="local:GroupInfo">
|
||||
<Border x:Name="ButtonBorder">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="0.4*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Rectangle RadiusX="15" RadiusY="15" x:Name="Border" StrokeThickness="2" Stroke="Yellow" Grid.RowSpan="2" Fill="#B0000000"/>
|
||||
<Label x:Name="groupName" Text="{TemplateBinding GroupName}" FontSize="15" TextColor="White" HorizontalOptions="Center" VerticalOptions="Center"/>
|
||||
<HorizontalStackLayout Grid.Row="1" >
|
||||
<Label Text="{TemplateBinding ProgramsCount}" FontSize="10" TextColor="White" HorizontalOptions="Center" VerticalOptions="Center" LineHeight="10"/>
|
||||
<Label FontSize="10" TextColor="White" HorizontalOptions="Center" VerticalOptions="Center" LineHeight="10">программ</Label >
|
||||
</HorizontalStackLayout>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
|
||||
<!--<Label
|
||||
Text="Welcome to .NET Multi-platform App UI"
|
||||
SemanticProperties.HeadingLevel="Level2"
|
||||
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
|
||||
FontSize="18"
|
||||
HorizontalOptions="Center" />-->
|
||||
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using CommunityToolkit.Maui;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using CommunityToolkit.Maui;
|
||||
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
@@ -17,10 +17,6 @@ namespace TV_Player.MAUI
|
||||
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||
});
|
||||
|
||||
#if DEBUG
|
||||
builder.Logging.AddDebug();
|
||||
#endif
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using LibVLCSharp.Shared;
|
||||
|
||||
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
public class MediaViewer : ContentView
|
||||
{
|
||||
private LibVLC _libVLC;
|
||||
private MediaPlayer _mediaPlayer;
|
||||
//private MediaPlayer _mediaPlayer;
|
||||
|
||||
public static BindableProperty StreamUrlProperty = BindableProperty.Create(nameof(StreamUrl)
|
||||
, typeof(string)
|
||||
@@ -29,16 +27,13 @@ namespace TV_Player.MAUI
|
||||
|
||||
private void InitializeMediaPlayer()
|
||||
{
|
||||
_libVLC = new LibVLC();
|
||||
_mediaPlayer = new MediaPlayer(_libVLC);
|
||||
// var media = new Media(_libVLC, new Uri("http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiNDk3IiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODM2MS92aWRlby5tM3U4P3Rva2V/video.m3u8"));
|
||||
//_mediaPlayer.Play();
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
var media = new Media(_libVLC, new Uri("http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiNDk3IiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODM2MS92aWRlby5tM3U4P3Rva2V/video.m3u8"));
|
||||
_mediaPlayer.Play(media);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="TV_Player.MAUI.PlayerPage"
|
||||
xmlns:local="clr-namespace:TV_Player.MAUI"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
Title="PlayerPage"
|
||||
xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
|
||||
Loaded="ContentPage_Loaded">
|
||||
>
|
||||
|
||||
<StackLayout>
|
||||
<!--<toolkit:MediaElement x:Name="mediaElement"
|
||||
ShouldAutoPlay="True"
|
||||
ShouldShowPlaybackControls="True"
|
||||
Source="http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiNDk3IiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODM2MS92aWRlby5tM3U4P3Rva2V/video.m3u8"
|
||||
HeightRequest="300"
|
||||
WidthRequest="400"
|
||||
/>-->
|
||||
<!--<local:MediaViewer x:Name="libVLCSharpView" WidthRequest="300" HeightRequest="300" HorizontalOptions="CenterAndExpand" StreamUrl="{Binding URLSource,Source={x:Reference MyCustomView}}"/>-->
|
||||
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1"/>
|
||||
<toolkit:MediaElement x:Name="mediaElement"/>
|
||||
</StackLayout>
|
||||
|
||||
</ContentPage>
|
||||
|
||||
@@ -1,111 +1,78 @@
|
||||
using LibVLCSharp.Shared;
|
||||
|
||||
namespace TV_Player.MAUI;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
|
||||
|
||||
public partial class PlayerPage : ContentPage
|
||||
{
|
||||
public static BindableProperty StreamUrlProperty = BindableProperty.Create(nameof(StreamUrl)
|
||||
, typeof(string)
|
||||
, typeof(MediaViewer)
|
||||
, ""
|
||||
, defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
private const string StreamUrl = "http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiOTcyIiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODQwMC92aWRlby5tM3U4P3Rva2V/tracks-v1a1/mono.m3u8?cid=972&did=23146&m=1&rid=23038&token=e3600e10faf0eab8aaf5a56c4d7ece19";
|
||||
//MediaPlayer _mediaPlayer;
|
||||
private const int RefreshIntervalMs = 1000; // Refresh interval in milliseconds
|
||||
private readonly string tempFilePath;
|
||||
private Timer timer;
|
||||
|
||||
public string StreamUrl
|
||||
{
|
||||
get => (string)GetValue(StreamUrlProperty);
|
||||
set
|
||||
{
|
||||
SetValue(StreamUrlProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
LibVLC _libVLC;
|
||||
MediaPlayer _mediaPlayer;
|
||||
|
||||
|
||||
public VideoPlayer()
|
||||
public PlayerPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_libVLC = new LibVLC(enableDebugLogs: true);
|
||||
_mediaPlayer = new MediaPlayer(_libVLC);
|
||||
tempFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "temp_media.mp4");
|
||||
StartStreaming();
|
||||
}
|
||||
|
||||
VideoView.Loaded += (sender, e) =>
|
||||
private void StartStreaming()
|
||||
{
|
||||
try
|
||||
{
|
||||
VideoView.MediaPlayer = _mediaPlayer;
|
||||
VideoView.MouseLeftButtonDown += VideoView_MouseLeftButtonDown;
|
||||
VideoView.MediaPlayer.EnableMouseInput = false;
|
||||
VideoView.PreviewMouseLeftButtonDown += VideoView_MouseLeftButtonDown;
|
||||
AutoPlay();
|
||||
};
|
||||
Unloaded += VideoPlayer_Unloaded;
|
||||
|
||||
}
|
||||
|
||||
private void VideoView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
ToggleOverlay();
|
||||
}
|
||||
|
||||
private void VideoPlayer_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
VideoView.Dispose();
|
||||
}
|
||||
|
||||
|
||||
void PauseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (VideoView.MediaPlayer.IsPlaying)
|
||||
// Initialize the timer
|
||||
timer = new Timer(RefreshIntervalMs);
|
||||
timer.Elapsed += async (sender, e) => await UpdateTempFile();
|
||||
timer.AutoReset = true;
|
||||
timer.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
VideoView.MediaPlayer.Pause();
|
||||
Console.WriteLine($"Error starting stream: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoPlay()
|
||||
private async Task UpdateTempFile()
|
||||
{
|
||||
if (!VideoView.MediaPlayer.IsPlaying)
|
||||
try
|
||||
{
|
||||
// Download the stream data
|
||||
byte[] streamData = await DownloadStreamData(StreamUrl);
|
||||
|
||||
using (var media = new Media(_libVLC, new Uri(SourceUrl)))
|
||||
VideoView.MediaPlayer.Play(media);
|
||||
// Write the stream data to the temporary file
|
||||
if (streamData != null)
|
||||
{
|
||||
await File.WriteAllBytesAsync(tempFilePath, streamData);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error updating temporary file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void MyUserControl_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
private async Task<byte[]> DownloadStreamData(string streamUrl)
|
||||
{
|
||||
ToggleOverlay();
|
||||
}
|
||||
|
||||
private void MyUserControl_TouchDown(object sender, TouchEventArgs e)
|
||||
{
|
||||
ToggleOverlay();
|
||||
}
|
||||
|
||||
private void ToggleOverlay()
|
||||
{
|
||||
if (overlayPanel.Visibility == Visibility.Visible)
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
HideOverlay();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowOverlay();
|
||||
// Download the stream asynchronously
|
||||
return await client.GetByteArrayAsync(streamUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowOverlay()
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
overlayPanel.Visibility = Visibility.Visible;
|
||||
base.OnAppearing();
|
||||
// Set the MediaElement source to the temporary file when the page appears
|
||||
mediaElement.Source = tempFilePath;
|
||||
mediaElement.ShouldAutoPlay = true; // Auto-play the media
|
||||
}
|
||||
|
||||
public void HideOverlay()
|
||||
{
|
||||
overlayPanel.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
VideoView.MediaPlayer?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,23 +46,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="3.0.1" />
|
||||
<PackageReference Include="LibVLCSharp" Version="3.8.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
|
||||
<PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||
<PackageReference Include="System.IO" Version="4.3.0" />
|
||||
<PackageReference Include="System.Reactive" Version="6.0.0" />
|
||||
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-ios'">
|
||||
<PackageReference Include="CommunityToolkit.Maui">
|
||||
<Version>7.0.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-windows10.0.19041.0'">
|
||||
<PackageReference Include="CommunityToolkit.Maui">
|
||||
<Version>7.0.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -86,4 +73,11 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.Maui.Controls" Version="8.0.20" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.Maui.Controls.Compatibility" Version="8.0.20" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
[Serializable]
|
||||
public class GroupInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -1,14 +1,34 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
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 StopTime { 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<ProgramInfo> Programs { get; set; } = new List<ProgramInfo>();
|
||||
}
|
||||
public static class M3UParser
|
||||
{
|
||||
public static async Task<List<M3UInfo>> DownloadM3UFromWebAsync(string url)
|
||||
{
|
||||
List<M3UInfo> playlistItems = new List<M3UInfo>();
|
||||
|
||||
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())
|
||||
{
|
||||
@@ -18,14 +38,84 @@ namespace TV_Player.MAUI
|
||||
var response = await client.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Parse M3U content
|
||||
playlistItems = ParseM3UFromString(responseBody);
|
||||
epgChannels = ParseEpg(responseBody);
|
||||
}
|
||||
return playlistItems;
|
||||
return epgChannels;
|
||||
}
|
||||
|
||||
static string[] SplitStringBeforeSeparator(string input, string separator)
|
||||
private static List<ProgramGuide> ParseEpg(string epgData)
|
||||
{
|
||||
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))
|
||||
{
|
||||
try
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element && reader.Name == "channel")
|
||||
{
|
||||
ProgramGuide channel = new ProgramGuide();
|
||||
channel.Id = reader.GetAttribute("id");
|
||||
reader.Read();
|
||||
channel.DisplayName = reader.ReadElementContentAsString();
|
||||
|
||||
epgChannels.Add(channel);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reader.NodeType == XmlNodeType.Element && reader.Name == "programme")
|
||||
{
|
||||
ProgramInfo program = new ProgramInfo();
|
||||
|
||||
var id = reader.GetAttribute("channel");
|
||||
var channel = epgChannels.FirstOrDefault(x => x.Id == id);
|
||||
program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
||||
program.StopTime = 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{}
|
||||
}
|
||||
return epgChannels;
|
||||
}
|
||||
|
||||
private static async Task<string> 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<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
|
||||
{
|
||||
var fileData=await ReadFile(url);
|
||||
// Parse M3U content
|
||||
return ParseM3UFromString(fileData);
|
||||
}
|
||||
private static string[] SplitStringBeforeSeparator(string input, string separator)
|
||||
{
|
||||
string[] parts = input.Split(separator);
|
||||
|
||||
@@ -43,10 +133,10 @@ namespace TV_Player.MAUI
|
||||
return parts;
|
||||
}
|
||||
|
||||
static List<M3UInfo> ParseM3UFromString(string content)
|
||||
private static (List<M3UInfo> programList, string programGuide) ParseM3UFromString(string content)
|
||||
{
|
||||
List<M3UInfo> playlistItems = new List<M3UInfo>();
|
||||
|
||||
string programGuideLink = string.Empty;
|
||||
try
|
||||
{
|
||||
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
||||
@@ -60,6 +150,10 @@ namespace TV_Player.MAUI
|
||||
playlistItems.Add(m3uInfo);
|
||||
}
|
||||
}
|
||||
if (line.StartsWith("#EXTM3U"))
|
||||
{
|
||||
programGuideLink=ExtractXtvgUrl(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -67,11 +161,10 @@ namespace TV_Player.MAUI
|
||||
Console.WriteLine("Error reading M3U file: " + ex.Message);
|
||||
}
|
||||
|
||||
return playlistItems;
|
||||
return (playlistItems,programGuideLink);
|
||||
}
|
||||
|
||||
|
||||
static bool TryParseM3ULine(string m3uLine, out M3UInfo? info)
|
||||
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>.*)$";
|
||||
@@ -96,5 +189,23 @@ namespace TV_Player.MAUI
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string ExtractXtvgUrl(string m3uEntry)
|
||||
{
|
||||
// Define a regular expression pattern to match x-tvg-url attribute
|
||||
string pattern = @"x-tvg-url=""(.*?)""";
|
||||
|
||||
// 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[1].Value;
|
||||
}
|
||||
|
||||
// Return null or an empty string if no match is found
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace TV_Player.MAUI
|
||||
{
|
||||
public class MainViewModel : ObservableViewModelBase
|
||||
{
|
||||
private IDisposable _groupsSubscriber;
|
||||
private List<GroupInfo> _programs;
|
||||
public List<GroupInfo> Programs
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace TV_Player.MAUI
|
||||
public MainViewModel()
|
||||
{
|
||||
ItemSelectedCommand = new Command(OnItemSelected);
|
||||
ProgramsData.Instance.GroupsInformation.Subscribe(x=>Programs = x);
|
||||
_groupsSubscriber = TVPlayerViewModel.Instance.PlaylistData.GroupsInformation.Subscribe(x => Programs = x);
|
||||
}
|
||||
|
||||
private void OnItemSelected()
|
||||
@@ -33,6 +34,8 @@ namespace TV_Player.MAUI
|
||||
};
|
||||
// Navigate to the OtherPage
|
||||
navigation.PushAsync(programPage);
|
||||
|
||||
_groupsSubscriber.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using LibVLCSharp.Shared;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
@@ -26,6 +25,7 @@ namespace TV_Player.MAUI
|
||||
//_libVLC = new LibVLC();
|
||||
//_mediaPlayer = new MediaPlayer(new Media(_libVLC, new Uri(_currentProgram.Url)));
|
||||
//_mediaPlayer.Play();
|
||||
URLSource = _currentProgram.Url;
|
||||
PlayCommand = new Command(OnPlayButtonClicked);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace TV_Player.MAUI
|
||||
{
|
||||
URLSource = _currentProgram.Url;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
// Handle exceptions
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace TV_Player.MAUI
|
||||
{
|
||||
public class ProgramViewModel : ObservableViewModelBase
|
||||
{
|
||||
private IDisposable _programSubscriber;
|
||||
private List<M3UInfo> _programs;
|
||||
public List<M3UInfo> Programs
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace TV_Player.MAUI
|
||||
public ProgramViewModel(GroupInfo groupInfo)
|
||||
{
|
||||
ItemSelectedCommand = new Command(OnItemSelected);
|
||||
ProgramsData.Instance.AllPrograms.Subscribe(x=>Programs = x.Where(p=>p.GroupTitle== groupInfo.Name).ToList());
|
||||
_programSubscriber = TVPlayerViewModel.Instance.PlaylistData.AllPrograms.Subscribe(x => Programs = x.Where(p => p.GroupTitle == groupInfo.Name).ToList());
|
||||
}
|
||||
|
||||
private void OnItemSelected()
|
||||
@@ -33,6 +34,8 @@ namespace TV_Player.MAUI
|
||||
};
|
||||
// Navigate to the OtherPage
|
||||
navigation.PushAsync(playerPage);
|
||||
|
||||
_programSubscriber.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,36 +4,42 @@ namespace TV_Player.MAUI
|
||||
{
|
||||
public class ProgramsData
|
||||
{
|
||||
private static ProgramsData _instance;
|
||||
public static ProgramsData Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
_instance ??= new ProgramsData();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
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>>();
|
||||
public IObservable<List<M3UInfo>> AllPrograms => programsSubject;
|
||||
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
|
||||
|
||||
private ProgramsData()
|
||||
{
|
||||
_=Initialize();
|
||||
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
|
||||
public ProgramsData()
|
||||
{
|
||||
}
|
||||
private async Task Initialize()
|
||||
|
||||
private async Task GetPrograms(string m3uLink)
|
||||
{
|
||||
string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
|
||||
var programs = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
|
||||
//string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
|
||||
var result = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
|
||||
|
||||
programsSubject.OnNext(result.programList);
|
||||
|
||||
programsSubject.OnNext(programs);
|
||||
|
||||
var groupping = programs.GroupBy(item => item.GroupTitle)
|
||||
var groupping = result.programList.GroupBy(item => item.GroupTitle)
|
||||
.Select(group => new GroupInfo() { Name = group.Key, Count = group.Count() })
|
||||
.ToList();
|
||||
groupsSubject.OnNext(groupping);
|
||||
|
||||
await Task.Run(() => GetProgramGuide(result.programGuide));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
internal void GetData(string playlistURL)
|
||||
{
|
||||
Task.Run(() => GetPrograms(playlistURL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
namespace TV_Player.MAUI
|
||||
{
|
||||
public class TVPlayerViewModel
|
||||
{
|
||||
public ProgramsData PlaylistData { get; private set; }
|
||||
|
||||
public Action ButtonBackAction { get; set; }
|
||||
|
||||
private static TVPlayerViewModel _instance;
|
||||
public static TVPlayerViewModel Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = new TVPlayerViewModel();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public TVPlayerViewModel()
|
||||
{
|
||||
PlaylistData = new ProgramsData();
|
||||
PlaylistData.GetData("http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user