Broadcast Text pull-out (Live screen) + launch geometry 1920x1040/1366x768 with restore

This commit is contained in:
2026-08-24 11:04:41 -07:00
parent d5b611d8d5
commit b988e6d72e
11 changed files with 670 additions and 48 deletions
+29 -14
View File
@@ -84,8 +84,6 @@ public class MainViewModel : ViewModelBase
private bool _isAboutOpen;
private bool _isLicensesOpen;
private string _overlayTitle = string.Empty;
private string _defaultStreamTitle = string.Empty;
private string _defaultStreamDescription = string.Empty;
private string _bugReportText = string.Empty;
private string _bugReportEmail = string.Empty;
private string _featureRequestText = string.Empty;
@@ -465,6 +463,7 @@ public class MainViewModel : ViewModelBase
OnPropertyChanged(nameof(IsOffline));
OnPropertyChanged(nameof(IsLive));
OnPropertyChanged(nameof(IsEditMode));
OnPropertyChanged(nameof(IsBroadcastMetadataVisible));
OnPropertyChanged(nameof(RecDotBrush));
OnPropertyChanged(nameof(RecTextBrush));
OnPropertyChanged(nameof(RecDotOpacity));
@@ -1242,17 +1241,23 @@ public class MainViewModel : ViewModelBase
private set => SetProperty(ref _overlayTitle, value);
}
public string DefaultStreamTitle
{
get => _defaultStreamTitle;
set => SetProperty(ref _defaultStreamTitle, value);
}
// ─── Broadcast metadata pull-out (the "Text" tab on the Live screen) ───
public string DefaultStreamDescription
{
get => _defaultStreamDescription;
set => SetProperty(ref _defaultStreamDescription, value);
}
/// <summary>The broadcast id of the current/last session; null until Go Live
/// succeeds. Gates the pull-out's Update button (nothing remote to patch).</summary>
public string? CurrentBroadcastId => _currentBroadcastId;
/// <summary>The pull-out exists on the Live screen only — visible while streaming.</summary>
public bool IsBroadcastMetadataVisible => IsLive;
public Services.LiveBroadcastFormViewModel BroadcastForm { get; }
// Window geometry pass-through (code-behind owns the WPF specifics).
public (double Left, double Top, double Width, double Height, string State)? RestoreWindowState()
=> _layoutStore.LoadWindowState();
public void PersistWindowState(double left, double top, double width, double height, string state)
=> _layoutStore.SaveWindowState(left, top, width, height, state);
// ─── Resolution quality dropdown (bottom bar) ───
// Tiers offered to the creator before going live. First = default. The
@@ -1561,6 +1566,11 @@ public class MainViewModel : ViewModelBase
_layoutStore = new LayoutStore(LayoutPathOverride ?? DefaultLayoutPath);
_activeLayoutPath = _layoutStore.ActivePath;
BroadcastForm = new Services.LiveBroadcastFormViewModel(
() => _layoutStore,
() => _youtubeStream,
() => CurrentBroadcastId);
// Restore the last picked mic so the mixer's first capture attaches to
// the same, already-vetted device (green dot) or reports it missing
// (yellow) instead of silently falling back to the default.
@@ -1849,6 +1859,8 @@ public class MainViewModel : ViewModelBase
SelectedTransitionType = parsed;
TransitionDurationMs = transDur;
BroadcastForm.Load();
ScheduleSave();
AppLog.Write("LoadLayout end");
}
@@ -2899,8 +2911,8 @@ public class MainViewModel : ViewModelBase
{
var dialog = new GoLiveViewModel(() => SignInAsync(), _youtubeAuth.CurrentChannel)
{
StreamTitle = DefaultStreamTitle,
StreamDescription = DefaultStreamDescription,
StreamTitle = BroadcastForm.Title,
StreamDescription = BroadcastForm.Description,
};
var window = new ytLive.GoLiveWindow(dialog) { Owner = System.Windows.Application.Current.MainWindow };
if (window.ShowDialog() == true)
@@ -2908,6 +2920,7 @@ public class MainViewModel : ViewModelBase
StreamTitle = dialog.StreamTitle;
StreamDescription = dialog.StreamDescription;
StreamVisibility = dialog.Visibility; // always "Private" (ship step 7)
BroadcastForm.CaptureGoLive(dialog.StreamTitle, dialog.StreamDescription, DateTime.UtcNow);
WindowTitle = string.IsNullOrWhiteSpace(dialog.StreamTitle)
? "LlamaCasty"
: $"{dialog.StreamTitle} — LlamaCasty";
@@ -2950,6 +2963,7 @@ public class MainViewModel : ViewModelBase
return;
}
AppLog.Write($"Broadcast created: {_currentBroadcastId}");
OnPropertyChanged(nameof(CurrentBroadcastId));
// Start chat polling (TASK 9.4): fetch the liveChatId from the broadcast's
// contentDetails, then start the poll loop. Chat is non-fatal — if it fails,
@@ -3048,6 +3062,7 @@ public class MainViewModel : ViewModelBase
_audioMixer.StopLive();
_ = _framePump.StopAsync();
_currentBroadcastId = null;
OnPropertyChanged(nameof(CurrentBroadcastId));
// Graceful end completes the session = signs out (the DPAPI token is
// cleared so the next Start Stream requires a fresh sign-in). A crash
// never runs this, so the token survives and the creator stays signed in.