Got WPF version work

This commit is contained in:
Vova
2024-01-18 17:42:44 +02:00
parent df51ee3ad6
commit 10b4146772
64 changed files with 866 additions and 411 deletions
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TV_Player"
StartupUri="MainWindow.xaml">
>
<Application.Resources>
</Application.Resources>
+22
View File
@@ -0,0 +1,22 @@
using DirectShowLib.BDA;
using System.Configuration;
using System.Data;
using System.Windows;
using TV_Player.ViewModels;
namespace TV_Player
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private TVPlayerViewModel _tvPlayer;
protected override void OnStartup(StartupEventArgs e)
{
_tvPlayer = new TVPlayerViewModel();
base.OnStartup(e);
}
}
}

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

@@ -3,7 +3,6 @@
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:local="clr-namespace:TV_Player"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200">
<Viewbox Grid.Row="1">
@@ -6,13 +6,13 @@
xmlns:local="clr-namespace:TV_Player"
mc:Ignorable="d"
Title="TV" Height="450" Width="800">
<Window.Resources>
<local:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</Window.Resources>
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.Background>
<ImageBrush ImageSource="Assets/bkground.jpg" />
</Grid.Background>
<ContentControl Name="ControlContainer" />
<ContentControl Name="ControlContainer" Content="{Binding Control}" />
</Grid>
</Window>
+13
View File
@@ -0,0 +1,13 @@
using System.Windows;
namespace TV_Player
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
+24
View File
@@ -0,0 +1,24 @@
<UserControl x:Class="TV_Player.ProgramsGroupGrid"
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:local="clr-namespace:TV_Player"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="groupsGrid">
<ScrollViewer>
<ListBox ItemsSource="{Binding Programs}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</UserControl>
+23
View File
@@ -0,0 +1,23 @@
using System.Windows.Controls;
namespace TV_Player
{
/// <summary>
/// Interaction logic for ProgramsGroupGrid.xaml
/// </summary>
public partial class ProgramsGroupGrid : UserControl
{
public ProgramsGroupGrid()
{
InitializeComponent();
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (DataContext is ProgramsGroupViewModel viewModel)
{
viewModel.ItemSelectedCommand.Execute(null);
}
}
}
}
+25
View File
@@ -0,0 +1,25 @@
<UserControl x:Class="TV_Player.ProgramsList"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="groupsGrid">
<ScrollViewer>
<ListBox ItemsSource="{Binding Programs}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Logo}" Width="50" Height="50"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</UserControl>
+22
View File
@@ -0,0 +1,22 @@
using System.Windows.Controls;
namespace TV_Player
{
/// <summary>
/// Interaction logic for ProgramsGroupGrid.xaml
/// </summary>
public partial class ProgramsList : UserControl
{
public ProgramsList()
{
InitializeComponent();
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (DataContext is ProgramsListViewModel viewModel)
{
viewModel.ItemSelectedCommand.Execute(null);
}
}
}
}
@@ -15,8 +15,9 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
<PackageReference Include="Vlc.DotNet.Wpf" Version="3.1.0" />
<PackageReference Include="LibVLCSharp" Version="3.8.2" />
<PackageReference Include="LibVLCSharp.WPF" Version="3.8.2" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
<PackageReference Include="WPFMediaKitCore" Version="0.0.3" />
</ItemGroup>
@@ -26,4 +27,13 @@
</Resource>
</ItemGroup>
<ItemGroup>
<Compile Update="VideoPlayer.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="ProgramsList.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>
+31
View File
@@ -0,0 +1,31 @@
<UserControl x:Class="TV_Player.VideoPlayer"
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:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
>
<Grid>
<Grid x:Name="videoPlayer">
<vlc:VideoView x:Name="VideoView" Panel.ZIndex="1">
<StackPanel Panel.ZIndex="2" Opacity="0.9"
IsHitTestVisible="True"
HorizontalAlignment="Stretch" MouseLeftButtonDown="MyUserControl_MouseDown"
TouchDown="MyUserControl_TouchDown" >
<Grid x:Name="overlayPanel" Visibility="Collapsed" >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Pause" HorizontalAlignment="Stretch" Click="PauseButton_Click" />
</Grid>
</StackPanel>
</vlc:VideoView>
</Grid>
</Grid>
</UserControl>
+114
View File
@@ -0,0 +1,114 @@
using LibVLCSharp.Shared;
using LibVLCSharp.WPF;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TV_Player
{
/// <summary>
/// Interaction logic for ProgramsGroupGrid.xaml
/// </summary>
public partial class VideoPlayer : UserControl
{
private readonly DirectoryInfo vlcLibDirectory;
public static readonly DependencyProperty SourceUrlProperty =
DependencyProperty.Register(
"SourceUrl", // Name of the property
typeof(string), // Type of the property
typeof(VideoPlayer), // Type of the owner class
new PropertyMetadata(string.Empty) // Default value
);
LibVLC _libVLC;
MediaPlayer _mediaPlayer;
public string SourceUrl
{
get { return (string)GetValue(SourceUrlProperty); }
set { SetValue(SourceUrlProperty, value);}
}
public VideoPlayer()
{
InitializeComponent();
_libVLC = new LibVLC(enableDebugLogs: true);
_mediaPlayer = new MediaPlayer(_libVLC);
VideoView.Loaded += (sender, e) =>
{
VideoView.MediaPlayer = _mediaPlayer;
VideoView.MouseLeftButtonDown += VideoView_MouseLeftButtonDown;
VideoView.MediaPlayer.EnableMouseInput = false;
VideoView.PreviewMouseLeftButtonDown += VideoView_MouseLeftButtonDown;
AutoPlay();
};
Unloaded += VideoPlayer_Unloaded;
}
private void VideoView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ToggleOverlay();
}
private void VideoPlayer_Unloaded(object sender, RoutedEventArgs e)
{
VideoView.Dispose();
}
void PauseButton_Click(object sender, RoutedEventArgs e)
{
if (VideoView.MediaPlayer.IsPlaying)
{
VideoView.MediaPlayer.Pause();
}
}
private void AutoPlay()
{
if (!VideoView.MediaPlayer.IsPlaying)
{
using (var media = new Media(_libVLC, new Uri(SourceUrl)))
VideoView.MediaPlayer.Play(media);
}
}
private void MyUserControl_MouseDown(object sender, MouseButtonEventArgs e)
{
ToggleOverlay();
}
private void MyUserControl_TouchDown(object sender, TouchEventArgs e)
{
ToggleOverlay();
}
private void ToggleOverlay()
{
if (overlayPanel.Visibility == Visibility.Visible)
{
HideOverlay();
}
else
{
ShowOverlay();
}
}
public void ShowOverlay()
{
overlayPanel.Visibility = Visibility.Visible;
}
public void HideOverlay()
{
overlayPanel.Visibility = Visibility.Collapsed;
}
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace TV_Player
{
public class GroupInfo
{
public string Name { get; set; }
public int Count { get; set; }
}
}
@@ -90,7 +90,7 @@ namespace TV_Player
GroupTitle = match.Groups["GroupTitle"].Value,
Logo = match.Groups["Logo"].Value,
Name = match.Groups["Name"].Value,
Url = match.Groups["Url"].Value
Url = match.Groups["URL"].Value
};
return true;
}
+23
View File
@@ -0,0 +1,23 @@
using System.Windows.Controls;
namespace TV_Player
{
public class MainViewModel : ObservableViewModelBase
{
private ContentControl _control;
public ContentControl Control
{
get => _control;
set => SetProperty(ref _control, value);
}
public MainViewModel()
{
var vm = new ProgramsGroupViewModel();
var control = new ProgramsGroupGrid();
control.DataContext = vm;
Control = control;
}
}
}
@@ -0,0 +1,28 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TV_Player
{
public abstract class ObservableViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary>
/// Set a property and raise a property changed event if it has changed
/// </summary>
protected bool SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(property, value))
{
return false;
}
property = value;
RaisePropertyChanged(propertyName);
return true;
}
}
}
@@ -0,0 +1,48 @@
using System.Windows.Input;
namespace TV_Player
{
public class PlayerViewModel: ObservableViewModelBase
{
private readonly M3UInfo _currentProgram;
private string _urlSource;
public string URLSource
{
get => _urlSource;
set => SetProperty(ref _urlSource, value);
}
public GroupInfo SelectedItem { get; set; }
public ICommand PlayCommand { get; }
public PlayerViewModel(M3UInfo selectedProgram)
{
_currentProgram = selectedProgram;
//PlayM3U8(_currentProgram.Url);
//_libVLC = new LibVLC();
//_mediaPlayer = new MediaPlayer(new Media(_libVLC, new Uri(_currentProgram.Url)));
//_mediaPlayer.Play();
//PlayCommand = new Command(OnPlayButtonClicked);
}
private void OnPlayButtonClicked()
{
PlayM3U8(_currentProgram.Url);
}
private void PlayM3U8(string url)
{
try
{
URLSource = _currentProgram.Url;
}
catch (Exception ex)
{
// Handle exceptions
}
}
}
}
+39
View File
@@ -0,0 +1,39 @@
using System.Reactive.Subjects;
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>>();
public IObservable<List<M3UInfo>> AllPrograms => programsSubject;
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
private ProgramsData()
{
_=Initialize();
}
private async Task Initialize()
{
string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
var programs = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
programsSubject.OnNext(programs);
var groupping = programs.GroupBy(item => item.GroupTitle)
.Select(group => new GroupInfo() { Name = group.Key, Count = group.Count() })
.ToList();
groupsSubject.OnNext(groupping);
}
}
}
@@ -0,0 +1,36 @@
using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;
using TV_Player.ViewModels;
namespace TV_Player
{
public class ProgramsGroupViewModel : ObservableViewModelBase
{
private List<GroupInfo> _programs;
public List<GroupInfo> Programs
{
get => _programs;
set => SetProperty(ref _programs, value);
}
public GroupInfo SelectedItem { get; set; }
public ICommand ItemSelectedCommand { get; }
public ProgramsGroupViewModel()
{
ItemSelectedCommand = new RelayCommand(OnItemSelected);
ProgramsData.Instance.GroupsInformation.Subscribe(x=>Programs = x);
}
private void OnItemSelected()
{
//var navigation = (INavigation)Application.Current.MainPage.Navigation;
var programListViewModel = new ProgramsListViewModel(SelectedItem);
var conrtrol = new ProgramsList();
TVPlayerViewModel.Instance.SetPageContext(conrtrol, programListViewModel);
}
}
}
@@ -0,0 +1,45 @@
using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;
using TV_Player.ViewModels;
namespace TV_Player
{
public class ProgramsListViewModel : ObservableViewModelBase
{
private List<M3UInfo> _programs;
public List<M3UInfo> Programs
{
get => _programs;
set => SetProperty(ref _programs, value);
}
public M3UInfo SelectedItem { get; set; }
public ICommand ItemSelectedCommand { get; }
public ProgramsListViewModel(GroupInfo groupInfo)
{
ItemSelectedCommand = new RelayCommand(OnItemSelected);
ProgramsData.Instance.AllPrograms.Subscribe(x=>Programs = x.Where(p=>p.GroupTitle== groupInfo.Name).ToList());
}
private void OnItemSelected()
{
var playerViewModel = new PlayerViewModel(SelectedItem);
var conrtrol = new VideoPlayer();
conrtrol.SourceUrl = SelectedItem.Url;
TVPlayerViewModel.Instance.SetPageContext(conrtrol, playerViewModel);
//var navigation = (INavigation)Application.Current.MainPage.Navigation;
//var playerViewModel = new PlayerViewModel(SelectedItem);
//// Create a new SecondPage and set its BindingContext to the ViewModel
//var playerPage = new PlayerPage
//{
// BindingContext = playerViewModel
//};
//// Navigate to the OtherPage
//navigation.PushAsync(playerPage);
}
}
}
+33
View File
@@ -0,0 +1,33 @@
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,37 @@
using System.Windows.Controls;
namespace TV_Player.ViewModels
{
public class TVPlayerViewModel
{
private readonly MainViewModel _mainViewModel;
private static TVPlayerViewModel _instance;
public static TVPlayerViewModel Instance
{
get
{
if(_instance==null)
_instance = new TVPlayerViewModel();
return _instance;
}
}
public TVPlayerViewModel()
{
_mainViewModel = new MainViewModel();
var mainWindow=new MainWindow();
mainWindow.DataContext = _mainViewModel;
mainWindow.Show();
_instance = this;
}
public void SetPageContext(ContentControl control, object viewModel)
{
control.DataContext = viewModel;
_mainViewModel.Control = control;
}
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TV_Player"
x:Class="TV_Player.App">
xmlns:local="clr-namespace:TV_Player.MAUI"
x:Class="TV_Player.MAUI.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
+1 -1
View File
@@ -1,4 +1,4 @@
namespace TV_Player
namespace TV_Player.MAUI
{
public partial class App : Application
{
+2 -2
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="TV_Player.AppShell"
x:Class="TV_Player.MAUI.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TV_Player"
xmlns:local="clr-namespace:TV_Player.MAUI"
Shell.FlyoutBehavior="Disabled">
<ShellContent
+1 -1
View File
@@ -1,4 +1,4 @@
namespace TV_Player
namespace TV_Player.MAUI
{
public partial class AppShell : Shell
{
+3 -3
View File
@@ -1,7 +1,7 @@
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TV_Player"
x:Class="TV_Player.MainPage">
xmlns:local="clr-namespace:TV_Player.MAUI"
x:Class="TV_Player.MAUI.MainPage">
<ContentPage.BindingContext>
<local:MainViewModel />
</ContentPage.BindingContext>
@@ -15,7 +15,7 @@
SelectedItem="{Binding SelectedItem}"
SelectionChangedCommand="{Binding ItemSelectedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="local:GroupInfo">
<DataTemplate x:DataType="local:M3UInfo">
<Label Text="{Binding Name}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
+1 -13
View File
@@ -1,4 +1,4 @@
namespace TV_Player
namespace TV_Player.MAUI
{
public partial class MainPage : ContentPage
{
@@ -6,18 +6,6 @@
{
InitializeComponent();
}
//private void OnCounterClicked(object sender, EventArgs e)
//{
// count++;
// if (count == 1)
// CounterBtn.Text = $"Clicked {count} time";
// else
// CounterBtn.Text = $"Clicked {count} times";
// SemanticScreenReader.Announce(CounterBtn.Text);
//}
}
}
+4 -2
View File
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging;
using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;
namespace TV_Player
namespace TV_Player.MAUI
{
public static class MauiProgram
{
@@ -9,6 +10,7 @@ namespace TV_Player
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkitMediaElement()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+44
View File
@@ -0,0 +1,44 @@
using LibVLCSharp.Shared;
namespace TV_Player.MAUI
{
public class MediaViewer : ContentView
{
private LibVLC _libVLC;
private MediaPlayer _mediaPlayer;
public static BindableProperty StreamUrlProperty = BindableProperty.Create(nameof(StreamUrl)
, typeof(string)
, typeof(MediaViewer)
, ""
, defaultBindingMode: BindingMode.TwoWay);
public string StreamUrl
{
get => (string)GetValue(StreamUrlProperty);
set
{
SetValue(StreamUrlProperty, value);
}
}
public MediaViewer()
{
InitializeMediaPlayer();
}
private void InitializeMediaPlayer()
{
_libVLC = new LibVLC();
_mediaPlayer = new MediaPlayer(_libVLC);
// var media = new Media(_libVLC, new Uri("http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiNDk3IiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODM2MS92aWRlby5tM3U4P3Rva2V/video.m3u8"));
//_mediaPlayer.Play();
}
public void Play()
{
var media = new Media(_libVLC, new Uri("http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiNDk3IiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODM2MS92aWRlby5tM3U4P3Rva2V/video.m3u8"));
_mediaPlayer.Play(media);
}
}
}
@@ -1,5 +1,6 @@
using Android.App;
using Android.Runtime;
using TV_Player.MAUI;
namespace TV_Player
{
@@ -1,10 +0,0 @@
using Foundation;
namespace TV_Player
{
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>
@@ -1,16 +0,0 @@
using ObjCRuntime;
using UIKit;
namespace TV_Player
{
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
-17
View File
@@ -1,17 +0,0 @@
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using System;
namespace TV_Player
{
internal class Program : MauiApplication
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
static void Main(string[] args)
{
var app = new Program();
app.Run(args);
}
}
}
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="7" xmlns="http://tizen.org/ns/packages">
<profile name="common" />
<ui-application appid="maui-application-id-placeholder" exec="TV Player.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
<label>maui-application-title-placeholder</label>
<icon>maui-appicon-placeholder</icon>
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
</ui-application>
<shortcut-list />
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
<dependencies />
<provides-appdefined-privileges />
</manifest>
+1
View File
@@ -1,4 +1,5 @@
using Microsoft.UI.Xaml;
using TV_Player.MAUI;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
-10
View File
@@ -1,10 +0,0 @@
using Foundation;
namespace TV_Player
{
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
-32
View File
@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>
-16
View File
@@ -1,16 +0,0 @@
using ObjCRuntime;
using UIKit;
namespace TV_Player
{
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TV_Player.MAUI.PlayerPage"
xmlns:local="clr-namespace:TV_Player.MAUI"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
Title="PlayerPage"
Loaded="ContentPage_Loaded">
<StackLayout>
<toolkit:MediaElement x:Name="mediaElement"
ShouldAutoPlay="True"
ShouldShowPlaybackControls="True"
Source="http://ost.da-tv.vip/uPVtzdGJfdG9rZW5dIiwibCI6ImE3MWU3N2ZhIiwicCI6ImE3MWU3N2ZhODM1YjMyMTYiLCJjIjoiNDk3IiwidCI6ImUzNjAwZTEwZmFmMGVhYjhhYWY1YTU2YzRkN2VjZTE5IiwiZCI6IjIzMTQ2IiwiciI6IjIzMDM4IiwibSI6InR2IiwiZHQiOiIwIn0eyJ1IjoiaHR0cDovLzQ1LjkzLjQ2LjI3Ojg4ODcvODM2MS92aWRlby5tM3U4P3Rva2V/video.m3u8"
HeightRequest="300"
WidthRequest="400"
/>
<!--<local:MediaViewer x:Name="libVLCSharpView" WidthRequest="300" HeightRequest="300" HorizontalOptions="CenterAndExpand" StreamUrl="{Binding URLSource,Source={x:Reference MyCustomView}}"/>-->
<Button Text="Play" Command="{Binding PlayCommand}" />
</StackLayout>
</ContentPage>
+14
View File
@@ -0,0 +1,14 @@
namespace TV_Player.MAUI;
public partial class PlayerPage : ContentPage
{
public PlayerPage()
{
this.BindingContext = this;
InitializeComponent();
}
private void ContentPage_Loaded(object sender, EventArgs e)
{
}
}
+26
View File
@@ -0,0 +1,26 @@
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TV_Player.MAUI"
x:Class="TV_Player.MAUI.ProgramPage">
<ScrollView>
<VerticalStackLayout
Padding="30,0"
VerticalOptions="Center">
<CollectionView ItemsSource="{Binding Programs}"
ItemsLayout="VerticalGrid, 5" SelectionMode="Single"
SelectedItem="{Binding SelectedItem}"
SelectionChangedCommand="{Binding ItemSelectedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="local:M3UInfo">
<VerticalStackLayout>
<Image Source="{Binding Logo}" WidthRequest="50" HeightRequest="50"/>
<Label Text="{Binding Name}" HorizontalTextAlignment="Center"/>
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
+10
View File
@@ -0,0 +1,10 @@
namespace TV_Player.MAUI
{
public partial class ProgramPage : ContentPage
{
public ProgramPage()
{
InitializeComponent();
}
}
}
@@ -22,12 +22,9 @@
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
@@ -49,8 +46,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="3.0.1" />
<PackageReference Include="LibVLCSharp" Version="3.8.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-ios'">
@@ -65,4 +65,25 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Update="ProgramPage.xaml.cs">
<DependentUpon>ProgramPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<MauiXaml Update="PlayerPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ProgramPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
<ItemGroup>
<None Update="PlayerPage.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
</ItemGroup>
</Project>
+1 -2
View File
@@ -1,7 +1,6 @@
namespace TV_Player
namespace TV_Player.MAUI
{
public class GroupInfo
{
public string Name { get; set; }
public int Count { get; set; }
+1 -1
View File
@@ -1,4 +1,4 @@
namespace TV_Player
namespace TV_Player.MAUI
{
public class M3UInfo
{
+2 -2
View File
@@ -1,7 +1,7 @@
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
namespace TV_Player
namespace TV_Player.MAUI
{
public static class M3UParser
{
@@ -89,7 +89,7 @@ namespace TV_Player
GroupTitle = match.Groups["GroupTitle"].Value,
Logo = match.Groups["Logo"].Value,
Name = match.Groups["Name"].Value,
Url = match.Groups["Url"].Value
Url = match.Groups["URL"].Value
};
return true;
}
+12 -2
View File
@@ -1,6 +1,6 @@
using System.Windows.Input;
namespace TV_Player
namespace TV_Player.MAUI
{
public class MainViewModel : ObservableViewModelBase
{
@@ -22,7 +22,17 @@ namespace TV_Player
private void OnItemSelected()
{
var navigation = (INavigation)Application.Current.MainPage.Navigation;
var programPageViewModel = new ProgramViewModel(SelectedItem);
// Create a new SecondPage and set its BindingContext to the ViewModel
var programPage = new ProgramPage
{
BindingContext = programPageViewModel
};
// Navigate to the OtherPage
navigation.PushAsync(programPage);
}
}
}
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TV_Player
namespace TV_Player.MAUI
{
public abstract class ObservableViewModelBase : INotifyPropertyChanged
{
+49
View File
@@ -0,0 +1,49 @@
using LibVLCSharp.Shared;
using System.Windows.Input;
namespace TV_Player.MAUI
{
public class PlayerViewModel: ObservableViewModelBase
{
private readonly M3UInfo _currentProgram;
private string _urlSource;
public string URLSource
{
get => _urlSource;
set => SetProperty(ref _urlSource, value);
}
public GroupInfo SelectedItem { get; set; }
public ICommand PlayCommand { get; }
public PlayerViewModel(M3UInfo selectedProgram)
{
_currentProgram = selectedProgram;
//PlayM3U8(_currentProgram.Url);
//_libVLC = new LibVLC();
//_mediaPlayer = new MediaPlayer(new Media(_libVLC, new Uri(_currentProgram.Url)));
//_mediaPlayer.Play();
PlayCommand = new Command(OnPlayButtonClicked);
}
private void OnPlayButtonClicked()
{
PlayM3U8(_currentProgram.Url);
}
private void PlayM3U8(string url)
{
try
{
URLSource = _currentProgram.Url;
}
catch (Exception ex)
{
// Handle exceptions
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
using System.Windows.Input;
namespace TV_Player.MAUI
{
public class ProgramViewModel : ObservableViewModelBase
{
private List<M3UInfo> _programs;
public List<M3UInfo> Programs
{
get => _programs;
set => SetProperty(ref _programs, value);
}
public M3UInfo SelectedItem { get; set; }
public ICommand ItemSelectedCommand { get; }
public ProgramViewModel(GroupInfo groupInfo)
{
ItemSelectedCommand = new Command(OnItemSelected);
ProgramsData.Instance.AllPrograms.Subscribe(x=>Programs = x.Where(p=>p.GroupTitle== groupInfo.Name).ToList());
}
private void OnItemSelected()
{
var navigation = (INavigation)Application.Current.MainPage.Navigation;
var playerViewModel = new PlayerViewModel(SelectedItem);
// Create a new SecondPage and set its BindingContext to the ViewModel
var playerPage = new PlayerPage
{
BindingContext = playerViewModel
};
// Navigate to the OtherPage
navigation.PushAsync(playerPage);
}
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
using System.Reactive.Subjects;
namespace TV_Player
namespace TV_Player.MAUI
{
public class ProgramsData
{
@@ -14,10 +14,10 @@ namespace TV_Player
}
}
private readonly Subject<List<M3UInfo>> programsSubject = new Subject<List<M3UInfo>>();
private readonly Subject<List<GroupInfo>> groupsSubject = new Subject<List<GroupInfo>>();
private readonly ReplaySubject<List<M3UInfo>> programsSubject = new ReplaySubject<List<M3UInfo>>();
private readonly ReplaySubject<List<GroupInfo>> groupsSubject = new ReplaySubject<List<GroupInfo>>();
public IObservable<List<M3UInfo>> AllPrograms => programsSubject;
public Subject<List<GroupInfo>> GroupsInformation => groupsSubject;
public IObservable<List<GroupInfo>> GroupsInformation => groupsSubject;
private ProgramsData()
{
-14
View File
@@ -1,14 +0,0 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace TV_Player
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
-102
View File
@@ -1,102 +0,0 @@
using System.ComponentModel;
using System.Data.Common;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using Vlc.DotNet.Wpf;
namespace TV_Player
{
public partial class MainWindow : Window
{
private readonly DirectoryInfo vlcLibDirectory;
private VlcControl control;
public MainWindow()
{
InitializeComponent();
var currentAssembly = Assembly.GetEntryAssembly();
var currentDirectory = new FileInfo(currentAssembly.Location).DirectoryName;
// Default installation path of VideoLAN.LibVLC.Windows
vlcLibDirectory = new DirectoryInfo(Path.Combine(currentDirectory, "libvlc", IntPtr.Size == 4 ? "win-x86" : "win-x64"));
this.control = new VlcControl();
this.control.SourceProvider.CreatePlayer(vlcLibDirectory/* pass your player parameters here */);
Initialize();
}
protected override void OnClosing(CancelEventArgs e)
{
this.control?.Dispose();
base.OnClosing(e);
}
private void OnPlayButtonClick(object sender, RoutedEventArgs e)
{
//this.control?.Dispose();
//this.control = new VlcControl();
//this.ControlContainer.Content = this.control;
//this.control.SourceProvider.CreatePlayer(this.vlcLibDirectory);
// This can also be called before EndInit
this.control.SourceProvider.MediaPlayer.Log += (_, args) =>
{
string message = $"libVlc : {args.Level} {args.Message} @ {args.Module}";
System.Diagnostics.Debug.WriteLine(message);
};
control.SourceProvider.MediaPlayer.Play(new Uri("http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi"));
}
private void OnStopButtonClick(object sender, RoutedEventArgs e)
{
this.control?.Dispose();
this.control = null;
}
private void OnForwardButtonClick(object sender, RoutedEventArgs e)
{
if (this.control == null)
{
return;
}
this.control.SourceProvider.MediaPlayer.Rate = 2;
}
private void GetLength_Click(object sender, RoutedEventArgs e)
{
if (this.control == null)
{
return;
}
//GetLength.Content = this.control.SourceProvider.MediaPlayer.Length + " ms";
}
private void GetCurrentTime_Click(object sender, RoutedEventArgs e)
{
if (this.control == null)
{
return;
}
//GetCurrentTime.Content = this.control.SourceProvider.MediaPlayer.Time + " ms";
}
private void SetCurrentTime_Click(object sender, RoutedEventArgs e)
{
if (this.control == null)
{
return;
}
this.control.SourceProvider.MediaPlayer.Time = 5000;
//SetCurrentTime.Content = this.control.SourceProvider.MediaPlayer.Time + " ms";
}
}
}
-43
View File
@@ -1,43 +0,0 @@
using System.Windows.Controls;
namespace TV_Player
{
public class MainWindowViewModel
{
public MainWindowViewModel()
{
}
private async Task Initialize()
{
string m3uLink = "http://pl.da-tv.vip/a71e77fa/835b3216/tv.m3u";
var programs = await M3UParser.DownloadM3UFromWebAsync(m3uLink);
var groupedPrograms = programs.GroupBy(item => item.GroupTitle)
.ToDictionary(
group => group.Key,
group => group.Select(item => item).ToList()
);
var row = 0;
var column = 0;
foreach (var group in groupedPrograms)
{
var button = new GroupButton(group.Key, group.Value.Count());
button.TouchDown += (s, e) =>
{
};
Grid.SetRow(button, row);
Grid.SetColumn(button, column);
groupsGrid.Children.Add(button);
column++;
if (column > 4)
{
row++;
column = 0;
}
}
}
}
}
-23
View File
@@ -1,23 +0,0 @@
<UserControl x:Class="TV_Player.ProgramsGroupGrid"
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:local="clr-namespace:TV_Player"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="groupsGrid" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
</Grid>
</UserControl>
-28
View File
@@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TV_Player
{
/// <summary>
/// Interaction logic for ProgramsGroupGrid.xaml
/// </summary>
public partial class ProgramsGroupGrid : UserControl
{
public ProgramsGroupGrid()
{
InitializeComponent();
}
}
}
+13 -7
View File
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TV Player", "TV Player\TV Player.csproj", "{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TV Player WPF", "TV Player WPF\TV Player WPF.csproj", "{556AB42C-8961-4051-B0DC-A6B907121B9E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TV Player MAUI", "TV Player\TV Player MAUI.csproj", "{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,12 +13,16 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}.Release|Any CPU.Build.0 = Release|Any CPU
{A2ADD73B-0E50-4D86-9E1B-35FB2A31F5BF}.Release|Any CPU.Deploy.0 = Release|Any CPU
{556AB42C-8961-4051-B0DC-A6B907121B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{556AB42C-8961-4051-B0DC-A6B907121B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{556AB42C-8961-4051-B0DC-A6B907121B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{556AB42C-8961-4051-B0DC-A6B907121B9E}.Release|Any CPU.Build.0 = Release|Any CPU
{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}.Release|Any CPU.Build.0 = Release|Any CPU
{4AEADD32-BAF5-4FB8-AB5B-63050CF46D29}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE