diff --git a/TV Player/App.xaml b/TV Player/App.xaml new file mode 100644 index 0000000..afed686 --- /dev/null +++ b/TV Player/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/TV Player/App.xaml.cs b/TV Player/App.xaml.cs new file mode 100644 index 0000000..8a99059 --- /dev/null +++ b/TV Player/App.xaml.cs @@ -0,0 +1,12 @@ +namespace TV_Player +{ + public partial class App : Application + { + public App() + { + InitializeComponent(); + + MainPage = new AppShell(); + } + } +} diff --git a/TV Player/AppShell.xaml b/TV Player/AppShell.xaml new file mode 100644 index 0000000..d5d383b --- /dev/null +++ b/TV Player/AppShell.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/TV Player/AppShell.xaml.cs b/TV Player/AppShell.xaml.cs new file mode 100644 index 0000000..ed0c74f --- /dev/null +++ b/TV Player/AppShell.xaml.cs @@ -0,0 +1,10 @@ +namespace TV_Player +{ + public partial class AppShell : Shell + { + public AppShell() + { + InitializeComponent(); + } + } +} diff --git a/TV Player/MainPage.xaml b/TV Player/MainPage.xaml new file mode 100644 index 0000000..4e3ce52 --- /dev/null +++ b/TV Player/MainPage.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/TV Player/MainPage.xaml.cs b/TV Player/MainPage.xaml.cs new file mode 100644 index 0000000..352c26a --- /dev/null +++ b/TV Player/MainPage.xaml.cs @@ -0,0 +1,23 @@ +namespace TV_Player +{ + public partial class MainPage : ContentPage + { + public MainPage() + { + 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); + //} + } + +} diff --git a/TV Player/MauiProgram.cs b/TV Player/MauiProgram.cs new file mode 100644 index 0000000..df475a4 --- /dev/null +++ b/TV Player/MauiProgram.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Logging; + +namespace TV_Player +{ + public static class MauiProgram + { + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + +#if DEBUG + builder.Logging.AddDebug(); +#endif + + return builder.Build(); + } + } +} diff --git a/TV Player/Platforms/Android/AndroidManifest.xml b/TV Player/Platforms/Android/AndroidManifest.xml new file mode 100644 index 0000000..e9937ad --- /dev/null +++ b/TV Player/Platforms/Android/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TV Player/Platforms/Android/MainActivity.cs b/TV Player/Platforms/Android/MainActivity.cs new file mode 100644 index 0000000..5296c3d --- /dev/null +++ b/TV Player/Platforms/Android/MainActivity.cs @@ -0,0 +1,11 @@ +using Android.App; +using Android.Content.PM; +using Android.OS; + +namespace TV_Player +{ + [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] + public class MainActivity : MauiAppCompatActivity + { + } +} diff --git a/TV Player/Platforms/Android/MainApplication.cs b/TV Player/Platforms/Android/MainApplication.cs new file mode 100644 index 0000000..8424df7 --- /dev/null +++ b/TV Player/Platforms/Android/MainApplication.cs @@ -0,0 +1,16 @@ +using Android.App; +using Android.Runtime; + +namespace TV_Player +{ + [Application] + public class MainApplication : MauiApplication + { + public MainApplication(IntPtr handle, JniHandleOwnership ownership) + : base(handle, ownership) + { + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} diff --git a/TV Player/Platforms/Android/Resources/values/colors.xml b/TV Player/Platforms/Android/Resources/values/colors.xml new file mode 100644 index 0000000..c04d749 --- /dev/null +++ b/TV Player/Platforms/Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #512BD4 + #2B0B98 + #2B0B98 + \ No newline at end of file diff --git a/TV Player/Platforms/MacCatalyst/AppDelegate.cs b/TV Player/Platforms/MacCatalyst/AppDelegate.cs new file mode 100644 index 0000000..237a786 --- /dev/null +++ b/TV Player/Platforms/MacCatalyst/AppDelegate.cs @@ -0,0 +1,10 @@ +using Foundation; + +namespace TV_Player +{ + [Register("AppDelegate")] + public class AppDelegate : MauiUIApplicationDelegate + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} diff --git a/TV Player/Platforms/MacCatalyst/Info.plist b/TV Player/Platforms/MacCatalyst/Info.plist new file mode 100644 index 0000000..c96dd0a --- /dev/null +++ b/TV Player/Platforms/MacCatalyst/Info.plist @@ -0,0 +1,30 @@ + + + + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + diff --git a/TV Player/Platforms/MacCatalyst/Program.cs b/TV Player/Platforms/MacCatalyst/Program.cs new file mode 100644 index 0000000..d6861d1 --- /dev/null +++ b/TV Player/Platforms/MacCatalyst/Program.cs @@ -0,0 +1,16 @@ +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)); + } + } +} diff --git a/TV Player/Platforms/Tizen/Main.cs b/TV Player/Platforms/Tizen/Main.cs new file mode 100644 index 0000000..2c6aa6e --- /dev/null +++ b/TV Player/Platforms/Tizen/Main.cs @@ -0,0 +1,17 @@ +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); + } + } +} diff --git a/TV Player/Platforms/Tizen/tizen-manifest.xml b/TV Player/Platforms/Tizen/tizen-manifest.xml new file mode 100644 index 0000000..f3f0641 --- /dev/null +++ b/TV Player/Platforms/Tizen/tizen-manifest.xml @@ -0,0 +1,15 @@ + + + + + + maui-appicon-placeholder + + + + + http://tizen.org/privilege/internet + + + + \ No newline at end of file diff --git a/TV Player/Platforms/Windows/App.xaml b/TV Player/Platforms/Windows/App.xaml new file mode 100644 index 0000000..41a5378 --- /dev/null +++ b/TV Player/Platforms/Windows/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/TV Player/Platforms/Windows/App.xaml.cs b/TV Player/Platforms/Windows/App.xaml.cs new file mode 100644 index 0000000..98341ae --- /dev/null +++ b/TV Player/Platforms/Windows/App.xaml.cs @@ -0,0 +1,25 @@ +using Microsoft.UI.Xaml; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace TV_Player.WinUI +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : MauiWinUIApplication + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } + +} diff --git a/TV Player/Platforms/Windows/Package.appxmanifest b/TV Player/Platforms/Windows/Package.appxmanifest new file mode 100644 index 0000000..4211f56 --- /dev/null +++ b/TV Player/Platforms/Windows/Package.appxmanifest @@ -0,0 +1,46 @@ + + + + + + + + + $placeholder$ + User Name + $placeholder$.png + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TV Player/Platforms/Windows/app.manifest b/TV Player/Platforms/Windows/app.manifest new file mode 100644 index 0000000..211915d --- /dev/null +++ b/TV Player/Platforms/Windows/app.manifest @@ -0,0 +1,15 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + diff --git a/TV Player/Platforms/iOS/AppDelegate.cs b/TV Player/Platforms/iOS/AppDelegate.cs new file mode 100644 index 0000000..237a786 --- /dev/null +++ b/TV Player/Platforms/iOS/AppDelegate.cs @@ -0,0 +1,10 @@ +using Foundation; + +namespace TV_Player +{ + [Register("AppDelegate")] + public class AppDelegate : MauiUIApplicationDelegate + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} diff --git a/TV Player/Platforms/iOS/Info.plist b/TV Player/Platforms/iOS/Info.plist new file mode 100644 index 0000000..0004a4f --- /dev/null +++ b/TV Player/Platforms/iOS/Info.plist @@ -0,0 +1,32 @@ + + + + + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + diff --git a/TV Player/Platforms/iOS/Program.cs b/TV Player/Platforms/iOS/Program.cs new file mode 100644 index 0000000..d6861d1 --- /dev/null +++ b/TV Player/Platforms/iOS/Program.cs @@ -0,0 +1,16 @@ +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)); + } + } +} diff --git a/TV Player/Properties/launchSettings.json b/TV Player/Properties/launchSettings.json new file mode 100644 index 0000000..edf8aad --- /dev/null +++ b/TV Player/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Windows Machine": { + "commandName": "MsixPackage", + "nativeDebugging": false + } + } +} \ No newline at end of file diff --git a/TV Player/Resources/AppIcon/appicon.svg b/TV Player/Resources/AppIcon/appicon.svg new file mode 100644 index 0000000..9d63b65 --- /dev/null +++ b/TV Player/Resources/AppIcon/appicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/TV Player/Resources/AppIcon/appiconfg.svg b/TV Player/Resources/AppIcon/appiconfg.svg new file mode 100644 index 0000000..21dfb25 --- /dev/null +++ b/TV Player/Resources/AppIcon/appiconfg.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/TV Player/Resources/Fonts/OpenSans-Regular.ttf b/TV Player/Resources/Fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..dadcb01 Binary files /dev/null and b/TV Player/Resources/Fonts/OpenSans-Regular.ttf differ diff --git a/TV Player/Resources/Fonts/OpenSans-Semibold.ttf b/TV Player/Resources/Fonts/OpenSans-Semibold.ttf new file mode 100644 index 0000000..5601642 Binary files /dev/null and b/TV Player/Resources/Fonts/OpenSans-Semibold.ttf differ diff --git a/TV Player/Resources/Images/dotnet_bot.svg b/TV Player/Resources/Images/dotnet_bot.svg new file mode 100644 index 0000000..abfaff2 --- /dev/null +++ b/TV Player/Resources/Images/dotnet_bot.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TV Player/Resources/Raw/AboutAssets.txt b/TV Player/Resources/Raw/AboutAssets.txt new file mode 100644 index 0000000..15d6244 --- /dev/null +++ b/TV Player/Resources/Raw/AboutAssets.txt @@ -0,0 +1,15 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories). Deployment of the asset to your application +is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. + + + +These files will be deployed with you package and will be accessible using Essentials: + + async Task LoadMauiAsset() + { + using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); + using var reader = new StreamReader(stream); + + var contents = reader.ReadToEnd(); + } diff --git a/TV Player/Resources/Splash/splash.svg b/TV Player/Resources/Splash/splash.svg new file mode 100644 index 0000000..21dfb25 --- /dev/null +++ b/TV Player/Resources/Splash/splash.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/TV Player/Resources/Styles/Colors.xaml b/TV Player/Resources/Styles/Colors.xaml new file mode 100644 index 0000000..245758b --- /dev/null +++ b/TV Player/Resources/Styles/Colors.xaml @@ -0,0 +1,44 @@ + + + + + #512BD4 + #DFD8F7 + #2B0B98 + White + Black + #E1E1E1 + #C8C8C8 + #ACACAC + #919191 + #6E6E6E + #404040 + #212121 + #141414 + + + + + + + + + + + + + + + #F7B548 + #FFD590 + #FFE5B9 + #28C2D1 + #7BDDEF + #C3F2F4 + #3E8EED + #72ACF1 + #A7CBF6 + + \ No newline at end of file diff --git a/TV Player/Resources/Styles/Styles.xaml b/TV Player/Resources/Styles/Styles.xaml new file mode 100644 index 0000000..dc4a034 --- /dev/null +++ b/TV Player/Resources/Styles/Styles.xaml @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TV Player/TV Player.csproj b/TV Player/TV Player.csproj new file mode 100644 index 0000000..d298b45 --- /dev/null +++ b/TV Player/TV Player.csproj @@ -0,0 +1,68 @@ + + + + net8.0-android + $(TargetFrameworks);net8.0-windows10.0.19041.0 + + + Exe + TV_Player + true + true + enable + + + TV Player + + + com.companyname.tv_player + 5d5f328c-695f-403a-b602-eb8e64927cc2 + + + 1.0 + 1 + + 11.0 + 13.1 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7.0.1 + + + + + + 7.0.1 + + + + diff --git a/TV Player/ViewModels/GroupInfo.cs b/TV Player/ViewModels/GroupInfo.cs new file mode 100644 index 0000000..99a3d84 --- /dev/null +++ b/TV Player/ViewModels/GroupInfo.cs @@ -0,0 +1,9 @@ +namespace TV_Player +{ + public class GroupInfo + + { + public string Name { get; set; } + public int Count { get; set; } + } +} diff --git a/TV Player/ViewModels/M3UInfo.cs b/TV Player/ViewModels/M3UInfo.cs new file mode 100644 index 0000000..c46ee17 --- /dev/null +++ b/TV Player/ViewModels/M3UInfo.cs @@ -0,0 +1,14 @@ +namespace TV_Player +{ + public class M3UInfo + { + public string CUID { get; set; } + public string Number { get; set; } + public string TvgID { get; set; } + public string TvgName { get; set; } + public string GroupTitle { get; set; } + public string Logo { get; set; } + public string Name{ get; set; } + public string Url { get; set; } + } +} diff --git a/TV Player/ViewModels/M3UParser.cs b/TV Player/ViewModels/M3UParser.cs new file mode 100644 index 0000000..9143db0 --- /dev/null +++ b/TV Player/ViewModels/M3UParser.cs @@ -0,0 +1,100 @@ +using System.Net.Http.Headers; +using System.Text.RegularExpressions; + +namespace TV_Player +{ + public static class M3UParser + { + public static async Task> DownloadM3UFromWebAsync(string url) + { + List playlistItems = new List(); + + using (var client = new HttpClient()) + using (var request = new HttpRequestMessage()) + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text")); + request.Method = HttpMethod.Get; + request.RequestUri = new Uri(url); + var response = await client.GetAsync(url); + response.EnsureSuccessStatusCode(); + string responseBody = await response.Content.ReadAsStringAsync(); + + // 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]; + } + } + + return parts; + } + + static List ParseM3UFromString(string content) + { + List playlistItems = new List(); + + try + { + var m3u = SplitStringBeforeSeparator(content, "#EXT"); + + foreach (var line in m3u) + { + if (line.StartsWith("#EXTINF:")) + { + if (TryParseM3ULine(line, out var m3uInfo)) + { + playlistItems.Add(m3uInfo); + } + } + } + } + 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=""(?.*?)"" number=""(?.*?)"" tvg-id=""(?.*?)"" tvg-name=""(?.*?)"".*?tvg-logo=""(?.*?)"" group-title=""(?.*?)""[^,]*,(?.*)[^\r](?.*)$"; + 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; + } + } +} diff --git a/TV Player/ViewModels/MainViewModel.cs b/TV Player/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..7ff8653 --- /dev/null +++ b/TV Player/ViewModels/MainViewModel.cs @@ -0,0 +1,28 @@ +using System.Windows.Input; + +namespace TV_Player +{ + public class MainViewModel : ObservableViewModelBase + { + private List _programs; + public List Programs + { + get => _programs; + set => SetProperty(ref _programs, value); + } + + public GroupInfo SelectedItem { get; set; } + public ICommand ItemSelectedCommand { get; } + + public MainViewModel() + { + ItemSelectedCommand = new Command(OnItemSelected); + ProgramsData.Instance.GroupsInformation.Subscribe(x=>Programs = x); + } + + private void OnItemSelected() + { + + } + } +} diff --git a/TV Player/ViewModels/ObservableViewModelBase.cs b/TV Player/ViewModels/ObservableViewModelBase.cs new file mode 100644 index 0000000..f54f263 --- /dev/null +++ b/TV Player/ViewModels/ObservableViewModelBase.cs @@ -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)); + + /// + /// Set a property and raise a property changed event if it has changed + /// + protected bool SetProperty(ref T property, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(property, value)) + { + return false; + } + + property = value; + RaisePropertyChanged(propertyName); + return true; + } + } +} diff --git a/TV Player/ViewModels/ProgramsData.cs b/TV Player/ViewModels/ProgramsData.cs new file mode 100644 index 0000000..0d5e8e6 --- /dev/null +++ b/TV Player/ViewModels/ProgramsData.cs @@ -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 Subject> programsSubject = new Subject>(); + private readonly Subject> groupsSubject = new Subject>(); + public IObservable> AllPrograms => programsSubject; + public Subject> 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); + } + } +} diff --git a/TV Player_old/App.xaml b/TV Player_old/App.xaml new file mode 100644 index 0000000..1dee3fc --- /dev/null +++ b/TV Player_old/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/TV Player_old/App.xaml.cs b/TV Player_old/App.xaml.cs new file mode 100644 index 0000000..ac39428 --- /dev/null +++ b/TV Player_old/App.xaml.cs @@ -0,0 +1,14 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace TV_Player +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } + +} diff --git a/TV Player_old/AssemblyInfo.cs b/TV Player_old/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/TV Player_old/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/TV Player_old/Assets/bkground.jpg b/TV Player_old/Assets/bkground.jpg new file mode 100644 index 0000000..73f1a2d Binary files /dev/null and b/TV Player_old/Assets/bkground.jpg differ diff --git a/TV Player_old/EnumToBooleanConverter.cs b/TV Player_old/EnumToBooleanConverter.cs new file mode 100644 index 0000000..1205a56 --- /dev/null +++ b/TV Player_old/EnumToBooleanConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace TV_Player +{ + public class EnumToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value.Equals(parameter); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value.Equals(true) ? parameter : Binding.DoNothing; + } + } +} diff --git a/TV Player_old/GroupButton.xaml b/TV Player_old/GroupButton.xaml new file mode 100644 index 0000000..7f54c71 --- /dev/null +++ b/TV Player_old/GroupButton.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + Programs + + + + + diff --git a/TV Player_old/GroupButton.xaml.cs b/TV Player_old/GroupButton.xaml.cs new file mode 100644 index 0000000..d9985fb --- /dev/null +++ b/TV Player_old/GroupButton.xaml.cs @@ -0,0 +1,17 @@ +using System.Windows.Controls; + +namespace TV_Player +{ + /// + /// Interaction logic for GroupButton.xaml + /// + public partial class GroupButton : UserControl + { + public GroupButton(string groupName,int programsFound) + { + InitializeComponent(); + this.groupName.Text = groupName; + this.programsFound.Text = programsFound.ToString(); + } + } +} diff --git a/TV Player_old/M3UInfo.cs b/TV Player_old/M3UInfo.cs new file mode 100644 index 0000000..c46ee17 --- /dev/null +++ b/TV Player_old/M3UInfo.cs @@ -0,0 +1,14 @@ +namespace TV_Player +{ + public class M3UInfo + { + public string CUID { get; set; } + public string Number { get; set; } + public string TvgID { get; set; } + public string TvgName { get; set; } + public string GroupTitle { get; set; } + public string Logo { get; set; } + public string Name{ get; set; } + public string Url { get; set; } + } +} diff --git a/TV Player_old/M3UParser.cs b/TV Player_old/M3UParser.cs new file mode 100644 index 0000000..765c6e9 --- /dev/null +++ b/TV Player_old/M3UParser.cs @@ -0,0 +1,101 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.RegularExpressions; + +namespace TV_Player +{ + public static class M3UParser + { + public static async Task> DownloadM3UFromWebAsync(string url) + { + List playlistItems = new List(); + + using (var client = new HttpClient()) + using (var request = new HttpRequestMessage()) + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/text")); + request.Method = HttpMethod.Get; + request.RequestUri = new Uri(url); + var response = await client.GetAsync(url); + response.EnsureSuccessStatusCode(); + string responseBody = await response.Content.ReadAsStringAsync(); + + // 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]; + } + } + + return parts; + } + + static List ParseM3UFromString(string content) + { + List playlistItems = new List(); + + try + { + var m3u = SplitStringBeforeSeparator(content, "#EXT"); + + foreach (var line in m3u) + { + if (line.StartsWith("#EXTINF:")) + { + if (TryParseM3ULine(line, out var m3uInfo)) + { + playlistItems.Add(m3uInfo); + } + } + } + } + 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=""(?.*?)"" number=""(?.*?)"" tvg-id=""(?.*?)"" tvg-name=""(?.*?)"".*?tvg-logo=""(?.*?)"" group-title=""(?.*?)""[^,]*,(?.*)[^\r](?.*)$"; + 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; + } + } +} diff --git a/TV Player_old/MainWindow.xaml b/TV Player_old/MainWindow.xaml new file mode 100644 index 0000000..edfd22e --- /dev/null +++ b/TV Player_old/MainWindow.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/TV Player_old/MainWindow.xaml.cs b/TV Player_old/MainWindow.xaml.cs new file mode 100644 index 0000000..23fedba --- /dev/null +++ b/TV Player_old/MainWindow.xaml.cs @@ -0,0 +1,102 @@ +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"; + } + } +} \ No newline at end of file diff --git a/TV Player_old/MainWindowViewModel.cs b/TV Player_old/MainWindowViewModel.cs new file mode 100644 index 0000000..371c267 --- /dev/null +++ b/TV Player_old/MainWindowViewModel.cs @@ -0,0 +1,43 @@ +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; + } + } + } + } +} diff --git a/TV Player_old/ProgramsGroupGrid.xaml b/TV Player_old/ProgramsGroupGrid.xaml new file mode 100644 index 0000000..83d6964 --- /dev/null +++ b/TV Player_old/ProgramsGroupGrid.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/TV Player_old/ProgramsGroupGrid.xaml.cs b/TV Player_old/ProgramsGroupGrid.xaml.cs new file mode 100644 index 0000000..19a26f0 --- /dev/null +++ b/TV Player_old/ProgramsGroupGrid.xaml.cs @@ -0,0 +1,28 @@ +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 +{ + /// + /// Interaction logic for ProgramsGroupGrid.xaml + /// + public partial class ProgramsGroupGrid : UserControl + { + public ProgramsGroupGrid() + { + InitializeComponent(); + } + } +} diff --git a/TV Player_old/TV Player.csproj b/TV Player_old/TV Player.csproj new file mode 100644 index 0000000..3c5e77a --- /dev/null +++ b/TV Player_old/TV Player.csproj @@ -0,0 +1,29 @@ + + + + WinExe + net8.0-windows + TV_Player + enable + enable + true + + + + + + + + + + + + + + + + Never + + + + diff --git a/TV player.sln b/TV player.sln new file mode 100644 index 0000000..29c741a --- /dev/null +++ b/TV player.sln @@ -0,0 +1,27 @@ + +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}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + 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 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FEE14CDB-8F69-4F66-8937-B9148BBE9088} + EndGlobalSection +EndGlobal