add settings screen

get playlist from settings, in first run - open Settings
This commit is contained in:
Vova
2024-01-30 16:31:11 +02:00
parent d8365d2b1f
commit 9b8e8a835a
18 changed files with 527 additions and 228 deletions
@@ -216,4 +216,82 @@ 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.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="YellowGradient" >
<GradientStop Offset="0" Color="LightGray"/>
<GradientStop Offset="1" Color="Gray"/>
</LinearGradientBrush>
<Style x:Key="{x:Type CheckBox}" TargetType="CheckBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Background" Value="{StaticResource YellowGradient}" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="FontSize" Value="18" />
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="#FF03428f" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Grid FlowDirection="LeftToRight">
<Border Name="Border"
Background="White"
CornerRadius="1"
BorderBrush="#FF92c8e5"
BorderThickness="2"
Width="46"
Height="38"
>
<Border
Name="ShadowBorder"
CornerRadius="2"
Padding="2"
ClipToBounds="True"
Background="Transparent"
BorderBrush="#FF92c8e5"
Margin="-2,-2,0,0"
BorderThickness="2,2,0,0" >
<Border.Effect>
<DropShadowEffect ShadowDepth="3" BlurRadius="7"/>
</Border.Effect>
</Border>
</Border>
<Path x:Name="CheckMark" SnapsToDevicePixels="False" Data="F1M1,14.489C1,14.489 4.963,10.525 4.963,10.525 4.963,10.525 11.328,16.89 11.328,16.89 11.328,16.89 27.034,1.183 27.034,1.183 27.034,1.183 31,5.146 31,5.146 31,5.146 11.328,24.817 11.328,24.817 11.328,24.817 1,14.489 1,14.489z" Fill="#FFFFFFFF" Height="26" Canvas.Left="0" Canvas.Top="0" Width="32"/>
</Grid>
</BulletDecorator.Bullet>
<ContentPresenter Margin="4,0,0,0" TextBlock.Foreground="{TemplateBinding Foreground}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
RecognizesAccessKey="True"/>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{Binding Background,RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type CheckBox}}}" />
<Setter TargetName="ShadowBorder" Property="Visibility" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TextBox" TargetType="TextBox">
<Setter Property="FontFamily" Value="Arial"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="FontSize" Value="25"></Setter>
<Setter Property="Foreground" Value="Gray"></Setter>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="TextAlignment" Value="Left"></Setter>
</Style>
</ResourceDictionary>
+7 -7
View File
@@ -20,17 +20,17 @@
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Visibility="{Binding IsTopPanelVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}" Height="70">
<Grid Visibility="{Binding IsTopPanelVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}" Height="80">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="80"/>
</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}" />
<Button Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonGear}" Command="{Binding SettingsCommand}" />
<Button Grid.Column="1" Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
<TextBlock Grid.Column="2" FontSize="30" Foreground="White" Text="{Binding TopPanelTitle}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Grid.Column="4" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
<Button Grid.Column="4" Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
</Grid>
<ContentControl Grid.Row="1" Name="ControlContainer" Content="{Binding Control}" />
</Grid>
+18
View File
@@ -0,0 +1,18 @@
<UserControl x:Class="TV_Player.Settings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:tv_player="clr-namespace:TV_Player"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel x:Name="groupsGrid" VerticalAlignment="Center">
<Label Margin="10" Foreground="White" FontSize="25" FontWeight="Bold" FontFamily="Arial">Адрес плейлиста</Label>
<TextBox Margin="10" Style="{StaticResource TextBox}" HorizontalAlignment="Stretch" Text="{Binding PlaylistURL}"></TextBox>
<CheckBox Margin="10" Foreground="White" FontSize="25" IsChecked="{Binding StartFullScreen}">Откывать во весь экран</CheckBox>
<CheckBox Margin="10" Foreground="White" FontSize="25" IsChecked="{Binding StartLastScreen}">Запоминать последний выбор</CheckBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Grid.Column="1" Height="70" Width="70" Margin="10,0,50,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
<Button HorizontalAlignment="Center" FontSize="25" Command="{Binding SaveCommand}">Сохранить</Button>
</StackPanel>
</StackPanel>
</UserControl>
+15
View File
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace TV_Player
{
/// <summary>
/// Interaction logic for ProgramsGroupGrid.xaml
/// </summary>
public partial class Settings : UserControl
{
public Settings()
{
InitializeComponent();
}
}
}
+4
View File
@@ -19,6 +19,7 @@
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="LibVLCSharp.WPF" Version="3.8.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
</ItemGroup>
@@ -30,6 +31,9 @@
</ItemGroup>
<ItemGroup>
<Compile Update="Settings.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="VideoPlayer.xaml.cs">
<SubType>Code</SubType>
</Compile>
+20 -15
View File
@@ -4,47 +4,51 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TV_Player"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Unloaded="UserControl_Unloaded">
<UserControl.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverterKey"/>
</UserControl.Resources>
<Grid>
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1">
<Grid Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown">
<Grid x:Name="overlayPanel" Visibility="Visible">
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="80"/>
<RowDefinition Height="*"/>
<RowDefinition Height="70"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<Grid Background="#70000000" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
<Button Height="50" Width="70" 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"/>
<Button Grid.Column="4" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
<Button Grid.Column="4" Height="70" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonFullScreen}" Command="{Binding FullscreenCommand}" />
</Grid>
<Grid Grid.Row="2" Background="#70000000">
<Grid Grid.Row="2" Background="#B0000000">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.RowSpan="2" Orientation="Horizontal" >
<Button Grid.RowSpan="2" Height="70" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonDown}" Command="{Binding PreviousCommand}" />
<TextBlock FontSize="15" Foreground="White" Text="Ch" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Grid.RowSpan="2" Height="70" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonUp}" Command="{Binding NextCommand}" />
</StackPanel>
<Grid Grid.Column="1" x:Name="programInfo" Visibility="{Binding IsProgramInfoVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.RowSpan="2" Orientation="Horizontal" >
<Button Grid.RowSpan="2" Height="50" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonDown}" Command="{Binding PreviousCommand}" />
<TextBlock FontSize="15" Foreground="White" Text="Ch" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Grid.RowSpan="2" Height="50" Width="50" Margin="10,0,10,0" Style="{DynamicResource ButtonUp}" Command="{Binding NextCommand}" />
</StackPanel>
<TextBlock Grid.Column="1" FontSize="20" Foreground="White" Text="{Binding ProgramGuideText}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Column="1" Grid.Row="1">
<TextBlock FontSize="20" Foreground="White" Text="{Binding ProgramGuideText}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1">
<TextBlock FontSize="15" 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="15" Foreground="White" Text="{Binding EndProgram}" HorizontalAlignment="Center" />
@@ -52,6 +56,7 @@
</Grid>
</Grid>
</Grid>
</Grid>
</vlc:VideoView>
</Grid>
</UserControl>
+15 -1
View File
@@ -3,6 +3,7 @@ using LibVLCSharp.WPF;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace TV_Player
{
@@ -16,11 +17,17 @@ namespace TV_Player
private LibVLC _libVLC;
private MediaPlayer _mediaPlayer;
private PlayerViewModel _viewModel;
private DispatcherTimer _overlayAutoHideTimer;
public VideoPlayer()
{
InitializeComponent();
_overlayAutoHideTimer = new DispatcherTimer();
_overlayAutoHideTimer.Interval = TimeSpan.FromSeconds(3);
_overlayAutoHideTimer.Tick += _overlayAutoHideTimer_Tick;
_overlayAutoHideTimer.Start();
_libVLC = new LibVLC(enableDebugLogs: true);
_mediaPlayer = new MediaPlayer(_libVLC);
this.DataContextChanged += (sender, e) =>
@@ -29,7 +36,6 @@ namespace TV_Player
_viewModel.SourceUrlChangedEvent += _viewModel_SourceUrlChangedEvent;
};
VideoView.Loaded += (sender, e) =>
{
VideoView.MediaPlayer = _mediaPlayer;
@@ -90,12 +96,20 @@ namespace TV_Player
ShowOverlay();
}
}
private void _overlayAutoHideTimer_Tick(object? sender, EventArgs e)
{
HideOverlay();
}
public void ShowOverlay()
{
_overlayAutoHideTimer.Start();
overlayPanel.Visibility = Visibility.Visible;
}
public void HideOverlay()
{
_overlayAutoHideTimer.Stop();
overlayPanel.Visibility = Visibility.Collapsed;
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
+1
View File
@@ -1,5 +1,6 @@
namespace TV_Player
{
[Serializable]
public class GroupInfo
{
public string Name { get; set; }
+41 -50
View File
@@ -1,7 +1,6 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using System.Threading.Channels;
using System.Xml;
namespace TV_Player
@@ -28,7 +27,6 @@ namespace TV_Player
public static class M3UParser
{
public static async Task<List<ProgramGuide>> DownloadGuideFromWebAsync(string url)
{
List<ProgramGuide> epgChannels = new List<ProgramGuide>(); ;
@@ -46,7 +44,6 @@ namespace TV_Player
return epgChannels;
}
private static List<ProgramGuide> ParseEpg(string epgData)
{
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
@@ -75,7 +72,7 @@ namespace TV_Player
{
ProgramInfo program = new ProgramInfo();
var id=reader.GetAttribute("channel");
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);
@@ -92,46 +89,16 @@ namespace TV_Player
}
}
}
catch (Exception ex) {
catch (Exception ex)
{
}
}
//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);
// }
//}
return epgChannels;
}
public static async Task<List<M3UInfo>> DownloadM3UFromWebAsync(string url)
private static async Task<string> ReadFile(string url)
{
List<M3UInfo> playlistItems = new List<M3UInfo>();
string responseBody;
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
@@ -140,15 +107,18 @@ namespace TV_Player
request.RequestUri = new Uri(url);
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
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
playlistItems = ParseM3UFromString(responseBody);
return ParseM3UFromString(fileData);
}
return playlistItems;
}
static string[] SplitStringBeforeSeparator(string input, string separator)
private static string[] SplitStringBeforeSeparator(string input, string separator)
{
string[] parts = input.Split(separator);
@@ -166,10 +136,10 @@ namespace TV_Player
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");
@@ -183,6 +153,10 @@ namespace TV_Player
playlistItems.Add(m3uInfo);
}
}
if (line.StartsWith("#EXTM3U"))
{
programGuideLink=ExtractXtvgUrl(line);
}
}
}
catch (Exception ex)
@@ -190,11 +164,10 @@ namespace TV_Player
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>.*)$";
@@ -219,5 +192,23 @@ namespace TV_Player
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;
}
}
}
@@ -2,6 +2,7 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using TV_Player.ViewModels;
namespace TV_Player
{
@@ -48,11 +49,14 @@ namespace TV_Player
public Action ButtonBackAction { get; set; }
public ICommand BackCommand { get; }
public ICommand SettingsCommand{ get; }
public MainViewModel()
{
BackCommand = new RelayCommand(OnButtonBackClick);
FullscreenCommand = new RelayCommand(OnFullSctreenButtonClick);
SettingsCommand = new RelayCommand(OnSettingsButtonClick);
CurrentWindowStyle = WindowStyle.SingleBorderWindow;
}
@@ -76,5 +80,10 @@ namespace TV_Player
ButtonBackAction?.Invoke();
}
private void OnSettingsButtonClick()
{
TVPlayerViewModel.Instance.ShowSettingsScreen();
}
}
}
+25 -8
View File
@@ -13,6 +13,13 @@ namespace TV_Player
private M3UInfo _currentProgram;
private bool _isProgramInfoVisible;
public bool IsProgramInfoVisible
{
get => _isProgramInfoVisible;
set => SetProperty(ref _isProgramInfoVisible, value);
}
private string _topPaneTitle;
public string TopPanelTitle
{
@@ -71,7 +78,7 @@ namespace TV_Player
PreviousCommand = new RelayCommand(PreviousProgram);
FullscreenCommand = new RelayCommand(TVPlayerViewModel.Instance.FullScreenToggle);
_programSubscriber = ProgramsData.Instance.AllPrograms.Subscribe(x =>
_programSubscriber = TVPlayerViewModel.Instance.PlaylistData.AllPrograms.Subscribe(x =>
{
_programs = x.Where(p => p.GroupTitle == _currentProgram.GroupTitle).ToList();
_currentProgramIndex = _programs.Select((program, index) => new { program, index })
@@ -89,7 +96,7 @@ namespace TV_Player
TopPanelTitle = _currentProgram.Name;
SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
_programGuideDisposable = ProgramsData.Instance.ProgramGuideInfo.Subscribe(x =>
_programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(x =>
{
try
{
@@ -125,23 +132,33 @@ namespace TV_Player
}
private void UpdateScreenInfo()
{
_currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now);
if (_currentProgramInfo.Title != ProgramGuideText)
try
{
_currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now);
if (_currentProgramInfo == null)
{
IsProgramInfoVisible = false;
}
else if (_currentProgramInfo.Title != ProgramGuideText)
{
IsProgramInfoVisible = true;
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);
}
}
catch
{ }
}
private void OnButtonBackClick()
{
var groupInfo = new GroupInfo() { Name = _currentProgram.GroupTitle, Count = 0 };
var programListViewModel = new ProgramsListViewModel(groupInfo);
var conrtrol = new ProgramsList();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
TVPlayerViewModel.Instance.ShowProgramsListScreen(groupInfo);
}
public void Dispose()
{
+16 -20
View File
@@ -4,16 +4,6 @@ namespace TV_Player
{
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>>();
@@ -21,29 +11,35 @@ namespace TV_Player
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
private ProgramsData()
public ProgramsData()
{
Task.Run(() => GetPrograms());
Task.Run(() => GetProgramGuide());
}
private async Task GetPrograms()
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(programs);
programsSubject.OnNext(result.programList);
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()
private async Task GetProgramGuide(string guideLink)
{
string guideLink = "http://epg.da-tv.vip/107-light.xml";
//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));
}
}
}
@@ -20,16 +20,14 @@ namespace TV_Player
public ProgramsGroupViewModel()
{
ItemSelectedCommand = new RelayCommand(OnItemSelected);
_groupInformationSubscriber = ProgramsData.Instance.GroupsInformation.Subscribe(x=>Programs = x);
_groupInformationSubscriber = TVPlayerViewModel.Instance.PlaylistData.GroupsInformation.Subscribe(x=>Programs = x);
TVPlayerViewModel.Instance.TopPanelVisible(true, "Группы");
}
private void OnItemSelected()
{
var programListViewModel = new ProgramsListViewModel(SelectedItem);
var conrtrol = new ProgramsList();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
TVPlayerViewModel.Instance.ShowProgramsListScreen(SelectedItem);
}
public void Dispose()
@@ -16,26 +16,22 @@ namespace TV_Player
public M3UInfo SelectedItem { get; set; }
public ICommand ItemSelectedCommand { get; }
private IDisposable _programSubscriber;
public ProgramsListViewModel(GroupInfo groupInfo)
{
TVPlayerViewModel.Instance.TopPanelVisible(true, groupInfo.Name);
ItemSelectedCommand = new RelayCommand(OnItemSelected);
_programSubscriber = 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());
TVPlayerViewModel.Instance.SetBackButtonAction(new Action(() =>
{
var programGroupViewModel = new ProgramsGroupViewModel();
var conrtrol = new ProgramsGroupGrid();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programGroupViewModel);
TVPlayerViewModel.Instance.ShowProgramsGroupScreen();
}));
}
private void OnItemSelected()
{
var playerViewModel = new PlayerViewModel(SelectedItem);
var conrtrol = new VideoPlayer();
conrtrol.SourceUrl = SelectedItem.Url;
TVPlayerViewModel.Instance.SetPageContext(conrtrol, playerViewModel);
TVPlayerViewModel.Instance.ShowPlayerScreen(SelectedItem);
}
public void Dispose()
-33
View File
@@ -1,33 +0,0 @@
using System.Windows.Input;
namespace TV_Player.ViewModels
{
//public class RelayCommand : ICommand
//{
// private readonly Action _execute;
// private readonly Func<bool> _canExecute;
// public event EventHandler CanExecuteChanged;
// public RelayCommand(Action execute, Func<bool> canExecute = null)
// {
// _execute = execute ?? throw new ArgumentNullException(nameof(execute));
// _canExecute = canExecute;
// }
// public bool CanExecute(object parameter)
// {
// return _canExecute == null || _canExecute();
// }
// public void Execute(object parameter)
// {
// _execute();
// }
// public void RaiseCanExecuteChanged()
// {
// CanExecuteChanged?.Invoke(this, EventArgs.Empty);
// }
//}
}
+64
View File
@@ -0,0 +1,64 @@
using Newtonsoft.Json;
using System.IO;
namespace TV_Player.ViewModels
{
public static class SettingsModel
{
private const string _filePath = "settings.json";
public static string PlaylistURL { get; set; }
public static bool StartFullScreen { get; set; }
public static bool StartFromLastScreen { get; set; }
public static string LastScreen { get; set; }
public static GroupInfo Group { get; set; }
public static M3UInfo Program { get; set; }
public static void SaveSetttings()
{
// Create an anonymous object to hold the properties
var dataToSerialize = new
{
PlaylistURL,
StartFromLastScreen,
StartFullScreen,
LastScreen,
Group,
Program
};
// Serialize the object to JSON
string json = JsonConvert.SerializeObject(dataToSerialize, Formatting.Indented);
// Save the JSON to a file
File.WriteAllText(_filePath, json);
}
public static void LoadSettings()
{
var loadedData = new
{
PlaylistURL = default(string),
LastScreen = default(string),
Group = default(GroupInfo),
Program = default(M3UInfo),
StartFromLastScreen = default(bool),
StartFullScreen = default(bool)
};
if (File.Exists(_filePath))
{
// Read the JSON content from the file
string json = File.ReadAllText(_filePath);
loadedData = JsonConvert.DeserializeAnonymousType(json, loadedData);
}
// Assign the values to the properties
PlaylistURL = loadedData.PlaylistURL;
LastScreen = loadedData.LastScreen;
Group = loadedData.Group;
Program = loadedData.Program;
StartFromLastScreen = loadedData.StartFromLastScreen;
StartFullScreen = loadedData.StartFullScreen;
}
}
}
@@ -0,0 +1,59 @@
using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;
namespace TV_Player.ViewModels
{
internal class SettingsViewModel : ObservableViewModelBase
{
private string _playlistURL;
public string PlaylistURL
{
get => _playlistURL;
set => SetProperty(ref _playlistURL, value);
}
private bool _startFullScreen;
public bool StartFullScreen
{
get => _startFullScreen;
set => SetProperty(ref _startFullScreen, value);
}
private bool _startLastScreen;
public bool StartLastScreen
{
get => _startLastScreen;
set => SetProperty(ref _startLastScreen, value);
}
public ICommand SaveCommand { get; }
public ICommand BackCommand { get; }
public SettingsViewModel()
{
TVPlayerViewModel.Instance.TopPanelVisible(false, "");
SaveCommand = new RelayCommand(OnSaveSettings);
BackCommand = new RelayCommand(OnBackCommand);
StartFullScreen = SettingsModel.StartFullScreen;
StartLastScreen = SettingsModel.StartFromLastScreen;
PlaylistURL = SettingsModel.PlaylistURL;
}
private void OnBackCommand()
{
TVPlayerViewModel.Instance.SelectScreen();
}
private void OnSaveSettings()
{
SettingsModel.StartFullScreen = StartFullScreen;
SettingsModel.StartFromLastScreen = StartLastScreen;
SettingsModel.PlaylistURL = PlaylistURL;
SettingsModel.SaveSetttings();
TVPlayerViewModel.Instance.InitializeTVWithData();
}
}
}
+70 -3
View File
@@ -5,6 +5,7 @@ namespace TV_Player.ViewModels
public class TVPlayerViewModel : IDisposable
{
private readonly MainViewModel _mainViewModel;
public ProgramsData PlaylistData { get; private set; }
public Action ButtonBackAction { get; set; }
@@ -21,6 +22,8 @@ namespace TV_Player.ViewModels
public TVPlayerViewModel()
{
PlaylistData = new ProgramsData();
_mainViewModel = new MainViewModel();
var mainWindow = new MainWindow();
mainWindow.DataContext = _mainViewModel;
@@ -28,19 +31,81 @@ namespace TV_Player.ViewModels
mainWindow.Show();
_instance = this;
ShowInitialScreen();
SettingsModel.LoadSettings();
InitializeTVWithData();
}
private void ShowInitialScreen()
public void InitializeTVWithData()
{
if (!string.IsNullOrEmpty(SettingsModel.PlaylistURL))
{
if (SettingsModel.StartFullScreen)
FullScreenToggle();
PlaylistData.GetData(SettingsModel.PlaylistURL);
if (SettingsModel.StartFromLastScreen)
SelectScreen();
else
ShowProgramsGroupScreen();
}
else
{
ShowSettingsScreen();
}
}
public void SelectScreen()
{
switch (SettingsModel.LastScreen)
{
case "ProgramsListViewModel":
ShowProgramsListScreen(SettingsModel.Group);
break;
case "PlayerViewModel":
ShowPlayerScreen(SettingsModel.Program);
break;
default:
ShowProgramsGroupScreen();
break;
}
}
public void ShowProgramsGroupScreen()
{
var vm = new ProgramsGroupViewModel();
var control = new ProgramsGroupGrid();
control.DataContext = vm;
SettingsModel.LastScreen = nameof(ProgramsGroupViewModel);
SetPageContext(control, vm);
}
public void ShowProgramsListScreen(GroupInfo group)
{
SettingsModel.Group = group;
var programListViewModel = new ProgramsListViewModel(group);
var conrtrol = new ProgramsList();
SettingsModel.LastScreen = nameof(ProgramsListViewModel);
SetPageContext(conrtrol, programListViewModel);
}
public void ShowPlayerScreen(M3UInfo program)
{
SettingsModel.Program = program;
var playerViewModel = new PlayerViewModel(program);
var conrtrol = new VideoPlayer();
conrtrol.SourceUrl = program.Url;
SettingsModel.LastScreen = nameof(PlayerViewModel);
SetPageContext(conrtrol, playerViewModel);
}
public void ShowSettingsScreen()
{
var playerViewModel = new SettingsViewModel();
var conrtrol = new Settings();
SetPageContext(conrtrol, playerViewModel);
}
public void TopPanelVisible(bool value, string title)
{
_mainViewModel.IsTopPanelVisible = value;
@@ -57,13 +122,15 @@ namespace TV_Player.ViewModels
_mainViewModel.ButtonBackAction = action;
}
public void SetPageContext(ContentControl control, object viewModel)
private void SetPageContext(ContentControl control, object viewModel)
{
if (_mainViewModel.Control is IDisposable disposable)
disposable.Dispose();
control.DataContext = viewModel;
_mainViewModel.Control = control;
SettingsModel.SaveSetttings();
}
public void Dispose()