Restart schedule from bottom active item

This commit is contained in:
2026-05-14 12:07:18 +09:00
parent 258b3ddaeb
commit 72afee11fc

View File

@@ -26,6 +26,7 @@ public sealed class ChannelScheduleEngine
private TaskCompletionSource<bool>? _advanceSignal;
private Guid? _lastPlaybackItemId;
private Guid? _skipCurrentItemId;
private Guid? _restartFromTopAfterItemId;
private ChannelScheduleItem? _directPlaybackItem;
private PreparedCutFrame? _preparedCutFrame;
@@ -77,6 +78,12 @@ public sealed class ChannelScheduleEngine
{
if (IsRunning)
{
if (GetNextPlayableItem() is null)
{
await RestartFromTopAsync().ConfigureAwait(false);
return;
}
await AdvanceToNextAsync().ConfigureAwait(false);
return;
}
@@ -87,7 +94,10 @@ public sealed class ChannelScheduleEngine
}
_lastPlaybackItemId = null;
_skipCurrentItemId = null;
_restartFromTopAfterItemId = null;
ResetDataUnavailableItems();
ClearStalePlaybackStatesBeforeStart();
_playbackCts = new CancellationTokenSource();
IsRunning = true;
RefreshQueueMarkers();
@@ -112,6 +122,7 @@ public sealed class ChannelScheduleEngine
ClearPreparedFrame(resetState: true);
_lastPlaybackItemId = null;
_skipCurrentItemId = null;
_restartFromTopAfterItemId = null;
RefreshQueueMarkers();
QueueChanged?.Invoke(this, EventArgs.Empty);
return;
@@ -135,6 +146,7 @@ public sealed class ChannelScheduleEngine
_lastPlaybackItemId = null;
_skipCurrentItemId = null;
_restartFromTopAfterItemId = null;
ClearPreparedFrame(resetState: false);
IsRunning = false;
RefreshQueueMarkers();
@@ -152,6 +164,8 @@ public sealed class ChannelScheduleEngine
try
{
_lastPlaybackItemId = null;
_skipCurrentItemId = null;
_restartFromTopAfterItemId = null;
ResetDataUnavailableItems();
ClearPreparedFrame(resetState: true);
RefreshQueueMarkers();
@@ -260,6 +274,8 @@ public sealed class ChannelScheduleEngine
public void Reset()
{
_lastPlaybackItemId = null;
_skipCurrentItemId = null;
_restartFromTopAfterItemId = null;
ClearPreparedFrame(resetState: false);
foreach (var item in Queue)
{
@@ -327,6 +343,37 @@ public sealed class ChannelScheduleEngine
return Task.CompletedTask;
}
private Task RestartFromTopAsync()
{
if (!IsRunning)
{
return Task.CompletedTask;
}
var activeItem = Queue.FirstOrDefault(item => item.State is ScheduleQueueItemState.OnAir or ScheduleQueueItemState.Sending);
if (activeItem is not null)
{
_skipCurrentItemId = activeItem.Id;
_restartFromTopAfterItemId = activeItem.Id;
_lastPlaybackItemId = null;
activeItem.State = ScheduleQueueItemState.Queued;
activeItem.LastError = string.Empty;
activeItem.CurrentRegionLabel = string.Empty;
activeItem.ClearInternalNextPreview();
activeItem.ClearPlaybackCountdown();
}
else
{
_lastPlaybackItemId = null;
_restartFromTopAfterItemId = null;
}
RefreshQueueMarkers();
QueueChanged?.Invoke(this, EventArgs.Empty);
_advanceSignal?.TrySetResult(true);
return Task.CompletedTask;
}
public bool Remove(ChannelScheduleItem? item)
{
if (item is null || !item.CanDelete)
@@ -547,7 +594,7 @@ public sealed class ChannelScheduleEngine
if (regionTargets.Count == 0)
{
_lastPlaybackItemId = queueItem.Id;
MarkLastPlaybackItem(queueItem);
MarkDataUnavailable(queueItem, "선택한 지역 조건에 송출 가능한 데이터가 없습니다.");
RefreshQueueMarkers();
return;
@@ -637,7 +684,7 @@ public sealed class ChannelScheduleEngine
queueItem.CurrentRegionLabel = string.Empty;
queueItem.ClearInternalNextPreview();
queueItem.ClearPlaybackCountdown();
_lastPlaybackItemId = queueItem.Id;
MarkLastPlaybackItem(queueItem);
if (playedAny)
{
queueItem.State = ScheduleQueueItemState.Queued;
@@ -737,7 +784,7 @@ public sealed class ChannelScheduleEngine
queueItem.CurrentRegionLabel = string.Empty;
queueItem.ClearInternalNextPreview();
queueItem.ClearPlaybackCountdown();
_lastPlaybackItemId = queueItem.Id;
MarkLastPlaybackItem(queueItem);
if (playedAny)
{
queueItem.State = ScheduleQueueItemState.Queued;
@@ -1770,6 +1817,36 @@ public sealed class ChannelScheduleEngine
return item.State is ScheduleQueueItemState.Queued or ScheduleQueueItemState.Next or ScheduleQueueItemState.Completed;
}
private void MarkLastPlaybackItem(ChannelScheduleItem queueItem)
{
if (_restartFromTopAfterItemId == queueItem.Id)
{
_lastPlaybackItemId = null;
_restartFromTopAfterItemId = null;
return;
}
_lastPlaybackItemId = queueItem.Id;
}
private void ClearStalePlaybackStatesBeforeStart()
{
foreach (var item in Queue.Where(item => item.State is ScheduleQueueItemState.OnAir or ScheduleQueueItemState.Sending))
{
if (_preparedCutFrame?.Item.Id == item.Id)
{
continue;
}
item.State = ScheduleQueueItemState.Queued;
item.LastError = string.Empty;
item.CurrentRegionLabel = string.Empty;
item.ClearRenderedPreview();
item.ClearInternalNextPreview();
item.ClearPlaybackCountdown();
}
}
private void ResetDataUnavailableItems()
{
foreach (var item in Queue.Where(item => item.State == ScheduleQueueItemState.DataUnavailable))