From 9103ff1fb73f31592eae96ac5d7bea8c22fe139d Mon Sep 17 00:00:00 2001 From: gramps Date: Tue, 4 Aug 2026 09:09:52 -0700 Subject: [PATCH] Initial scaffold: ytLive C# WPF project - MVVM architecture (Models, ViewModels, Views, Services, Helpers) - Models: Scene, Source, StreamConfig, StreamHealth, YouTubeChannel, ChatMessage - Services: YouTubeAuthService (OAuth2), YouTubeStreamService (broadcasts/stream health), YouTubeChatService (live chat polling) - ViewModels: MainViewModel with scene management, stream controls, chat - MainWindow with dark theme: scene/source panel, preview area, chat panel, status bar - Version roadmap: v0.1 (scenes, RTMP, OAuth2, chat, health) -> 1.0 --- App.xaml | 9 ++ App.xaml.cs | 13 ++ AssemblyInfo.cs | 10 ++ Helpers/RelayCommand.cs | 24 +++ Helpers/ViewModelBase.cs | 23 +++ MainWindow.xaml | 242 +++++++++++++++++++++++++++++++ MainWindow.xaml.cs | 11 ++ Models/Scene.cs | 8 + Models/Source.cs | 35 +++++ Models/StreamConfig.cs | 35 +++++ Models/YouTube.cs | 26 ++++ Services/YouTubeAuthService.cs | 118 +++++++++++++++ Services/YouTubeChatService.cs | 102 +++++++++++++ Services/YouTubeStreamService.cs | 129 ++++++++++++++++ ViewModels/MainViewModel.cs | 143 ++++++++++++++++++ ytLive.csproj | 15 ++ 16 files changed, 943 insertions(+) create mode 100644 App.xaml create mode 100644 App.xaml.cs create mode 100644 AssemblyInfo.cs create mode 100644 Helpers/RelayCommand.cs create mode 100644 Helpers/ViewModelBase.cs create mode 100644 MainWindow.xaml create mode 100644 MainWindow.xaml.cs create mode 100644 Models/Scene.cs create mode 100644 Models/Source.cs create mode 100644 Models/StreamConfig.cs create mode 100644 Models/YouTube.cs create mode 100644 Services/YouTubeAuthService.cs create mode 100644 Services/YouTubeChatService.cs create mode 100644 Services/YouTubeStreamService.cs create mode 100644 ViewModels/MainViewModel.cs create mode 100644 ytLive.csproj diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..2bf3cea --- /dev/null +++ b/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..1ff0ff3 --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,13 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace ytLive; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} + diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..c73865b --- /dev/null +++ b/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/Helpers/RelayCommand.cs b/Helpers/RelayCommand.cs new file mode 100644 index 0000000..9ea4c8f --- /dev/null +++ b/Helpers/RelayCommand.cs @@ -0,0 +1,24 @@ +using System.Windows.Input; + +namespace ytLive.Helpers; + +public class RelayCommand : ICommand +{ + private readonly Action _execute; + private readonly Func? _canExecute; + + public RelayCommand(Action execute, Func? canExecute = null) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler? CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + + public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true; + public void Execute(object? parameter) => _execute(parameter); +} diff --git a/Helpers/ViewModelBase.cs b/Helpers/ViewModelBase.cs new file mode 100644 index 0000000..f762d2c --- /dev/null +++ b/Helpers/ViewModelBase.cs @@ -0,0 +1,23 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace ytLive.Helpers; + +public abstract class ViewModelBase : INotifyPropertyChanged +{ + public event PropertyChangedEventHandler? PropertyChanged; + + protected bool SetProperty(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) + return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + + protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100644 index 0000000..8d4fb81 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +