using System.Collections.ObjectModel; using System.Windows; using System.Windows.Input; using System.Windows.Threading; using ytLive.Helpers; using ytLive.Models; using ytLive.Services; namespace ytLive.ViewModels; public class MainViewModel : ViewModelBase { private readonly YouTubeAuthService _youtubeAuth; private readonly YouTubeStreamService _youtubeStream; private readonly YouTubeChatService _youtubeChat; private readonly DispatcherTimer _liveTimer; private Scene? _activeScene; private StreamStatus _streamStatus = StreamStatus.Offline; private StreamHealth _currentHealth = new(); private string _streamTitle = string.Empty; private string _streamDescription = string.Empty; private string _streamVisibility = "Public"; private string _windowTitle = "ytLive"; private string _topBarBackground = "#16213e"; private string _previewGlowBrush = "Transparent"; private Thickness _previewGlowThickness = new(0); private string _liveElapsedText = "00:00:00"; private double _livePulseOpacity = 1.0; private TimeSpan _liveElapsed; public ObservableCollection Scenes { get; } = new(); public ObservableCollection ChatMessages { get; } = new(); public Scene? ActiveScene { get => _activeScene; set => SetProperty(ref _activeScene, value); } public StreamStatus StreamStatus { get => _streamStatus; set { if (SetProperty(ref _streamStatus, value)) { OnPropertyChanged(nameof(IsOffline)); OnPropertyChanged(nameof(IsLive)); OnPropertyChanged(nameof(LiveIndicatorVisible)); UpdateLiveVisuals(); } } } public bool IsOffline => StreamStatus == StreamStatus.Offline; public bool IsLive => StreamStatus == StreamStatus.Streaming; public bool LiveIndicatorVisible => IsLive; public StreamHealth CurrentHealth { get => _currentHealth; set => SetProperty(ref _currentHealth, value); } public string StreamTitle { get => _streamTitle; set => SetProperty(ref _streamTitle, value); } public string StreamDescription { get => _streamDescription; set => SetProperty(ref _streamDescription, value); } public string StreamVisibility { get => _streamVisibility; set => SetProperty(ref _streamVisibility, value); } public string WindowTitle { get => _windowTitle; set => SetProperty(ref _windowTitle, value); } public string TopBarBackground { get => _topBarBackground; set => SetProperty(ref _topBarBackground, value); } public string PreviewGlowBrush { get => _previewGlowBrush; set => SetProperty(ref _previewGlowBrush, value); } public Thickness PreviewGlowThickness { get => _previewGlowThickness; set => SetProperty(ref _previewGlowThickness, value); } public string LiveElapsedText { get => _liveElapsedText; set => SetProperty(ref _liveElapsedText, value); } public double LivePulseOpacity { get => _livePulseOpacity; set => SetProperty(ref _livePulseOpacity, value); } // Commands public ICommand AddSceneCommand { get; } public ICommand RemoveSceneCommand { get; } public ICommand StartStreamCommand { get; } public ICommand EndStreamCommand { get; } public MainViewModel() { _youtubeAuth = new YouTubeAuthService("", ""); // TODO: load from config _youtubeStream = new YouTubeStreamService(_youtubeAuth); _youtubeChat = new YouTubeChatService(_youtubeAuth); _youtubeChat.MessageReceived += OnChatMessageReceived; _liveTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _liveTimer.Tick += OnLiveTimerTick; AddSceneCommand = new RelayCommand(_ => AddScene()); RemoveSceneCommand = new RelayCommand(scene => RemoveScene(scene as Scene)); StartStreamCommand = new RelayCommand(_ => BeginGoLive()); EndStreamCommand = new RelayCommand(_ => StopStream(), _ => IsLive); // Start with a default scene AddScene("Scene 1"); } private void AddScene(string? name = null) { var scene = new Scene { Name = name ?? $"Scene {Scenes.Count + 1}" }; Scenes.Add(scene); ActiveScene = scene; } private void RemoveScene(Scene? scene) { if (scene == null) return; Scenes.Remove(scene); if (ActiveScene == scene) ActiveScene = Scenes.FirstOrDefault(); } private void OnChatMessageReceived(ChatMessage message) { System.Windows.Application.Current.Dispatcher.Invoke(() => { ChatMessages.Add(message); if (ChatMessages.Count > 500) ChatMessages.RemoveAt(0); }); } private void BeginGoLive() { var dialog = new GoLiveViewModel(); var window = new ytLive.GoLiveWindow(dialog) { Owner = System.Windows.Application.Current.MainWindow }; if (window.ShowDialog() == true) { StreamTitle = dialog.StreamTitle; StreamDescription = dialog.StreamDescription; StreamVisibility = dialog.Visibility; WindowTitle = string.IsNullOrWhiteSpace(dialog.StreamTitle) ? "ytLive" : $"{dialog.StreamTitle} — ytLive"; StreamStatus = StreamStatus.Streaming; } } private void StopStream() { StreamStatus = StreamStatus.Offline; WindowTitle = "ytLive"; } private void UpdateLiveVisuals() { var live = IsLive; TopBarBackground = live ? "#e94560" : "#16213e"; PreviewGlowBrush = live ? "#e94560" : "Transparent"; PreviewGlowThickness = live ? new Thickness(3) : new Thickness(0); if (live) { _liveElapsed = TimeSpan.Zero; LiveElapsedText = "00:00:00"; LivePulseOpacity = 1.0; _liveTimer.Start(); } else { _liveTimer.Stop(); LiveElapsedText = "00:00:00"; LivePulseOpacity = 1.0; } } private void OnLiveTimerTick(object? sender, EventArgs e) { _liveElapsed = _liveElapsed.Add(TimeSpan.FromSeconds(1)); LiveElapsedText = _liveElapsed.ToString(@"hh\:mm\:ss"); LivePulseOpacity = LivePulseOpacity > 0.5 ? 0.35 : 1.0; } }