201 lines
6.4 KiB
C#
201 lines
6.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using ytLive.Helpers;
|
|
using ytLive.Models;
|
|
|
|
namespace ytLive.Services;
|
|
|
|
/// <summary>
|
|
/// Backing VM for the broadcast metadata pull-out (the "Text" tab on the Live
|
|
/// screen). Owns the creator-editable liveBroadcast fields: every edit is
|
|
/// persisted to the Settings store immediately (pre-load on next run), and the
|
|
/// Update command pushes the whole snippet/status set to YouTube via
|
|
/// liveBroadcasts.update. The drawer open/close state lives here too so the
|
|
/// XAML binds one object.
|
|
/// </summary>
|
|
public sealed class LiveBroadcastFormViewModel : ViewModelBase
|
|
{
|
|
private readonly Func<LayoutStore> _store;
|
|
private readonly Func<YouTubeStreamService?> _streamService;
|
|
private readonly Func<string?> _broadcastId;
|
|
private readonly Func<bool> _isLive;
|
|
|
|
private string _title = string.Empty;
|
|
private string _description = string.Empty;
|
|
private string _tagsCsv = string.Empty;
|
|
private string _visibility = "private";
|
|
private bool _madeForKids;
|
|
private DateTime? _scheduledStartTime;
|
|
private bool _isDrawerOpen;
|
|
private bool _isUpdating;
|
|
private string? _updateError;
|
|
private BroadcastMetadata? _lastSyncedSnapshot;
|
|
|
|
public LiveBroadcastFormViewModel(
|
|
Func<LayoutStore> store,
|
|
Func<YouTubeStreamService?> streamService,
|
|
Func<string?> broadcastId,
|
|
Func<bool>? isLive = null)
|
|
{
|
|
_store = store;
|
|
_streamService = streamService;
|
|
_broadcastId = broadcastId;
|
|
_isLive = isLive ?? (() => false);
|
|
UpdateBroadcastCommand = new RelayCommand(_ => _ = UpdateRemoteAsync(), _ => CanUpdateRemote);
|
|
ToggleDrawerCommand = new RelayCommand(_ => IsDrawerOpen = !IsDrawerOpen);
|
|
CloseDrawerCommand = new RelayCommand(_ => IsDrawerOpen = false);
|
|
}
|
|
|
|
public ICommand UpdateBroadcastCommand { get; }
|
|
public RelayCommand ToggleDrawerCommand { get; }
|
|
public RelayCommand CloseDrawerCommand { get; }
|
|
|
|
public string Title { get => _title; set { if (SetProperty(ref _title, value)) Persist(); } }
|
|
public string Description { get => _description; set { if (SetProperty(ref _description, value)) Persist(); } }
|
|
public string TagsCsv { get => _tagsCsv; set { if (SetProperty(ref _tagsCsv, value)) Persist(); } }
|
|
public bool MadeForKids { get => _madeForKids; set { if (SetProperty(ref _madeForKids, value)) Persist(); } }
|
|
|
|
public string Visibility
|
|
{
|
|
get => _visibility;
|
|
set { if (SetProperty(ref _visibility, value)) Persist(); }
|
|
}
|
|
|
|
public IReadOnlyList<string> VisibilityOptions { get; } = new[] { "private", "unlisted", "public" };
|
|
|
|
public DateTime? ScheduledStartTime
|
|
{
|
|
get => _scheduledStartTime;
|
|
private set => SetProperty(ref _scheduledStartTime, value);
|
|
}
|
|
|
|
public string ScheduledStartText => ScheduledStartTime?.ToLocalTime().ToString("g") ?? "set at Go Live";
|
|
|
|
public bool IsDrawerOpen
|
|
{
|
|
get => _isDrawerOpen;
|
|
set => SetProperty(ref _isDrawerOpen, value);
|
|
}
|
|
|
|
public bool IsUpdating
|
|
{
|
|
get => _isUpdating;
|
|
private set
|
|
{
|
|
if (SetProperty(ref _isUpdating, value))
|
|
OnPropertyChanged(nameof(CanUpdateRemote));
|
|
}
|
|
}
|
|
|
|
public string? UpdateError
|
|
{
|
|
get => _updateError;
|
|
private set => SetProperty(ref _updateError, value);
|
|
}
|
|
|
|
public bool CanUpdateRemote => !IsUpdating;
|
|
|
|
/// <summary>Pull everything from the store (startup / Go Live refresh).
|
|
/// Seeds the synced snapshot so the next Update click knows what is already remote.</summary>
|
|
public void Load()
|
|
{
|
|
var meta = _store().LoadBroadcastMetadata();
|
|
_title = meta.Title;
|
|
_description = meta.Description;
|
|
_tagsCsv = meta.TagsCsv;
|
|
_visibility = meta.Visibility;
|
|
_madeForKids = meta.MadeForKids;
|
|
ScheduledStartTime = meta.ScheduledStartTime;
|
|
OnPropertyChanged(nameof(Title));
|
|
OnPropertyChanged(nameof(Description));
|
|
OnPropertyChanged(nameof(TagsCsv));
|
|
OnPropertyChanged(nameof(Visibility));
|
|
OnPropertyChanged(nameof(MadeForKids));
|
|
OnPropertyChanged(nameof(ScheduledStartText));
|
|
SnapshotSynced();
|
|
}
|
|
|
|
/// <summary>Called at Go Live with what was actually inserted remotely.</summary>
|
|
public void CaptureGoLive(string title, string description, DateTime scheduledStart)
|
|
{
|
|
Title = title;
|
|
Description = description;
|
|
ScheduledStartTime = scheduledStart;
|
|
UpdateError = null;
|
|
SnapshotSynced();
|
|
}
|
|
|
|
public BroadcastMetadata ToMetadata() => new()
|
|
{
|
|
Title = Title,
|
|
Description = Description,
|
|
TagsCsv = TagsCsv,
|
|
Visibility = Visibility,
|
|
MadeForKids = MadeForKids,
|
|
ScheduledStartTime = ScheduledStartTime,
|
|
};
|
|
|
|
private void Persist()
|
|
{
|
|
try
|
|
{
|
|
_store().SaveBroadcastMetadata(ToMetadata());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AppLog.Write($"Broadcast metadata save failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private async Task UpdateRemoteAsync()
|
|
{
|
|
var id = _broadcastId();
|
|
var service = _streamService();
|
|
if (IsUpdating) return;
|
|
IsUpdating = true;
|
|
UpdateError = null;
|
|
try
|
|
{
|
|
var current = ToMetadata();
|
|
if (!_isLive())
|
|
{
|
|
UpdateError = null;
|
|
IsDrawerOpen = false;
|
|
return;
|
|
}
|
|
if (id == null || service == null)
|
|
{
|
|
UpdateError = null;
|
|
IsDrawerOpen = false;
|
|
return;
|
|
}
|
|
if (_lastSyncedSnapshot != null && current.Equals(_lastSyncedSnapshot))
|
|
{
|
|
UpdateError = "No changes to update";
|
|
return;
|
|
}
|
|
var error = await service.UpdateBroadcast(id, current);
|
|
UpdateError = error;
|
|
if (error == null)
|
|
{
|
|
SnapshotSynced();
|
|
IsDrawerOpen = false;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AppLog.Write($"Broadcast update failed: {ex.Message}");
|
|
UpdateError = ex.Message;
|
|
}
|
|
finally
|
|
{
|
|
IsUpdating = false;
|
|
}
|
|
}
|
|
|
|
private void SnapshotSynced() => _lastSyncedSnapshot = ToMetadata();
|
|
}
|