add settings screen
get playlist from settings, in first run - open Settings
This commit is contained in:
@@ -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.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</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>
|
</ResourceDictionary>
|
||||||
@@ -20,17 +20,17 @@
|
|||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid Visibility="{Binding IsTopPanelVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}" Height="70">
|
<Grid Visibility="{Binding IsTopPanelVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}" Height="80">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="70"/>
|
<ColumnDefinition Width="80"/>
|
||||||
<ColumnDefinition Width="70"/>
|
<ColumnDefinition Width="80"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="70"/>
|
<ColumnDefinition Width="80"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Button Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonGear}" />
|
<Button Height="70" Width="70" Margin="10,0,0,0" Style="{DynamicResource ButtonGear}" Command="{Binding SettingsCommand}" />
|
||||||
<Button Grid.Column="1" Height="50" Width="50" Margin="10,0,0,0" Style="{DynamicResource ButtonBack}" Command="{Binding BackCommand}" />
|
<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"/>
|
<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>
|
</Grid>
|
||||||
<ContentControl Grid.Row="1" Name="ControlContainer" Content="{Binding Control}" />
|
<ContentControl Grid.Row="1" Name="ControlContainer" Content="{Binding Control}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<TreatAsUsed>true</TreatAsUsed>
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="LibVLCSharp.WPF" Version="3.8.2" />
|
<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="System.Reactive" Version="6.0.0" />
|
||||||
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
|
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -30,6 +31,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Update="Settings.xaml.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Update="VideoPlayer.xaml.cs">
|
<Compile Update="VideoPlayer.xaml.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -4,51 +4,56 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
|
xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:TV_Player"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
Unloaded="UserControl_Unloaded">
|
Unloaded="UserControl_Unloaded">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverterKey"/>
|
||||||
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1">
|
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1">
|
||||||
<Grid Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF" VerticalAlignment="Stretch"
|
<Grid Panel.ZIndex="10" IsHitTestVisible="True" Background="#01FFFFFF" VerticalAlignment="Stretch"
|
||||||
HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown">
|
HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown">
|
||||||
<Grid x:Name="overlayPanel" Visibility="Visible">
|
<Grid x:Name="overlayPanel" Visibility="Visible">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="70"/>
|
<RowDefinition Height="80"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="70"/>
|
<RowDefinition Height="80"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid Background="#70000000" >
|
<Grid Background="#70000000" >
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="70"/>
|
<ColumnDefinition Width="80"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="70"/>
|
<ColumnDefinition Width="80"/>
|
||||||
</Grid.ColumnDefinitions>
|
</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"/>
|
<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 Grid.Row="2" Background="#70000000">
|
<Grid Grid.Row="2" Background="#B0000000">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180"/>
|
<ColumnDefinition Width="180"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="70"/>
|
<ColumnDefinition Width="70"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<StackPanel Grid.RowSpan="2" Orientation="Horizontal" >
|
<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}" />
|
<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" />
|
<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}" />
|
<Button Grid.RowSpan="2" Height="70" 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="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" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<Grid Grid.Column="1" x:Name="programInfo" Visibility="{Binding IsProgramInfoVisible,Converter={StaticResource BooleanToVisibilityConverterKey}}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<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" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using LibVLCSharp.WPF;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace TV_Player
|
namespace TV_Player
|
||||||
{
|
{
|
||||||
@@ -16,11 +17,17 @@ namespace TV_Player
|
|||||||
private LibVLC _libVLC;
|
private LibVLC _libVLC;
|
||||||
private MediaPlayer _mediaPlayer;
|
private MediaPlayer _mediaPlayer;
|
||||||
private PlayerViewModel _viewModel;
|
private PlayerViewModel _viewModel;
|
||||||
|
private DispatcherTimer _overlayAutoHideTimer;
|
||||||
|
|
||||||
public VideoPlayer()
|
public VideoPlayer()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
_overlayAutoHideTimer = new DispatcherTimer();
|
||||||
|
_overlayAutoHideTimer.Interval = TimeSpan.FromSeconds(3);
|
||||||
|
_overlayAutoHideTimer.Tick += _overlayAutoHideTimer_Tick;
|
||||||
|
_overlayAutoHideTimer.Start();
|
||||||
|
|
||||||
_libVLC = new LibVLC(enableDebugLogs: true);
|
_libVLC = new LibVLC(enableDebugLogs: true);
|
||||||
_mediaPlayer = new MediaPlayer(_libVLC);
|
_mediaPlayer = new MediaPlayer(_libVLC);
|
||||||
this.DataContextChanged += (sender, e) =>
|
this.DataContextChanged += (sender, e) =>
|
||||||
@@ -28,7 +35,6 @@ namespace TV_Player
|
|||||||
_viewModel = (PlayerViewModel)e.NewValue;
|
_viewModel = (PlayerViewModel)e.NewValue;
|
||||||
_viewModel.SourceUrlChangedEvent += _viewModel_SourceUrlChangedEvent;
|
_viewModel.SourceUrlChangedEvent += _viewModel_SourceUrlChangedEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
VideoView.Loaded += (sender, e) =>
|
VideoView.Loaded += (sender, e) =>
|
||||||
{
|
{
|
||||||
@@ -39,7 +45,7 @@ namespace TV_Player
|
|||||||
AutoPlay();
|
AutoPlay();
|
||||||
};
|
};
|
||||||
Unloaded += VideoPlayer_Unloaded;
|
Unloaded += VideoPlayer_Unloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _viewModel_SourceUrlChangedEvent(string videoURL)
|
private void _viewModel_SourceUrlChangedEvent(string videoURL)
|
||||||
{
|
{
|
||||||
@@ -82,20 +88,28 @@ namespace TV_Player
|
|||||||
private void ToggleOverlay()
|
private void ToggleOverlay()
|
||||||
{
|
{
|
||||||
if (overlayPanel.Visibility == Visibility.Visible)
|
if (overlayPanel.Visibility == Visibility.Visible)
|
||||||
{
|
{
|
||||||
HideOverlay();
|
HideOverlay();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ShowOverlay();
|
ShowOverlay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void _overlayAutoHideTimer_Tick(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
HideOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
public void ShowOverlay()
|
public void ShowOverlay()
|
||||||
{
|
{
|
||||||
|
_overlayAutoHideTimer.Start();
|
||||||
overlayPanel.Visibility = Visibility.Visible;
|
overlayPanel.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
public void HideOverlay()
|
public void HideOverlay()
|
||||||
{
|
{
|
||||||
|
_overlayAutoHideTimer.Stop();
|
||||||
overlayPanel.Visibility = Visibility.Collapsed;
|
overlayPanel.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace TV_Player
|
namespace TV_Player
|
||||||
{
|
{
|
||||||
|
[Serializable]
|
||||||
public class GroupInfo
|
public class GroupInfo
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Channels;
|
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace TV_Player
|
namespace TV_Player
|
||||||
@@ -28,7 +27,6 @@ namespace TV_Player
|
|||||||
public static class M3UParser
|
public static class M3UParser
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
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>(); ;
|
||||||
@@ -46,7 +44,6 @@ namespace TV_Player
|
|||||||
return epgChannels;
|
return epgChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static List<ProgramGuide> ParseEpg(string epgData)
|
private static List<ProgramGuide> ParseEpg(string epgData)
|
||||||
{
|
{
|
||||||
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
|
List<ProgramGuide> epgChannels = new List<ProgramGuide>();
|
||||||
@@ -75,14 +72,14 @@ namespace TV_Player
|
|||||||
{
|
{
|
||||||
ProgramInfo program = new ProgramInfo();
|
ProgramInfo program = new ProgramInfo();
|
||||||
|
|
||||||
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);
|
program.StartTime = DateTime.ParseExact(reader.GetAttribute("start"), "yyyyMMddHHmmss zzz", null);
|
||||||
program.StopTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
program.StopTime = DateTime.ParseExact(reader.GetAttribute("stop"), "yyyyMMddHHmmss zzz", null);
|
||||||
|
|
||||||
reader.Read();
|
reader.Read();
|
||||||
program.Title = reader.ReadElementContentAsString();
|
program.Title = reader.ReadElementContentAsString();
|
||||||
|
|
||||||
|
|
||||||
channel.Programs.Add(program);
|
channel.Programs.Add(program);
|
||||||
}
|
}
|
||||||
@@ -92,132 +89,126 @@ 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;
|
return epgChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<string> ReadFile(string url)
|
||||||
public static async Task<List<M3UInfo>> DownloadM3UFromWebAsync(string url)
|
|
||||||
{
|
|
||||||
List<M3UInfo> playlistItems = new List<M3UInfo>();
|
|
||||||
|
|
||||||
using (var client = new HttpClient())
|
|
||||||
using (var request = new HttpRequestMessage())
|
|
||||||
{
|
{
|
||||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text"));
|
string responseBody;
|
||||||
request.Method = HttpMethod.Get;
|
using (var client = new HttpClient())
|
||||||
request.RequestUri = new Uri(url);
|
using (var request = new HttpRequestMessage())
|
||||||
var response = await client.GetAsync(url);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
string responseBody = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
// Parse M3U content
|
|
||||||
playlistItems = ParseM3UFromString(responseBody);
|
|
||||||
}
|
|
||||||
return playlistItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] SplitStringBeforeSeparator(string input, string separator)
|
|
||||||
{
|
|
||||||
string[] parts = input.Split(separator);
|
|
||||||
|
|
||||||
// Reconstruct the string until the separator is reached
|
|
||||||
int separatorIndex = input.IndexOf(separator);
|
|
||||||
if (separatorIndex != -1)
|
|
||||||
{
|
|
||||||
parts[0] = input.Substring(0, separatorIndex + 1);
|
|
||||||
for (int i = 1; i < parts.Length; i++)
|
|
||||||
{
|
{
|
||||||
parts[i] = separator + parts[i];
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts;
|
public static async Task<(List<M3UInfo> programList, string programGuide)> DownloadM3UFromWebAsync(string url)
|
||||||
}
|
|
||||||
|
|
||||||
static List<M3UInfo> ParseM3UFromString(string content)
|
|
||||||
{
|
|
||||||
List<M3UInfo> playlistItems = new List<M3UInfo>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var m3u = SplitStringBeforeSeparator(content, "#EXT");
|
var fileData=await ReadFile(url);
|
||||||
|
// Parse M3U content
|
||||||
|
return ParseM3UFromString(fileData);
|
||||||
|
}
|
||||||
|
private static string[] SplitStringBeforeSeparator(string input, string separator)
|
||||||
|
{
|
||||||
|
string[] parts = input.Split(separator);
|
||||||
|
|
||||||
foreach (var line in m3u)
|
// Reconstruct the string until the separator is reached
|
||||||
|
int separatorIndex = input.IndexOf(separator);
|
||||||
|
if (separatorIndex != -1)
|
||||||
{
|
{
|
||||||
if (line.StartsWith("#EXTINF:"))
|
parts[0] = input.Substring(0, separatorIndex + 1);
|
||||||
|
for (int i = 1; i < parts.Length; i++)
|
||||||
{
|
{
|
||||||
if (TryParseM3ULine(line, out var m3uInfo))
|
parts[i] = separator + parts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
foreach (var line in m3u)
|
||||||
|
{
|
||||||
|
if (line.StartsWith("#EXTINF:"))
|
||||||
{
|
{
|
||||||
playlistItems.Add(m3uInfo);
|
if (TryParseM3ULine(line, out var m3uInfo))
|
||||||
|
{
|
||||||
|
playlistItems.Add(m3uInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line.StartsWith("#EXTM3U"))
|
||||||
|
{
|
||||||
|
programGuideLink=ExtractXtvgUrl(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Error reading M3U file: " + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return playlistItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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>.*)$";
|
|
||||||
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
Match match = regex.Match(m3uLine);
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
info = new M3UInfo
|
|
||||||
{
|
{
|
||||||
CUID = match.Groups["CUID"].Value,
|
Console.WriteLine("Error reading M3U file: " + ex.Message);
|
||||||
Number = match.Groups["Number"].Value,
|
}
|
||||||
TvgID = match.Groups["TvgID"].Value,
|
|
||||||
TvgName = match.Groups["TvgName"].Value,
|
return (playlistItems,programGuideLink);
|
||||||
GroupTitle = match.Groups["GroupTitle"].Value,
|
|
||||||
Logo = match.Groups["Logo"].Value,
|
|
||||||
Name = match.Groups["Name"].Value,
|
|
||||||
Url = match.Groups["URL"].Value
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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>.*)$";
|
||||||
|
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
Match match = regex.Match(m3uLine);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
info = new M3UInfo
|
||||||
|
{
|
||||||
|
CUID = match.Groups["CUID"].Value,
|
||||||
|
Number = match.Groups["Number"].Value,
|
||||||
|
TvgID = match.Groups["TvgID"].Value,
|
||||||
|
TvgName = match.Groups["TvgName"].Value,
|
||||||
|
GroupTitle = match.Groups["GroupTitle"].Value,
|
||||||
|
Logo = match.Groups["Logo"].Value,
|
||||||
|
Name = match.Groups["Name"].Value,
|
||||||
|
Url = match.Groups["URL"].Value
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using TV_Player.ViewModels;
|
||||||
|
|
||||||
namespace TV_Player
|
namespace TV_Player
|
||||||
{
|
{
|
||||||
@@ -48,11 +49,14 @@ namespace TV_Player
|
|||||||
public Action ButtonBackAction { get; set; }
|
public Action ButtonBackAction { get; set; }
|
||||||
public ICommand BackCommand { get; }
|
public ICommand BackCommand { get; }
|
||||||
|
|
||||||
|
public ICommand SettingsCommand{ get; }
|
||||||
|
|
||||||
public MainViewModel()
|
public MainViewModel()
|
||||||
{
|
{
|
||||||
|
|
||||||
BackCommand = new RelayCommand(OnButtonBackClick);
|
BackCommand = new RelayCommand(OnButtonBackClick);
|
||||||
FullscreenCommand = new RelayCommand(OnFullSctreenButtonClick);
|
FullscreenCommand = new RelayCommand(OnFullSctreenButtonClick);
|
||||||
|
SettingsCommand = new RelayCommand(OnSettingsButtonClick);
|
||||||
|
|
||||||
CurrentWindowStyle = WindowStyle.SingleBorderWindow;
|
CurrentWindowStyle = WindowStyle.SingleBorderWindow;
|
||||||
}
|
}
|
||||||
@@ -76,5 +80,10 @@ namespace TV_Player
|
|||||||
|
|
||||||
ButtonBackAction?.Invoke();
|
ButtonBackAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSettingsButtonClick()
|
||||||
|
{
|
||||||
|
TVPlayerViewModel.Instance.ShowSettingsScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,14 @@ namespace TV_Player
|
|||||||
public delegate void SourceUrlChanged(string videoURL);
|
public delegate void SourceUrlChanged(string videoURL);
|
||||||
public event SourceUrlChanged SourceUrlChangedEvent;
|
public event SourceUrlChanged SourceUrlChangedEvent;
|
||||||
|
|
||||||
private M3UInfo _currentProgram;
|
private M3UInfo _currentProgram;
|
||||||
|
|
||||||
|
private bool _isProgramInfoVisible;
|
||||||
|
public bool IsProgramInfoVisible
|
||||||
|
{
|
||||||
|
get => _isProgramInfoVisible;
|
||||||
|
set => SetProperty(ref _isProgramInfoVisible, value);
|
||||||
|
}
|
||||||
|
|
||||||
private string _topPaneTitle;
|
private string _topPaneTitle;
|
||||||
public string TopPanelTitle
|
public string TopPanelTitle
|
||||||
@@ -71,7 +78,7 @@ namespace TV_Player
|
|||||||
PreviousCommand = new RelayCommand(PreviousProgram);
|
PreviousCommand = new RelayCommand(PreviousProgram);
|
||||||
FullscreenCommand = new RelayCommand(TVPlayerViewModel.Instance.FullScreenToggle);
|
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();
|
_programs = x.Where(p => p.GroupTitle == _currentProgram.GroupTitle).ToList();
|
||||||
_currentProgramIndex = _programs.Select((program, index) => new { program, index })
|
_currentProgramIndex = _programs.Select((program, index) => new { program, index })
|
||||||
@@ -79,7 +86,7 @@ namespace TV_Player
|
|||||||
.Select(x => x.index)
|
.Select(x => x.index)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
UpdateUI();
|
UpdateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +96,7 @@ namespace TV_Player
|
|||||||
TopPanelTitle = _currentProgram.Name;
|
TopPanelTitle = _currentProgram.Name;
|
||||||
SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
|
SourceUrlChangedEvent?.Invoke(_currentProgram.Url);
|
||||||
|
|
||||||
_programGuideDisposable = ProgramsData.Instance.ProgramGuideInfo.Subscribe(x =>
|
_programGuideDisposable = TVPlayerViewModel.Instance.PlaylistData.ProgramGuideInfo.Subscribe(x =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -125,23 +132,33 @@ namespace TV_Player
|
|||||||
}
|
}
|
||||||
private void UpdateScreenInfo()
|
private void UpdateScreenInfo()
|
||||||
{
|
{
|
||||||
_currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now);
|
try
|
||||||
if (_currentProgramInfo.Title != ProgramGuideText)
|
|
||||||
{
|
{
|
||||||
ProgramGuideText = _currentProgramInfo.Title;
|
_currentProgramInfo = _currentGuide.Programs.FirstOrDefault(d => d.StartTime <= DateTime.Now && d.StopTime >= DateTime.Now);
|
||||||
StartProgram = _currentProgramInfo.StartTime.ToShortTimeString();
|
if (_currentProgramInfo == null)
|
||||||
EndProgram = _currentProgramInfo.StopTime.ToShortTimeString();
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
var programMinutes = (_currentProgramInfo.StopTime - _currentProgramInfo.StartTime).TotalMinutes;
|
catch
|
||||||
DurationValue = (int)((DateTime.Now - _currentProgramInfo.StartTime).TotalMinutes / programMinutes * 100);
|
{ }
|
||||||
}
|
}
|
||||||
private void OnButtonBackClick()
|
private void OnButtonBackClick()
|
||||||
{
|
{
|
||||||
var groupInfo = new GroupInfo() { Name = _currentProgram.GroupTitle, Count = 0 };
|
var groupInfo = new GroupInfo() { Name = _currentProgram.GroupTitle, Count = 0 };
|
||||||
|
|
||||||
var programListViewModel = new ProgramsListViewModel(groupInfo);
|
TVPlayerViewModel.Instance.ShowProgramsListScreen(groupInfo);
|
||||||
var conrtrol = new ProgramsList();
|
|
||||||
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
|
|
||||||
}
|
}
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,16 +4,6 @@ namespace TV_Player
|
|||||||
{
|
{
|
||||||
public class ProgramsData
|
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<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>();
|
||||||
private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>();
|
private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>();
|
||||||
private readonly ReplaySubject<List<ProgramGuide>> programGuideSubject = new ReplaySubject<List<ProgramGuide>>();
|
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<GroupInfo>> GroupsInformation => groupsSubject;
|
||||||
|
|
||||||
public IObservable<List<ProgramGuide>> ProgramGuideInfo => programGuideSubject;
|
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";
|
//string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
|
||||||
var programs = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
|
var result = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
|
||||||
|
|
||||||
|
programsSubject.OnNext(result.programList);
|
||||||
|
|
||||||
programsSubject.OnNext(programs);
|
var groupping = result.programList.GroupBy(item => item.GroupTitle)
|
||||||
|
|
||||||
var groupping = programs.GroupBy(item => item.GroupTitle)
|
|
||||||
.Select(group => new GroupInfo() { Name = group.Key, Count = group.Count() })
|
.Select(group => new GroupInfo() { Name = group.Key, Count = group.Count() })
|
||||||
.ToList();
|
.ToList();
|
||||||
groupsSubject.OnNext(groupping);
|
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);
|
var programGuide = await M3UParser.DownloadGuideFromWebAsync(guideLink);
|
||||||
programGuideSubject.OnNext(programGuide);
|
programGuideSubject.OnNext(programGuide);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void GetData(string playlistURL)
|
||||||
|
{
|
||||||
|
Task.Run(() => GetPrograms(playlistURL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,16 +20,14 @@ namespace TV_Player
|
|||||||
public ProgramsGroupViewModel()
|
public ProgramsGroupViewModel()
|
||||||
{
|
{
|
||||||
ItemSelectedCommand = new RelayCommand(OnItemSelected);
|
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, "Группы");
|
TVPlayerViewModel.Instance.TopPanelVisible(true, "Группы");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnItemSelected()
|
private void OnItemSelected()
|
||||||
{
|
{
|
||||||
var programListViewModel = new ProgramsListViewModel(SelectedItem);
|
TVPlayerViewModel.Instance.ShowProgramsListScreen(SelectedItem);
|
||||||
var conrtrol = new ProgramsList();
|
|
||||||
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -16,26 +16,22 @@ namespace TV_Player
|
|||||||
public M3UInfo SelectedItem { get; set; }
|
public M3UInfo SelectedItem { get; set; }
|
||||||
public ICommand ItemSelectedCommand { get; }
|
public ICommand ItemSelectedCommand { get; }
|
||||||
private IDisposable _programSubscriber;
|
private IDisposable _programSubscriber;
|
||||||
|
|
||||||
public ProgramsListViewModel(GroupInfo groupInfo)
|
public ProgramsListViewModel(GroupInfo groupInfo)
|
||||||
{
|
{
|
||||||
TVPlayerViewModel.Instance.TopPanelVisible(true, groupInfo.Name);
|
TVPlayerViewModel.Instance.TopPanelVisible(true, groupInfo.Name);
|
||||||
ItemSelectedCommand = new RelayCommand(OnItemSelected);
|
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(() =>
|
TVPlayerViewModel.Instance.SetBackButtonAction(new Action(() =>
|
||||||
{
|
{
|
||||||
var programGroupViewModel = new ProgramsGroupViewModel();
|
TVPlayerViewModel.Instance.ShowProgramsGroupScreen();
|
||||||
var conrtrol = new ProgramsGroupGrid();
|
|
||||||
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programGroupViewModel);
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnItemSelected()
|
private void OnItemSelected()
|
||||||
{
|
{
|
||||||
var playerViewModel = new PlayerViewModel(SelectedItem);
|
TVPlayerViewModel.Instance.ShowPlayerScreen(SelectedItem);
|
||||||
var conrtrol = new VideoPlayer();
|
|
||||||
conrtrol.SourceUrl = SelectedItem.Url;
|
|
||||||
TVPlayerViewModel.Instance.SetPageContext(conrtrol, playerViewModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -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);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace TV_Player.ViewModels
|
|||||||
public class TVPlayerViewModel : IDisposable
|
public class TVPlayerViewModel : IDisposable
|
||||||
{
|
{
|
||||||
private readonly MainViewModel _mainViewModel;
|
private readonly MainViewModel _mainViewModel;
|
||||||
|
public ProgramsData PlaylistData { get; private set; }
|
||||||
|
|
||||||
public Action ButtonBackAction { get; set; }
|
public Action ButtonBackAction { get; set; }
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ namespace TV_Player.ViewModels
|
|||||||
|
|
||||||
public TVPlayerViewModel()
|
public TVPlayerViewModel()
|
||||||
{
|
{
|
||||||
|
PlaylistData = new ProgramsData();
|
||||||
|
|
||||||
_mainViewModel = new MainViewModel();
|
_mainViewModel = new MainViewModel();
|
||||||
var mainWindow = new MainWindow();
|
var mainWindow = new MainWindow();
|
||||||
mainWindow.DataContext = _mainViewModel;
|
mainWindow.DataContext = _mainViewModel;
|
||||||
@@ -28,19 +31,81 @@ namespace TV_Player.ViewModels
|
|||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
_instance = this;
|
_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 vm = new ProgramsGroupViewModel();
|
||||||
|
|
||||||
var control = new ProgramsGroupGrid();
|
var control = new ProgramsGroupGrid();
|
||||||
control.DataContext = vm;
|
control.DataContext = vm;
|
||||||
|
|
||||||
|
SettingsModel.LastScreen = nameof(ProgramsGroupViewModel);
|
||||||
SetPageContext(control, vm);
|
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)
|
public void TopPanelVisible(bool value, string title)
|
||||||
{
|
{
|
||||||
_mainViewModel.IsTopPanelVisible = value;
|
_mainViewModel.IsTopPanelVisible = value;
|
||||||
@@ -57,13 +122,15 @@ namespace TV_Player.ViewModels
|
|||||||
_mainViewModel.ButtonBackAction = action;
|
_mainViewModel.ButtonBackAction = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPageContext(ContentControl control, object viewModel)
|
private void SetPageContext(ContentControl control, object viewModel)
|
||||||
{
|
{
|
||||||
if (_mainViewModel.Control is IDisposable disposable)
|
if (_mainViewModel.Control is IDisposable disposable)
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
control.DataContext = viewModel;
|
control.DataContext = viewModel;
|
||||||
|
|
||||||
_mainViewModel.Control = control;
|
_mainViewModel.Control = control;
|
||||||
|
|
||||||
|
SettingsModel.SaveSetttings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
Reference in New Issue
Block a user