Browse Source

add search query collection type to block schedules (#2644)

* add search query collection type to block schedules

* fix history
pull/2645/head
Jason Dove 9 months ago committed by GitHub
parent
commit
b6d509b9cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 2
      ErsatzTV.Application/Scheduling/BlockItemViewModel.cs
  3. 2
      ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs
  4. 2
      ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItem.cs
  5. 9
      ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItemsHandler.cs
  6. 2
      ErsatzTV.Application/Scheduling/Mapper.cs
  7. 2
      ErsatzTV.Core/Domain/Scheduling/BlockItem.cs
  8. 5
      ErsatzTV.Core/Scheduling/CollectionKey.cs
  9. 4
      ErsatzTV.Core/Scheduling/HistoryDetails.cs
  10. 6876
      ErsatzTV.Infrastructure.MySql/Migrations/20251113162710_Add_BlockItemSearchQuery.Designer.cs
  11. 40
      ErsatzTV.Infrastructure.MySql/Migrations/20251113162710_Add_BlockItemSearchQuery.cs
  12. 6
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  13. 6703
      ErsatzTV.Infrastructure.Sqlite/Migrations/20251113162629_Add_BlockItemSearchQuery.Designer.cs
  14. 38
      ErsatzTV.Infrastructure.Sqlite/Migrations/20251113162629_Add_BlockItemSearchQuery.cs
  15. 6
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  16. 2
      ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs
  17. 25
      ErsatzTV/Pages/BlockEditor.razor
  18. 7
      ErsatzTV/ViewModels/BlockItemEditViewModel.cs

2
CHANGELOG.md

@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Add `ScaledResolution` template data (the final size of the frame before padding)
- Add `place_within_source_content` (true/false) field to image graphics element
- Add `name` field to all graphics elements to display in the UI
- Classic schedules: add collection type `Search Query`
- Classic and block schedules: add collection type `Search Query`
- This allows defining search queries directly on schedule items without creating smart collections beforehand
- As an example, this can be used to filter or combine existing smart collections
- Filter: `smart_collection:"sd movies" AND plot:"christmas"`

2
ErsatzTV.Application/Scheduling/BlockItemViewModel.cs

@ -14,6 +14,8 @@ public record BlockItemViewModel( @@ -14,6 +14,8 @@ public record BlockItemViewModel(
MultiCollectionViewModel MultiCollection,
SmartCollectionViewModel SmartCollection,
NamedMediaItemViewModel MediaItem,
string SearchTitle,
string SearchQuery,
PlaybackOrder PlaybackOrder,
bool IncludeInProgramGuide,
bool DisableWatermarks,

2
ErsatzTV.Application/Scheduling/Commands/PreviewBlockPlayoutHandler.cs

@ -136,6 +136,8 @@ public class PreviewBlockPlayoutHandler( @@ -136,6 +136,8 @@ public class PreviewBlockPlayoutHandler(
CollectionId = request.CollectionId,
MultiCollectionId = request.MultiCollectionId,
SmartCollectionId = request.SmartCollectionId,
SearchTitle = request.SearchTitle,
SearchQuery = request.SearchQuery,
MediaItemId = request.MediaItemId,
PlaybackOrder = request.PlaybackOrder
};

2
ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItem.cs

@ -9,6 +9,8 @@ public record ReplaceBlockItem( @@ -9,6 +9,8 @@ public record ReplaceBlockItem(
int? MultiCollectionId,
int? SmartCollectionId,
int? MediaItemId,
string SearchTitle,
string SearchQuery,
PlaybackOrder PlaybackOrder,
bool IncludeInProgramGuide,
bool DisableWatermarks,

9
ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItemsHandler.cs

@ -55,6 +55,8 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact @@ -55,6 +55,8 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact
MultiCollectionId = item.MultiCollectionId,
SmartCollectionId = item.SmartCollectionId,
MediaItemId = item.MediaItemId,
SearchTitle = item.SearchTitle,
SearchQuery = item.SearchQuery,
PlaybackOrder = item.PlaybackOrder,
IncludeInProgramGuide = item.IncludeInProgramGuide,
DisableWatermarks = item.DisableWatermarks,
@ -163,6 +165,13 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact @@ -163,6 +165,13 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact
return BaseError.New("[SmartCollection] is required for collection type 'SmartCollection'");
}
break;
case CollectionType.SearchQuery:
if (string.IsNullOrWhiteSpace(item.SearchQuery))
{
return BaseError.New("[SearchQuery] is required for collection type 'SearchQuery'");
}
break;
case CollectionType.FakeCollection:
default:

2
ErsatzTV.Application/Scheduling/Mapper.cs

@ -53,6 +53,8 @@ internal static class Mapper @@ -53,6 +53,8 @@ internal static class Mapper
Artist artist => MediaItems.Mapper.ProjectToViewModel(artist),
_ => null
},
blockItem.SearchTitle,
blockItem.SearchQuery,
blockItem.PlaybackOrder,
blockItem.IncludeInProgramGuide,
blockItem.DisableWatermarks,

2
ErsatzTV.Core/Domain/Scheduling/BlockItem.cs

@ -15,6 +15,8 @@ public class BlockItem @@ -15,6 +15,8 @@ public class BlockItem
public MultiCollection MultiCollection { get; set; }
public int? SmartCollectionId { get; set; }
public SmartCollection SmartCollection { get; set; }
public string SearchTitle { get; set; }
public string SearchQuery { get; set; }
public PlaybackOrder PlaybackOrder { get; set; }
public bool IncludeInProgramGuide { get; set; }
public bool DisableWatermarks { get; set; }

5
ErsatzTV.Core/Scheduling/CollectionKey.cs

@ -124,6 +124,11 @@ public class CollectionKey : Record<CollectionKey> @@ -124,6 +124,11 @@ public class CollectionKey : Record<CollectionKey>
CollectionType = item.CollectionType,
SmartCollectionId = item.SmartCollectionId
},
CollectionType.SearchQuery => new CollectionKey
{
CollectionType = item.CollectionType,
SearchQuery = item.SearchQuery
},
CollectionType.FakeCollection => new CollectionKey
{
CollectionType = item.CollectionType

4
ErsatzTV.Core/Scheduling/HistoryDetails.cs

@ -23,7 +23,8 @@ internal static class HistoryDetails @@ -23,7 +23,8 @@ internal static class HistoryDetails
blockItem.CollectionId,
blockItem.MultiCollectionId,
blockItem.SmartCollectionId,
blockItem.MediaItemId
blockItem.MediaItemId,
blockItem.SearchQuery
};
return JsonConvert.SerializeObject(key, Formatting.None, JsonSettings);
@ -96,6 +97,7 @@ internal static class HistoryDetails @@ -96,6 +97,7 @@ internal static class HistoryDetails
collectionKey.SmartCollectionId,
collectionKey.MediaItemId,
collectionKey.PlaylistId,
collectionKey.SearchQuery,
collectionKey.FakeCollectionKey
};

6876
ErsatzTV.Infrastructure.MySql/Migrations/20251113162710_Add_BlockItemSearchQuery.Designer.cs generated

File diff suppressed because it is too large Load Diff

40
ErsatzTV.Infrastructure.MySql/Migrations/20251113162710_Add_BlockItemSearchQuery.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_BlockItemSearchQuery : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SearchQuery",
table: "BlockItem",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "SearchTitle",
table: "BlockItem",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SearchQuery",
table: "BlockItem");
migrationBuilder.DropColumn(
name: "SearchTitle",
table: "BlockItem");
}
}
}

6
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -2658,6 +2658,12 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -2658,6 +2658,12 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("PlaybackOrder")
.HasColumnType("int");
b.Property<string>("SearchQuery")
.HasColumnType("longtext");
b.Property<string>("SearchTitle")
.HasColumnType("longtext");
b.Property<int?>("SmartCollectionId")
.HasColumnType("int");

6703
ErsatzTV.Infrastructure.Sqlite/Migrations/20251113162629_Add_BlockItemSearchQuery.Designer.cs generated

File diff suppressed because it is too large Load Diff

38
ErsatzTV.Infrastructure.Sqlite/Migrations/20251113162629_Add_BlockItemSearchQuery.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_BlockItemSearchQuery : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SearchQuery",
table: "BlockItem",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "SearchTitle",
table: "BlockItem",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SearchQuery",
table: "BlockItem");
migrationBuilder.DropColumn(
name: "SearchTitle",
table: "BlockItem");
}
}
}

6
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -2537,6 +2537,12 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -2537,6 +2537,12 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("PlaybackOrder")
.HasColumnType("INTEGER");
b.Property<string>("SearchQuery")
.HasColumnType("TEXT");
b.Property<string>("SearchTitle")
.HasColumnType("TEXT");
b.Property<int?>("SmartCollectionId")
.HasColumnType("INTEGER");

2
ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs

@ -226,6 +226,8 @@ public sealed class LuceneSearchIndex : ISearchIndex @@ -226,6 +226,8 @@ public sealed class LuceneSearchIndex : ISearchIndex
client?.Breadcrumbs?.Leave("SearchIndex.Search", BreadcrumbType.State, metadata);
query ??= string.Empty;
if (string.IsNullOrWhiteSpace(query.Replace("*", string.Empty).Replace("?", string.Empty)) ||
_writer.MaxDoc == 0)
{

25
ErsatzTV/Pages/BlockEditor.razor

@ -160,6 +160,7 @@ @@ -160,6 +160,7 @@
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.Artist">Artist</MudSelectItem> *@
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem> *@
<MudSelectItem Value="CollectionType.SmartCollection">Smart Collection</MudSelectItem>
<MudSelectItem Value="CollectionType.SearchQuery">Search Query</MudSelectItem>
</MudSelect>
</MudStack>
@if (_selectedItem.CollectionType == CollectionType.Collection)
@ -216,6 +217,24 @@ @@ -216,6 +217,24 @@
</MudStack>
}
@if (_selectedItem.CollectionType == CollectionType.SearchQuery)
{
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Search Title</MudText>
</div>
<MudTextField @bind-Value="@_selectedItem.SearchTitle"
For="@(() => _selectedItem.SearchTitle)"
HelperText="Optional title that will be displayed in the UI"/>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Search Query</MudText>
</div>
<MudTextField @bind-Value="@_selectedItem.SearchQuery" For="@(() => _selectedItem.SearchQuery)"/>
</MudStack>
}
@if (_selectedItem.CollectionType == CollectionType.TelevisionShow)
{
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
@ -489,6 +508,8 @@ else if (_previewItems != null) @@ -489,6 +508,8 @@ else if (_previewItems != null)
MultiCollection = item.MultiCollection,
SmartCollection = item.SmartCollection,
MediaItem = item.MediaItem,
SearchTitle = item.SearchTitle,
SearchQuery = item.SearchQuery,
PlaybackOrder = item.PlaybackOrder,
IncludeInProgramGuide = item.IncludeInProgramGuide,
DisableWatermarks = item.DisableWatermarks,
@ -522,6 +543,8 @@ else if (_previewItems != null) @@ -522,6 +543,8 @@ else if (_previewItems != null)
MultiCollection = item.MultiCollection,
SmartCollection = item.SmartCollection,
MediaItem = item.MediaItem,
SearchTitle = item.SearchTitle,
SearchQuery = item.SearchQuery,
IncludeInProgramGuide = item.IncludeInProgramGuide,
DisableWatermarks = item.DisableWatermarks,
Watermarks = item.Watermarks.ToList(),
@ -581,6 +604,8 @@ else if (_previewItems != null) @@ -581,6 +604,8 @@ else if (_previewItems != null)
item.MultiCollection?.Id,
item.SmartCollection?.Id,
item.MediaItem?.MediaItemId,
item.SearchTitle,
item.SearchQuery,
item.PlaybackOrder,
item.IncludeInProgramGuide,
item.DisableWatermarks,

7
ErsatzTV/ViewModels/BlockItemEditViewModel.cs

@ -28,11 +28,15 @@ public class BlockItemEditViewModel : INotifyPropertyChanged @@ -28,11 +28,15 @@ public class BlockItemEditViewModel : INotifyPropertyChanged
MultiCollection = null;
MediaItem = null;
SmartCollection = null;
SearchTitle = null;
SearchQuery = null;
OnPropertyChanged(nameof(Collection));
OnPropertyChanged(nameof(MultiCollection));
OnPropertyChanged(nameof(MediaItem));
OnPropertyChanged(nameof(SmartCollection));
OnPropertyChanged(nameof(SearchTitle));
OnPropertyChanged(nameof(SearchQuery));
}
if (_collectionType == CollectionType.MultiCollection)
@ -46,6 +50,8 @@ public class BlockItemEditViewModel : INotifyPropertyChanged @@ -46,6 +50,8 @@ public class BlockItemEditViewModel : INotifyPropertyChanged
public MultiCollectionViewModel MultiCollection { get; set; }
public SmartCollectionViewModel SmartCollection { get; set; }
public NamedMediaItemViewModel MediaItem { get; set; }
public string SearchTitle { get; set; }
public string SearchQuery { get; set; }
public string CollectionName => CollectionType switch
{
@ -55,6 +61,7 @@ public class BlockItemEditViewModel : INotifyPropertyChanged @@ -55,6 +61,7 @@ public class BlockItemEditViewModel : INotifyPropertyChanged
CollectionType.Artist => MediaItem?.Name,
CollectionType.MultiCollection => MultiCollection?.Name,
CollectionType.SmartCollection => SmartCollection?.Name,
CollectionType.SearchQuery => string.IsNullOrWhiteSpace(SearchTitle) ? SearchQuery : SearchTitle,
_ => string.Empty
};

Loading…
Cancel
Save