From 51449d2139e0a776703b77c386f912dde699cc8d Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:56:05 -0500 Subject: [PATCH] fix smart_collection search crashes --- CHANGELOG.md | 1 + .../Scheduling/ChronologicalMediaCollectionEnumerator.cs | 9 +++++++-- ErsatzTV.Infrastructure/Search/SearchQueryParser.cs | 9 ++++++++- ErsatzTV/Pages/Playouts.razor | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 072c2379e..b3cddaf0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix remote stream scripts (parsing issue with spaces and quotes) - Fix block history being removed when it is still needed for mirror channel - This caused playout build errors like "Unable to locate history for playout item" +- Fix crashes due to invalid smart collection searches, e.g. `smart_collection:"this collection does not exist"` ## [25.8.0] - 2025-10-26 ### Added diff --git a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs index 2343185fb..b7931b1b1 100644 --- a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs @@ -45,8 +45,13 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu public Option Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None; public Option CurrentIncludeInProgramGuide { get; } - public void MoveNext(Option scheduledAt) => - State.Index = (State.Index + 1) % _sortedMediaItems.Count; + public void MoveNext(Option scheduledAt) + { + if (_sortedMediaItems.Count > 0) + { + State.Index = (State.Index + 1) % _sortedMediaItems.Count; + } + } public Option MinimumDuration => _lazyMinimumDuration.Value; diff --git a/ErsatzTV.Infrastructure/Search/SearchQueryParser.cs b/ErsatzTV.Infrastructure/Search/SearchQueryParser.cs index 6ad9f98b1..d9c9730c6 100644 --- a/ErsatzTV.Infrastructure/Search/SearchQueryParser.cs +++ b/ErsatzTV.Infrastructure/Search/SearchQueryParser.cs @@ -77,7 +77,8 @@ public partial class SearchQueryParser(ISmartCollectionCache smartCollectionCach if (parsedQuery == replaceResult.Query) { logger.LogWarning( - "Failed to replace smart_collection in query; is the syntax correct? Quotes are required. Giving up..."); + "Failed to replace smart_collection in query; is the syntax correct? Quotes are required. Giving up on collection {Name}...", + smartCollectionName); break; } @@ -125,6 +126,12 @@ public partial class SearchQueryParser(ISmartCollectionCache smartCollectionCach { result = result.Replace(match.Value, $"({smartCollectionQuery})"); } + + if (maybeQuery.IsNone) + { + //logger.LogError("Cannot find nested smart collection {Name}; removing from query.", smartCollectionName); + result = result.Replace(match.Value, "(type:bad_query)"); + } } return new ReplaceResult(result, false); diff --git a/ErsatzTV/Pages/Playouts.razor b/ErsatzTV/Pages/Playouts.razor index bbd836e80..33ceffd06 100644 --- a/ErsatzTV/Pages/Playouts.razor +++ b/ErsatzTV/Pages/Playouts.razor @@ -366,7 +366,7 @@ ? playout.PlayoutId : null; - _buildMessage = !playout.BuildStatus.Success && !string.IsNullOrWhiteSpace(playout.BuildStatus.Message) + _buildMessage = !(playout.BuildStatus?.Success ?? false) && !string.IsNullOrWhiteSpace(playout.BuildStatus?.Message) ? playout.BuildStatus.Message : string.Empty;