feat: consolidate legacy parity migration baseline

This commit is contained in:
2026-07-20 22:06:12 +09:00
parent 5eb4054120
commit 4a54977341
66 changed files with 5391 additions and 460 deletions

View File

@@ -178,8 +178,9 @@ public sealed partial class MainWindow
{
case LegacyOperatorPlayoutCommand.Prepare:
var request = gateAPrepareRequest ??
_controller.CreatePlayoutRequest(
_compositionOptions!.Current.FadeDuration);
await CreateFreshPagedPlayoutRequestAsync(
workflow,
cancellationToken).ConfigureAwait(false);
result = await workflow.PrepareAsync(
request.Playlist,
request.SelectedIndexZeroBased,
@@ -190,8 +191,9 @@ public sealed partial class MainWindow
// candidate) to m_crow and ran ONAirMode + Play, even while another
// row was already on air. Re-read the current selection on every F8;
// a stale explicit PREPARE must never override a later row click.
var takeInRequest = _controller.CreatePlayoutRequest(
_compositionOptions!.Current.FadeDuration);
var takeInRequest = await CreateFreshPagedPlayoutRequestAsync(
workflow,
cancellationToken).ConfigureAwait(false);
result = await workflow.TakeSelectedAsync(
takeInRequest.Playlist,
takeInRequest.SelectedIndexZeroBased,
@@ -280,6 +282,61 @@ public sealed partial class MainWindow
}
}
private async Task<LegacyOperatorPlayoutRequest> CreateFreshPagedPlayoutRequestAsync(
LegacyPlayoutWorkflow workflow,
CancellationToken cancellationToken)
{
var fadeDuration = _compositionOptions!.Current.FadeDuration;
var request = _controller.CreatePlayoutRequest(fadeDuration);
// The first PREPARE/TAKE IN freezes the complete playlist. Resolve every
// operator PageN row from a fresh read-only DB preflight before that snapshot
// is handed to the workflow. A later F8 while PREPARED/PROGRAM reuses the
// already-frozen 1/N values and must not query a second navigation contract.
if (workflow.State.CurrentEntryId is not null)
{
return request;
}
var pagedEntries = request.Playlist
.Where(static entry => entry.PageNavigation?.PageSize.HasValue == true)
.ToArray();
if (pagedEntries.Length == 0)
{
return request;
}
var plans = await workflow.PreflightPagePlansAsync(
Array.AsReadOnly(pagedEntries),
cancellationToken).ConfigureAwait(false);
if (plans.Count != pagedEntries.Length)
{
throw new LegacySceneDataException(
"The playlist page preflight did not return every PageN entry.");
}
foreach (var plan in plans)
{
var entry = pagedEntries.SingleOrDefault(candidate =>
string.Equals(candidate.EntryId, plan.EntryId, StringComparison.Ordinal));
if (entry?.PageNavigation?.PageSize is not { } expectedPageSize ||
plan.PageSize != (int)expectedPageSize ||
plan.PageCount is < 1 or > ScenePaging.MaximumPageCount)
{
throw new LegacySceneDataException(
"The playlist page preflight does not match the operator PageN row.");
}
_controller.ReflectSuccessfulPlayout(
plan.EntryId,
pageIndexZeroBased: 0,
pageCount: plan.PageCount,
synchronizeOperatorSelection: false);
}
return _controller.CreatePlayoutRequest(fadeDuration);
}
private async Task<LegacyOperatorSnapshot> ExecuteGateAPrepareAsync(
string capability,
CancellationToken cancellationToken)
@@ -596,9 +653,7 @@ public sealed partial class MainWindow
}
private static bool IsOperatorMutationIntent(LegacyUiIntent intent) => intent is
LegacyCutPointerDownIntent or
LegacyCutKeyDownIntent or
LegacyClearCutSelectionIntent or
LegacyCutPointerDownIntent { AllowAppend: true } or
LegacyDropSelectedCutsIntent or
LegacySetPlaylistEnabledIntent or
LegacySetAllPlaylistEnabledIntent or
@@ -948,6 +1003,7 @@ public sealed partial class MainWindow
}
var intentGateEntered = false;
var databaseGateEntered = false;
var playoutGateEntered = false;
try
{
@@ -958,6 +1014,9 @@ public sealed partial class MainWindow
_intentBusy = true;
QueueOperatorState();
await AcquirePriorityDatabaseActivityAsync(token).ConfigureAwait(false);
databaseGateEntered = true;
await _playoutCommandGate.WaitAsync(token).ConfigureAwait(false);
playoutGateEntered = true;
Interlocked.Exchange(ref _playoutBusy, 1);
@@ -1058,6 +1117,11 @@ public sealed partial class MainWindow
_playoutCommandGate.Release();
}
if (databaseGateEntered)
{
_databaseActivityGate.Release();
}
if (intentGateEntered)
{
_intentBusy = false;