more design
This commit is contained in:
@@ -45,6 +45,20 @@ c-0.781-0.781-0.788-2.047-0.007-2.828L51.438,14.43c1.754-1.755,1.753-4.61-0.001-
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ButtonFullScreen" TargetType="{x:Type Button}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Background="Yellow" Height="50" Width="50" CornerRadius="300">
|
||||
<Viewbox Margin="0,6,5,5">
|
||||
<Path VerticalAlignment="Center" HorizontalAlignment="Center" Fill="Gray" Data="M18 4.654v.291a10 10 0 0 0-1.763 1.404l-2.944 2.944a1 1 0 0 0 1.414 1.414l2.933-2.932A9.995 9.995 0 0 0 19.05 6h.296l-.09.39A9.998 9.998 0 0 0 19 8.64v.857a1 1 0 1 0 2 0V4.503a1.5 1.5 0 0 0-1.5-1.5L14.5 3a1 1 0 1 0 0 2h.861a10 10 0 0 0 2.249-.256l.39-.09zM4.95 18a10 10 0 0 1 1.41-1.775l2.933-2.932a1 1 0 0 1 1.414 1.414l-2.944 2.944A9.998 9.998 0 0 1 6 19.055v.291l.39-.09A9.998 9.998 0 0 1 8.64 19H9.5a1 1 0 1 1 0 2l-5-.003a1.5 1.5 0 0 1-1.5-1.5V14.5a1 1 0 1 1 2 0v.861a10 10 0 0 1-.256 2.249l-.09.39h.295z" Stretch="Fill" Width="50"/>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ButtonGear" TargetType="{x:Type Button}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:TV_Player"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d" WindowStyle="{Binding CurrentWindowStyle}" WindowState="{Binding CurrentWindowState}"
|
||||
Title="TV" Height="450" Width="800">
|
||||
<Window.Resources>
|
||||
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverterKey"/>
|
||||
@@ -25,11 +25,13 @@
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="70"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonGear}" />
|
||||
<Button Grid.Column="1" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
|
||||
<TextBlock Grid.Column="2" FontSize="20" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center"/>
|
||||
<Button Grid.Column="4" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
|
||||
</Grid>
|
||||
<ContentControl Grid.Row="1" Name="ControlContainer" Content="{Binding Control}" />
|
||||
</Grid>
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace TV_Player
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -9,18 +9,37 @@
|
||||
Unloaded="UserControl_Unloaded">
|
||||
<Grid>
|
||||
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1">
|
||||
<StackPanel Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF"
|
||||
<Grid Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF" VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown"
|
||||
TouchDown="MyUserControl_TouchDown" >
|
||||
<Grid x:Name="overlayPanel" Visibility="Collapsed" Background="#70000000" Height="70" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
|
||||
<TextBlock Grid.Column="1" FontSize="20" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Grid x:Name="overlayPanel" Visibility="Visible">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="70"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="70"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Background="#70000000" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
|
||||
<TextBlock Grid.Column="1" FontSize="20" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2" Background="#70000000">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock FontSize="20" Foreground="White" Text="{Binding ProgramGuideText}" HorizontalAlignment="Center" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1">
|
||||
<TextBlock FontSize="10" Foreground="White" Text="{Binding StartProgram}" HorizontalAlignment="Center" />
|
||||
<ProgressBar Height="10" Foreground="Yellow" Name="progressBar" Value="{Binding DurationValue}" Maximum="100" VerticalAlignment="Center" HorizontalAlignment="Center" Width="400"/>
|
||||
<TextBlock FontSize="10" Foreground="White" Text="{Binding EndProgram}" HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</vlc:VideoView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,11 +1,109 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace TV_Player
|
||||
{
|
||||
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<ProgramGuide>> DownloadGuideFromWebAsync(string url)
|
||||
{
|
||||
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ;
|
||||
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);
|
||||
}
|
||||
return epgChannels;
|
||||
}
|
||||
|
||||
|
||||
private static List<ProgramGuide> ParseEpg(string epgData)
|
||||
{
|
||||
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
|
||||
|
||||
using (XmlReader reader = XmlReader.Create(new System.IO.StringReader(epgData)))
|
||||
{
|
||||
ProgramGuide currentChannel = null;
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(epgData);
|
||||
|
||||
XmlNodeList channelNodes = doc.SelectNodes("//channel");
|
||||
|
||||
foreach (XmlNode channelNode in channelNodes)
|
||||
{
|
||||
ProgramGuide channel = new ProgramGuide();
|
||||
channel.Id = channelNode.Attributes["id"].Value;
|
||||
channel.DisplayName = channelNode.SelectSingleNode("display-name").InnerText;
|
||||
|
||||
XmlNodeList programNodes = doc.SelectNodes($"//programme[@channel='{channel.Id}']");
|
||||
foreach (XmlNode programNode in programNodes)
|
||||
{
|
||||
ProgramInfo program = new ProgramInfo();
|
||||
program.Title = programNode.SelectSingleNode("title").InnerText;
|
||||
program.StartTime = DateTime.ParseExact(programNode.Attributes["start"].Value, "yyyyMMddHHmmss zzz", null);
|
||||
program.StopTime = DateTime.ParseExact(programNode.Attributes["stop"].Value, "yyyyMMddHHmmss zzz", null);
|
||||
|
||||
channel.Programs.Add(program);
|
||||
}
|
||||
|
||||
epgChannels.Add(channel);
|
||||
}
|
||||
//while (reader.Read())
|
||||
//{
|
||||
// if (reader.NodeType == XmlNodeType.Element)
|
||||
// {
|
||||
// if (reader.Name == "channel")
|
||||
// {
|
||||
// currentChannel = new ProgramGuide();
|
||||
// currentChannel.Id = reader.GetAttribute("id");
|
||||
// currentChannel.DisplayName = reader.ReadElementContentAsString();
|
||||
// epgChannels.Add(currentChannel);
|
||||
// }
|
||||
// else if (reader.Name == "programme" && currentChannel != null)
|
||||
// {
|
||||
// ProgramInfo program = new ProgramInfo();
|
||||
// program.Title = reader.ReadElementContentAsString();
|
||||
// program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
||||
// program.StopTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
||||
// currentChannel.Programs.Add(program);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
return epgChannels;
|
||||
}
|
||||
|
||||
|
||||
public static async Task<List<M3UInfo>> DownloadM3UFromWebAsync(string url)
|
||||
{
|
||||
List<M3UInfo> playlistItems = new List<M3UInfo>();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
@@ -14,7 +15,8 @@ namespace TV_Player
|
||||
}
|
||||
|
||||
private bool _isTopPanelVisible;
|
||||
public bool IsTopPanelVisible{
|
||||
public bool IsTopPanelVisible
|
||||
{
|
||||
get => _isTopPanelVisible;
|
||||
set => SetProperty(ref _isTopPanelVisible, value);
|
||||
}
|
||||
@@ -26,15 +28,51 @@ namespace TV_Player
|
||||
set => SetProperty(ref _topPaneTitle, value);
|
||||
}
|
||||
|
||||
private WindowState _currentWindowState;
|
||||
public WindowState CurrentWindowState
|
||||
{
|
||||
get => _currentWindowState;
|
||||
set => SetProperty(ref _currentWindowState, value);
|
||||
}
|
||||
|
||||
private WindowStyle _currentWindowStyle;
|
||||
public WindowStyle CurrentWindowStyle
|
||||
{
|
||||
get => _currentWindowStyle;
|
||||
set => SetProperty(ref _currentWindowStyle, value);
|
||||
}
|
||||
|
||||
public ICommand FullscreenCommand { get; }
|
||||
|
||||
public Action ButtonBackAction { get; set; }
|
||||
public ICommand BackCommand { get; }
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
{
|
||||
|
||||
BackCommand = new RelayCommand(OnButtonBackClick);
|
||||
FullscreenCommand = new RelayCommand(OnFullSctreenButtonClick);
|
||||
|
||||
CurrentWindowStyle = WindowStyle.SingleBorderWindow;
|
||||
}
|
||||
|
||||
private void OnFullSctreenButtonClick()
|
||||
{
|
||||
if (CurrentWindowStyle == WindowStyle.SingleBorderWindow)
|
||||
{
|
||||
CurrentWindowStyle = WindowStyle.None;
|
||||
CurrentWindowState = WindowState.Maximized;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentWindowStyle = WindowStyle.SingleBorderWindow;
|
||||
CurrentWindowState = WindowState.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonBackClick()
|
||||
{
|
||||
|
||||
ButtonBackAction?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using TV_Player.ViewModels;
|
||||
|
||||
namespace TV_Player
|
||||
{
|
||||
|
||||
public class PlayerViewModel: ObservableViewModelBase
|
||||
|
||||
public class PlayerViewModel : ObservableViewModelBase, IDisposable
|
||||
{
|
||||
|
||||
|
||||
private M3UInfo _currentProgram;
|
||||
public M3UInfo SelectedProgram
|
||||
{
|
||||
@@ -23,17 +22,82 @@ namespace TV_Player
|
||||
set => SetProperty(ref _topPaneTitle, value);
|
||||
}
|
||||
|
||||
private int _durationValue;
|
||||
public int DurationValue
|
||||
{
|
||||
get => _durationValue;
|
||||
set => SetProperty(ref _durationValue, value);
|
||||
}
|
||||
|
||||
private string _programGuide;
|
||||
public string ProgramGuideText
|
||||
{
|
||||
get => _programGuide;
|
||||
set => SetProperty(ref _programGuide, value);
|
||||
}
|
||||
|
||||
private string _startProgram;
|
||||
public string StartProgram
|
||||
{
|
||||
get => _startProgram;
|
||||
set => SetProperty(ref _startProgram, value);
|
||||
}
|
||||
|
||||
private string _endProgram;
|
||||
public string EndProgram
|
||||
{
|
||||
get => _endProgram;
|
||||
set => SetProperty(ref _endProgram, value);
|
||||
}
|
||||
|
||||
|
||||
public M3UInfo SelectedItem { get; set; }
|
||||
public ICommand BackCommand { get; }
|
||||
|
||||
private ProgramGuide _currentGuide;
|
||||
private ProgramInfo _currentProgramInfo;
|
||||
|
||||
private IDisposable _programGuideDisposable;
|
||||
private IDisposable _timer;
|
||||
public PlayerViewModel(M3UInfo selectedProgram)
|
||||
{
|
||||
_currentProgram = selectedProgram;
|
||||
BackCommand = new RelayCommand(OnButtonBackClick);
|
||||
TVPlayerViewModel.Instance.TopPanelVisible(false, selectedProgram.Name);
|
||||
|
||||
TopPanelTitle = selectedProgram.Name;
|
||||
|
||||
_programGuideDisposable = ProgramsData.Instance.ProgramGuideInfo.Subscribe(x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentGuide = x.FirstOrDefault(p => p.Id == selectedProgram.TvgID);
|
||||
UpdateScreenInfo();
|
||||
|
||||
_timer = Observable.Interval(TimeSpan.FromMinutes(1)).Subscribe(x =>
|
||||
{
|
||||
UpdateScreenInfo();
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void UpdateScreenInfo()
|
||||
{
|
||||
_currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now);
|
||||
if (_currentProgramInfo.Title != ProgramGuideText)
|
||||
{
|
||||
ProgramGuideText = _currentProgramInfo.Title;
|
||||
StartProgram = _currentProgramInfo.StartTime.ToShortTimeString();
|
||||
EndProgram = _currentProgramInfo.StopTime.ToShortTimeString();
|
||||
}
|
||||
var programMinutes = (_currentProgramInfo.StopTime - _currentProgramInfo.StartTime).TotalMinutes;
|
||||
DurationValue = (int)((DateTime.Now - _currentProgramInfo.StartTime).TotalMinutes / programMinutes * 100);
|
||||
}
|
||||
|
||||
|
||||
private void OnButtonBackClick()
|
||||
{
|
||||
var groupInfo = new GroupInfo() { Name = _currentProgram.GroupTitle, Count = 0 };
|
||||
@@ -43,5 +107,10 @@ namespace TV_Player
|
||||
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_programGuideDisposable.Dispose();
|
||||
_timer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,17 @@ 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>>();
|
||||
public IObservable<List<M3UInfo>> AllPrograms => programsSubject;
|
||||
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
|
||||
|
||||
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
|
||||
private ProgramsData()
|
||||
{
|
||||
_=Initialize();
|
||||
Task.Run(() => GetPrograms());
|
||||
Task.Run(() => GetProgramGuide());
|
||||
}
|
||||
private async Task Initialize()
|
||||
private async Task GetPrograms()
|
||||
{
|
||||
string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
|
||||
var programs = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
|
||||
@@ -35,5 +38,12 @@ namespace TV_Player
|
||||
.ToList();
|
||||
groupsSubject.OnNext(groupping);
|
||||
}
|
||||
|
||||
private async Task GetProgramGuide()
|
||||
{
|
||||
string guideLink = "http://epg.da-tv.vip/107-light.xml";
|
||||
var programGuide = await M3UParser.DownloadGuideFromWebAsync(guideLink);
|
||||
programGuideSubject.OnNext(programGuide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace TV_Player.ViewModels
|
||||
|
||||
public void SetPageContext(ContentControl control, object viewModel)
|
||||
{
|
||||
if (_mainViewModel.Control is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
control.DataContext = viewModel;
|
||||
_mainViewModel.Control = control;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user