diff --git a/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml b/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml
index 42b916d..d2093fc 100644
--- a/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml
+++ b/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml
@@ -205,6 +205,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml.cs b/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml.cs
index 5bdc571..2c423e6 100644
--- a/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml.cs
+++ b/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml.cs
@@ -64,6 +64,18 @@ public sealed partial class ChannelSchedulePanel : UserControl
command.Execute(item);
}
+ private void RetryDataIssueButton_Click(object sender, RoutedEventArgs e)
+ {
+ var item = GetItem(sender);
+ var command = ViewModel?.RetryDataIssueCommand;
+ if (item is null || command is null || !command.CanExecute(item))
+ {
+ return;
+ }
+
+ command.Execute(item);
+ }
+
private void IncreaseDurationButton_Click(object sender, RoutedEventArgs e)
{
GetItem(sender)?.StepDraftDuration(1d);
diff --git a/Tornado3_2026Election/Domain/ChannelScheduleItem.cs b/Tornado3_2026Election/Domain/ChannelScheduleItem.cs
index 03b96e2..7e3ad3c 100644
--- a/Tornado3_2026Election/Domain/ChannelScheduleItem.cs
+++ b/Tornado3_2026Election/Domain/ChannelScheduleItem.cs
@@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Text.Json.Serialization;
using Microsoft.UI;
+using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Tornado3_2026Election.Common;
using Tornado3_2026Election.Services;
@@ -12,6 +13,7 @@ public sealed class ChannelScheduleItem : ObservableObject
{
private ScheduleQueueItemState _state = ScheduleQueueItemState.Queued;
private string _lastError = string.Empty;
+ private DateTimeOffset? _lastIssueAt;
private DateTimeOffset? _lastPlayedAt;
private string _currentRegionLabel = string.Empty;
private double _defaultCutDurationSeconds;
@@ -103,6 +105,7 @@ public sealed class ChannelScheduleItem : ObservableObject
OnPropertyChanged(nameof(StateBadgeBackgroundBrush));
OnPropertyChanged(nameof(CardOpacity));
OnPropertyChanged(nameof(CanDelete));
+ OnIssueStateChanged();
}
}
}
@@ -110,7 +113,26 @@ public sealed class ChannelScheduleItem : ObservableObject
public string LastError
{
get => _lastError;
- set => SetProperty(ref _lastError, value);
+ set
+ {
+ if (SetProperty(ref _lastError, value))
+ {
+ LastIssueAt = string.IsNullOrWhiteSpace(value) ? null : DateTimeOffset.Now;
+ OnIssueStateChanged();
+ }
+ }
+ }
+
+ public DateTimeOffset? LastIssueAt
+ {
+ get => _lastIssueAt;
+ private set
+ {
+ if (SetProperty(ref _lastIssueAt, value))
+ {
+ OnPropertyChanged(nameof(LastIssueLabel));
+ }
+ }
}
public DateTimeOffset? LastPlayedAt
@@ -139,6 +161,7 @@ public sealed class ChannelScheduleItem : ObservableObject
ScheduleQueueItemState.Sending => "준비",
ScheduleQueueItemState.OnAir => "송출 중",
ScheduleQueueItemState.Completed => "대기",
+ ScheduleQueueItemState.DataUnavailable => "데이터 없음",
ScheduleQueueItemState.Error => "오류",
_ => "대기"
};
@@ -149,6 +172,7 @@ public sealed class ChannelScheduleItem : ObservableObject
ScheduleQueueItemState.Next => ColorHelper.FromArgb(255, 255, 184, 28),
ScheduleQueueItemState.Sending => ColorHelper.FromArgb(255, 255, 132, 38),
ScheduleQueueItemState.OnAir => ColorHelper.FromArgb(255, 239, 68, 68),
+ ScheduleQueueItemState.DataUnavailable => ColorHelper.FromArgb(255, 255, 184, 28),
ScheduleQueueItemState.Error => ColorHelper.FromArgb(255, 251, 113, 133),
_ => ColorHelper.FromArgb(255, 100, 116, 139)
});
@@ -159,6 +183,7 @@ public sealed class ChannelScheduleItem : ObservableObject
ScheduleQueueItemState.Next => ColorHelper.FromArgb(255, 72, 38, 10),
ScheduleQueueItemState.Sending => ColorHelper.FromArgb(255, 64, 42, 16),
ScheduleQueueItemState.OnAir => ColorHelper.FromArgb(255, 58, 22, 24),
+ ScheduleQueueItemState.DataUnavailable => ColorHelper.FromArgb(255, 63, 40, 16),
ScheduleQueueItemState.Error => ColorHelper.FromArgb(255, 54, 18, 31),
_ => ColorHelper.FromArgb(255, 18, 32, 51)
});
@@ -169,6 +194,7 @@ public sealed class ChannelScheduleItem : ObservableObject
ScheduleQueueItemState.Next => ColorHelper.FromArgb(255, 255, 184, 28),
ScheduleQueueItemState.Sending => ColorHelper.FromArgb(255, 255, 132, 38),
ScheduleQueueItemState.OnAir => ColorHelper.FromArgb(255, 255, 90, 84),
+ ScheduleQueueItemState.DataUnavailable => ColorHelper.FromArgb(255, 255, 184, 28),
ScheduleQueueItemState.Error => ColorHelper.FromArgb(255, 251, 113, 133),
_ => ColorHelper.FromArgb(255, 39, 64, 95)
});
@@ -179,6 +205,7 @@ public sealed class ChannelScheduleItem : ObservableObject
ScheduleQueueItemState.Next => ColorHelper.FromArgb(255, 194, 65, 12),
ScheduleQueueItemState.Sending => ColorHelper.FromArgb(255, 180, 83, 9),
ScheduleQueueItemState.OnAir => ColorHelper.FromArgb(255, 220, 38, 38),
+ ScheduleQueueItemState.DataUnavailable => ColorHelper.FromArgb(255, 180, 83, 9),
ScheduleQueueItemState.Error => ColorHelper.FromArgb(255, 190, 18, 60),
_ => ColorHelper.FromArgb(255, 26, 46, 71)
});
@@ -189,6 +216,49 @@ public sealed class ChannelScheduleItem : ObservableObject
[JsonIgnore]
public bool CanDelete => State is not ScheduleQueueItemState.OnAir and not ScheduleQueueItemState.Sending;
+ [JsonIgnore]
+ public bool HasIssue => State is ScheduleQueueItemState.DataUnavailable or ScheduleQueueItemState.Error;
+
+ [JsonIgnore]
+ public Visibility IssueVisibility => HasIssue ? Visibility.Visible : Visibility.Collapsed;
+
+ [JsonIgnore]
+ public Visibility DataUnavailableActionVisibility =>
+ State == ScheduleQueueItemState.DataUnavailable ? Visibility.Visible : Visibility.Collapsed;
+
+ [JsonIgnore]
+ public string IssueTitle => State switch
+ {
+ ScheduleQueueItemState.DataUnavailable => "데이터 없음 - 자동 건너뜀",
+ ScheduleQueueItemState.Error => "송출 오류",
+ _ => string.Empty
+ };
+
+ [JsonIgnore]
+ public string IssueDetail => string.IsNullOrWhiteSpace(LastError)
+ ? State == ScheduleQueueItemState.DataUnavailable
+ ? "이전 실행에서 조건에 맞는 데이터가 없어 보류되었습니다. 다시 시작하면 재확인합니다."
+ : string.Empty
+ : LastError;
+
+ [JsonIgnore]
+ public string IssueOperatorHint => State == ScheduleQueueItemState.DataUnavailable
+ ? "데이터가 들어온 뒤 다시 확인을 누르면 스케줄 대상에 복귀합니다."
+ : "CG 연결과 컷 파일, 데이터 수신 상태를 확인해 주세요.";
+
+ [JsonIgnore]
+ public string LastIssueLabel => LastIssueAt?.ToString("HH:mm:ss") ?? string.Empty;
+
+ [JsonIgnore]
+ public SolidColorBrush IssueBackgroundBrush => new(State == ScheduleQueueItemState.DataUnavailable
+ ? ColorHelper.FromArgb(255, 69, 43, 14)
+ : ColorHelper.FromArgb(255, 60, 18, 31));
+
+ [JsonIgnore]
+ public SolidColorBrush IssueBorderBrush => new(State == ScheduleQueueItemState.DataUnavailable
+ ? ColorHelper.FromArgb(255, 255, 184, 28)
+ : ColorHelper.FromArgb(255, 251, 113, 133));
+
[JsonIgnore]
public double MinimumDurationSeconds => ScheduleTemplatePolicy.GetMinimumCutDurationSeconds(Channel, FormatName);
@@ -356,6 +426,18 @@ public sealed class ChannelScheduleItem : ObservableObject
OnPropertyChanged(nameof(DurationApplyStatusLabel));
}
+ private void OnIssueStateChanged()
+ {
+ OnPropertyChanged(nameof(HasIssue));
+ OnPropertyChanged(nameof(IssueVisibility));
+ OnPropertyChanged(nameof(DataUnavailableActionVisibility));
+ OnPropertyChanged(nameof(IssueTitle));
+ OnPropertyChanged(nameof(IssueDetail));
+ OnPropertyChanged(nameof(IssueOperatorHint));
+ OnPropertyChanged(nameof(IssueBackgroundBrush));
+ OnPropertyChanged(nameof(IssueBorderBrush));
+ }
+
private void OnPreviewChanged()
{
OnPropertyChanged(nameof(PreviewSource));
diff --git a/Tornado3_2026Election/Domain/ScheduleQueueItemState.cs b/Tornado3_2026Election/Domain/ScheduleQueueItemState.cs
index f6f9eaf..faff807 100644
--- a/Tornado3_2026Election/Domain/ScheduleQueueItemState.cs
+++ b/Tornado3_2026Election/Domain/ScheduleQueueItemState.cs
@@ -7,5 +7,6 @@ public enum ScheduleQueueItemState
Sending,
OnAir,
Completed,
- Error
+ Error,
+ DataUnavailable
}
diff --git a/Tornado3_2026Election/Services/ChannelScheduleEngine.cs b/Tornado3_2026Election/Services/ChannelScheduleEngine.cs
index c9ce315..67b5dda 100644
--- a/Tornado3_2026Election/Services/ChannelScheduleEngine.cs
+++ b/Tornado3_2026Election/Services/ChannelScheduleEngine.cs
@@ -87,6 +87,7 @@ public sealed class ChannelScheduleEngine
}
_lastPlaybackItemId = null;
+ ResetDataUnavailableItems();
_playbackCts = new CancellationTokenSource();
IsRunning = true;
RefreshQueueMarkers();
@@ -150,6 +151,7 @@ public sealed class ChannelScheduleEngine
try
{
_lastPlaybackItemId = null;
+ ResetDataUnavailableItems();
ClearPreparedFrame(resetState: true);
RefreshQueueMarkers();
@@ -267,6 +269,23 @@ public sealed class ChannelScheduleEngine
RefreshQueueMarkers();
}
+ public bool RetryDataUnavailable(ChannelScheduleItem? item)
+ {
+ if (item is null || item.State != ScheduleQueueItemState.DataUnavailable)
+ {
+ return false;
+ }
+
+ item.State = ScheduleQueueItemState.Queued;
+ item.LastError = string.Empty;
+ item.CurrentRegionLabel = string.Empty;
+ item.ClearRenderedPreview();
+ item.ClearInternalNextPreview();
+ RefreshQueueMarkers();
+ QueueChanged?.Invoke(this, EventArgs.Empty);
+ return true;
+ }
+
public async Task ForceNextAsync()
{
await AdvanceToNextAsync().ConfigureAwait(false);
@@ -478,9 +497,9 @@ public sealed class ChannelScheduleEngine
if (previewFrame is null)
{
- queueItem.State = ScheduleQueueItemState.Error;
- queueItem.LastError = "송출 가능한 지역 데이터가 없습니다.";
- queueItem.CurrentRegionLabel = string.Empty;
+ MarkDataUnavailable(
+ queueItem,
+ "현재 선택한 컷과 지역 조건에 맞는 데이터가 없어 준비할 수 없습니다.");
RefreshQueueMarkers();
_logService.Warning($"[{Channel}] 준비할 수 있는 컷 데이터가 없습니다: {queueItem.DisplayName}");
return;
@@ -520,16 +539,15 @@ public sealed class ChannelScheduleEngine
if (regionTargets.Count == 0)
{
- queueItem.State = ScheduleQueueItemState.Error;
- queueItem.LastError = "송출 가능한 지역 데이터가 없습니다.";
- queueItem.CurrentRegionLabel = string.Empty;
_lastPlaybackItemId = queueItem.Id;
+ MarkDataUnavailable(queueItem, "선택한 지역 조건에 송출 가능한 데이터가 없습니다.");
RefreshQueueMarkers();
return;
}
var playedAny = false;
var lastFailure = string.Empty;
+ var dataUnavailableFailure = false;
if (ShouldUseAggregateScheduleSnapshot(template))
{
@@ -559,6 +577,7 @@ public sealed class ChannelScheduleEngine
if (!_dataRefreshGate.ValidateSnapshotForFormat(template, aggregateSnapshot, out var aggregateValidationError))
{
lastFailure = $"{ResolveAggregateRegionGroupLabel(queueItem, aggregateRegionGroup)}: {aggregateValidationError}";
+ dataUnavailableFailure = true;
_logService.Warning($"[{Channel}] 집계형 송출 데이터 검증 실패: {lastFailure}");
continue;
}
@@ -610,8 +629,21 @@ public sealed class ChannelScheduleEngine
queueItem.CurrentRegionLabel = string.Empty;
queueItem.ClearInternalNextPreview();
_lastPlaybackItemId = queueItem.Id;
- queueItem.State = playedAny ? ScheduleQueueItemState.Queued : ScheduleQueueItemState.Error;
- queueItem.LastError = playedAny ? string.Empty : (string.IsNullOrWhiteSpace(lastFailure) ? "송출 가능한 지역 데이터가 없습니다." : lastFailure);
+ if (playedAny)
+ {
+ queueItem.State = ScheduleQueueItemState.Queued;
+ queueItem.LastError = string.Empty;
+ }
+ else if (dataUnavailableFailure)
+ {
+ MarkDataUnavailable(queueItem, string.IsNullOrWhiteSpace(lastFailure) ? "송출 가능한 지역 데이터가 없습니다." : lastFailure);
+ }
+ else
+ {
+ queueItem.State = ScheduleQueueItemState.Error;
+ queueItem.LastError = string.IsNullOrWhiteSpace(lastFailure) ? "송출 가능한 지역 데이터가 없습니다." : lastFailure;
+ }
+
ClearSkipCurrentItem(queueItem);
RefreshQueueMarkers();
return;
@@ -642,6 +674,7 @@ public sealed class ChannelScheduleEngine
if (!_dataRefreshGate.ValidateSnapshotForFormat(template, snapshot, out var validationError))
{
lastFailure = $"{regionTarget.DisplayName}: {validationError}";
+ dataUnavailableFailure = true;
_logService.Warning($"[{Channel}] 스케줄 지역 검증 실패: {lastFailure}");
continue;
}
@@ -695,8 +728,21 @@ public sealed class ChannelScheduleEngine
queueItem.CurrentRegionLabel = string.Empty;
queueItem.ClearInternalNextPreview();
_lastPlaybackItemId = queueItem.Id;
- queueItem.State = playedAny ? ScheduleQueueItemState.Queued : ScheduleQueueItemState.Error;
- queueItem.LastError = playedAny ? string.Empty : (string.IsNullOrWhiteSpace(lastFailure) ? "송출 가능한 지역 데이터가 없습니다." : lastFailure);
+ if (playedAny)
+ {
+ queueItem.State = ScheduleQueueItemState.Queued;
+ queueItem.LastError = string.Empty;
+ }
+ else if (dataUnavailableFailure)
+ {
+ MarkDataUnavailable(queueItem, string.IsNullOrWhiteSpace(lastFailure) ? "송출 가능한 지역 데이터가 없습니다." : lastFailure);
+ }
+ else
+ {
+ queueItem.State = ScheduleQueueItemState.Error;
+ queueItem.LastError = string.IsNullOrWhiteSpace(lastFailure) ? "송출 가능한 지역 데이터가 없습니다." : lastFailure;
+ }
+
ClearSkipCurrentItem(queueItem);
RefreshQueueMarkers();
}
@@ -1624,7 +1670,7 @@ public sealed class ChannelScheduleEngine
foreach (var item in Queue)
{
- if (item == activeItem || item.State == ScheduleQueueItemState.Error)
+ if (item == activeItem || item.State is ScheduleQueueItemState.Error or ScheduleQueueItemState.DataUnavailable)
{
continue;
}
@@ -1677,6 +1723,34 @@ public sealed class ChannelScheduleEngine
return item.State is ScheduleQueueItemState.Queued or ScheduleQueueItemState.Next or ScheduleQueueItemState.Completed;
}
+ private void ResetDataUnavailableItems()
+ {
+ foreach (var item in Queue.Where(item => item.State == ScheduleQueueItemState.DataUnavailable))
+ {
+ item.State = ScheduleQueueItemState.Queued;
+ item.LastError = string.Empty;
+ item.CurrentRegionLabel = string.Empty;
+ item.ClearRenderedPreview();
+ item.ClearInternalNextPreview();
+ }
+ }
+
+ private void MarkDataUnavailable(ChannelScheduleItem queueItem, string reason)
+ {
+ queueItem.LastError = NormalizeDataUnavailableReason(reason);
+ queueItem.State = ScheduleQueueItemState.DataUnavailable;
+ queueItem.CurrentRegionLabel = string.Empty;
+ queueItem.ClearRenderedPreview();
+ queueItem.ClearInternalNextPreview();
+ }
+
+ private static string NormalizeDataUnavailableReason(string reason)
+ {
+ return string.IsNullOrWhiteSpace(reason)
+ ? "현재 조건에 맞는 데이터가 없어 송출을 건너뜁니다."
+ : reason;
+ }
+
private sealed record PreparedCutFrame(
ChannelScheduleItem Item,
FormatTemplateDefinition Template,
diff --git a/Tornado3_2026Election/ViewModels/ChannelScheduleViewModel.cs b/Tornado3_2026Election/ViewModels/ChannelScheduleViewModel.cs
index 7c16d0b..9e11db4 100644
--- a/Tornado3_2026Election/ViewModels/ChannelScheduleViewModel.cs
+++ b/Tornado3_2026Election/ViewModels/ChannelScheduleViewModel.cs
@@ -22,6 +22,8 @@ public sealed class ChannelScheduleViewModel : ObservableObject
private static readonly Regex PeopleSlotCountPattern = new(@"(\d+)인", RegexOptions.Compiled);
private static readonly Brush PlaybackActiveIconBrush = new SolidColorBrush(ColorHelper.FromArgb(255, 255, 90, 84));
private static readonly Brush PlaybackIdleIconBrush = new SolidColorBrush(ColorHelper.FromArgb(255, 183, 197, 216));
+ private static readonly Brush DataIssueBackgroundBrushValue = new SolidColorBrush(ColorHelper.FromArgb(255, 57, 38, 16));
+ private static readonly Brush DataIssueBorderBrushValue = new SolidColorBrush(ColorHelper.FromArgb(255, 255, 184, 28));
private readonly ChannelScheduleEngine _engine;
private readonly ITornado3Adapter _adapter;
private readonly CutDebugStateStore _cutDebugStateStore;
@@ -94,6 +96,7 @@ public sealed class ChannelScheduleViewModel : ObservableObject
ForceQueueNextCommand = new AsyncRelayCommand(ForceQueueNextAsync);
AddFormatCommand = new RelayCommand(AddFormat, CanAddFormat);
ResetQueueCommand = new RelayCommand(ResetQueue);
+ RetryDataIssueCommand = new RelayCommand(RetryDataIssue, CanRetryDataIssue);
RemoveItemCommand = new RelayCommand(RemoveItem);
MoveUpCommand = new RelayCommand(MoveUp);
MoveDownCommand = new RelayCommand(MoveDown);
@@ -168,6 +171,8 @@ public sealed class ChannelScheduleViewModel : ObservableObject
public RelayCommand ResetQueueCommand { get; }
+ public RelayCommand RetryDataIssueCommand { get; }
+
public RelayCommand RemoveItemCommand { get; }
public RelayCommand MoveUpCommand { get; }
@@ -397,6 +402,27 @@ public sealed class ChannelScheduleViewModel : ObservableObject
public string OperatorQuickSummary => $"{AdapterStateLabel} / {LoopSummary} / 빈 스케줄 {EmptyBehaviorLabel}";
+ public Visibility ScheduleDataIssueVisibility =>
+ LatestScheduleDataIssueItem is null ? Visibility.Collapsed : Visibility.Visible;
+
+ public Brush ScheduleDataIssueBackgroundBrush => DataIssueBackgroundBrushValue;
+
+ public Brush ScheduleDataIssueBorderBrush => DataIssueBorderBrushValue;
+
+ public string ScheduleDataIssueTitle => LatestScheduleDataIssueItem is { } item
+ ? $"{item.FormatName} 송출 보류"
+ : string.Empty;
+
+ public string ScheduleDataIssueMessage => LatestScheduleDataIssueItem?.IssueDetail ?? string.Empty;
+
+ public string ScheduleDataIssueDetail => LatestScheduleDataIssueItem is { } item
+ ? $"{item.DisplayRegionLabel} / {item.LastIssueLabel}"
+ : string.Empty;
+
+ public string ScheduleDataIssueHint => LatestScheduleDataIssueItem is null
+ ? string.Empty
+ : "시스템 오류가 아니라 현재 조건 데이터가 부족한 상태입니다. 데이터가 들어오면 항목의 다시 확인을 눌러 스케줄 대상에 복귀시킬 수 있습니다.";
+
public string SelectedFormatName => SelectedFormat?.Name ?? "컷을 선택하세요";
public string SelectedFormatDescription => SelectedFormat?.Description ?? "선택한 컷의 썸네일이 여기에 표시됩니다.";
@@ -485,6 +511,12 @@ public sealed class ChannelScheduleViewModel : ObservableObject
private ChannelScheduleItem? NextPlaybackItem =>
InternalNextPlaybackItem ?? QueueNextPlaybackItem;
+ private ChannelScheduleItem? LatestScheduleDataIssueItem =>
+ Queue
+ .Where(item => item.State == ScheduleQueueItemState.DataUnavailable)
+ .OrderByDescending(item => item.LastIssueAt ?? DateTimeOffset.MinValue)
+ .FirstOrDefault();
+
public async Task RefreshRegionOptionsAsync()
{
await RebuildRegionOptionsAsync();
@@ -749,6 +781,22 @@ public sealed class ChannelScheduleViewModel : ObservableObject
_logService.Info($"[{Title}] 스케줄을 맨 위 컷부터 다시 시작하도록 되돌렸습니다.");
}
+ private void RetryDataIssue(ChannelScheduleItem? item)
+ {
+ if (!_engine.RetryDataUnavailable(item))
+ {
+ return;
+ }
+
+ RefreshSummary();
+ _logService.Info($"[{Title}] 데이터 없음 항목을 다시 스케줄 대상으로 전환: {item?.DisplayName}");
+ }
+
+ private static bool CanRetryDataIssue(ChannelScheduleItem? item)
+ {
+ return item?.State == ScheduleQueueItemState.DataUnavailable;
+ }
+
private void RemoveItem(ChannelScheduleItem? item)
{
if (!_engine.Remove(item))
@@ -851,6 +899,13 @@ public sealed class ChannelScheduleViewModel : ObservableObject
nameof(QueuedItemCount),
nameof(QueueFootnote),
nameof(QueueSummary),
+ nameof(ScheduleDataIssueVisibility),
+ nameof(ScheduleDataIssueBackgroundBrush),
+ nameof(ScheduleDataIssueBorderBrush),
+ nameof(ScheduleDataIssueTitle),
+ nameof(ScheduleDataIssueMessage),
+ nameof(ScheduleDataIssueDetail),
+ nameof(ScheduleDataIssueHint),
nameof(IsCgConnected),
nameof(CgStatusSummary),
nameof(LoopSummary),
@@ -1098,15 +1153,20 @@ public sealed class ChannelScheduleViewModel : ObservableObject
or nameof(ChannelScheduleItem.InternalNextPreviewStatusLabel)
or nameof(ChannelScheduleItem.InternalNextPreviewDisplayName)
or nameof(ChannelScheduleItem.HasInternalNextPreview)
+ or nameof(ChannelScheduleItem.LastError)
+ or nameof(ChannelScheduleItem.LastIssueAt)
or nameof(ChannelScheduleItem.ThumbnailSource))
{
if (e.PropertyName is nameof(ChannelScheduleItem.State)
or nameof(ChannelScheduleItem.DisplayName)
- or nameof(ChannelScheduleItem.CurrentRegionLabel))
+ or nameof(ChannelScheduleItem.CurrentRegionLabel)
+ or nameof(ChannelScheduleItem.LastError)
+ or nameof(ChannelScheduleItem.LastIssueAt))
{
NotifyPlaybackStateChanged();
}
+ RetryDataIssueCommand.NotifyCanExecuteChanged();
NotifyPlaybackPreviewChanged();
}
}
@@ -1121,7 +1181,14 @@ public sealed class ChannelScheduleViewModel : ObservableObject
nameof(NextItemName),
nameof(QueuedItemCount),
nameof(QueueFootnote),
- nameof(QueueSummary));
+ nameof(QueueSummary),
+ nameof(ScheduleDataIssueVisibility),
+ nameof(ScheduleDataIssueBackgroundBrush),
+ nameof(ScheduleDataIssueBorderBrush),
+ nameof(ScheduleDataIssueTitle),
+ nameof(ScheduleDataIssueMessage),
+ nameof(ScheduleDataIssueDetail),
+ nameof(ScheduleDataIssueHint));
}
private void NotifyPlaybackPreviewChanged()