Browse Source

more search fields and highlighting (#2440)

pull/2441/head
Jason Dove 11 months ago committed by GitHub
parent
commit
a389c1bbbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 4
      ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs
  3. 4
      ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs
  4. 11
      ErsatzTV.Application/Playouts/Mapper.cs
  5. 3
      ErsatzTV.Application/Playouts/PagedPlayoutsViewModel.cs
  6. 3
      ErsatzTV.Application/Playouts/Queries/GetPagedPlayouts.cs
  7. 40
      ErsatzTV.Application/Playouts/Queries/GetPagedPlayoutsHandler.cs
  8. 1
      ErsatzTV.Core/Domain/Channel.cs
  9. 6723
      ErsatzTV.Infrastructure.MySql/Migrations/20250918112100_Add_ChannelSortNumber.Designer.cs
  10. 34
      ErsatzTV.Infrastructure.MySql/Migrations/20250918112100_Add_ChannelSortNumber.cs
  11. 3
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  12. 6552
      ErsatzTV.Infrastructure.Sqlite/Migrations/20250918112240_Add_ChannelSortNumber.Designer.cs
  13. 34
      ErsatzTV.Infrastructure.Sqlite/Migrations/20250918112240_Add_ChannelSortNumber.cs
  14. 3
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  15. 42
      ErsatzTV/Pages/Playouts.razor
  16. 5
      ErsatzTV/Pages/Schedules.razor
  17. 2
      ErsatzTV/wwwroot/css/site.css

3
CHANGELOG.md

@ -37,7 +37,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -37,7 +37,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `loop` - loop forever
- `hold` - hold last frame forever, or `hold_seconds`
- Draw order (`z_index`)
- Add search fields to filter collections and schedules tables
- Add search fields to filter collections, schedules, playouts tables
- Add selected row background color to schedules and playouts tables
### Fixed
- Fix green output when libplacebo tonemapping is used with NVIDIA acceleration and 10-bit output in FFmpeg Profile

4
ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading.Channels;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
@ -75,6 +76,7 @@ public class CreateChannelHandler( @@ -75,6 +76,7 @@ public class CreateChannelHandler(
{
Name = name,
Number = number,
SortNumber = double.Parse(number, CultureInfo.InvariantCulture),
Group = request.Group,
Categories = request.Categories,
FFmpegProfileId = ffmpegProfileId,

4
ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading.Channels;
using ErsatzTV.Application.Subtitles;
using ErsatzTV.Core;
@ -47,6 +48,7 @@ public class UpdateChannelHandler( @@ -47,6 +48,7 @@ public class UpdateChannelHandler(
c.Name = update.Name;
c.Number = update.Number;
c.SortNumber = double.Parse(update.Number, CultureInfo.InvariantCulture);
c.Group = update.Group;
c.Categories = update.Categories;
c.FFmpegProfileId = update.FFmpegProfileId;

11
ErsatzTV.Application/Playouts/Mapper.cs

@ -4,6 +4,17 @@ namespace ErsatzTV.Application.Playouts; @@ -4,6 +4,17 @@ namespace ErsatzTV.Application.Playouts;
internal static class Mapper
{
internal static PlayoutNameViewModel ProjectToViewModel(Playout playout) =>
new(
playout.Id,
playout.ScheduleKind,
playout.Channel.Name,
playout.Channel.Number,
playout.Channel.PlayoutMode,
playout.ProgramScheduleId == null ? string.Empty : playout.ProgramSchedule.Name,
playout.ScheduleFile,
playout.DailyRebuildTime);
internal static PlayoutItemViewModel ProjectToViewModel(PlayoutItem playoutItem) =>
new(
GetDisplayTitle(playoutItem.MediaItem, playoutItem.ChapterTitle),

3
ErsatzTV.Application/Playouts/PagedPlayoutsViewModel.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Playouts;
public record PagedPlayoutsViewModel(int TotalCount, List<PlayoutNameViewModel> Page);

3
ErsatzTV.Application/Playouts/Queries/GetPagedPlayouts.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Playouts;
public record GetPagedPlayouts(string Query, int PageNum, int PageSize) : IRequest<PagedPlayoutsViewModel>;

40
ErsatzTV.Application/Playouts/Queries/GetPagedPlayoutsHandler.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.Playouts.Mapper;
namespace ErsatzTV.Application.Playouts;
public class GetPagedPlayoutsHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetPagedPlayouts, PagedPlayoutsViewModel>
{
public async Task<PagedPlayoutsViewModel> Handle(
GetPagedPlayouts request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
int count = await dbContext.Playouts.CountAsync(cancellationToken);
IQueryable<Playout> query = dbContext.Playouts
.AsNoTracking()
.Include(p => p.Channel)
.Include(p => p.ProgramSchedule)
.Filter(p => p.Channel != null);
if (!string.IsNullOrWhiteSpace(request.Query))
{
query = query.Where(p => EF.Functions.Like(
EF.Functions.Collate(p.Channel.Name, TvContext.CaseInsensitiveCollation),
$"%{request.Query}%"));
}
List<PlayoutNameViewModel> page = await query
.OrderBy(p => p.Channel.SortNumber)
.Skip(request.PageNum * request.PageSize)
.Take(request.PageSize)
.ToListAsync(cancellationToken)
.Map(list => list.Map(ProjectToViewModel).ToList());
return new PagedPlayoutsViewModel(count, page);
}
}

1
ErsatzTV.Core/Domain/Channel.cs

@ -11,6 +11,7 @@ public class Channel @@ -11,6 +11,7 @@ public class Channel
public int Id { get; set; }
public Guid UniqueId { get; init; }
public string Number { get; set; }
public double SortNumber { get; set; }
public string Name { get; set; }
public string Group { get; set; }
public string Categories { get; set; }

6723
ErsatzTV.Infrastructure.MySql/Migrations/20250918112100_Add_ChannelSortNumber.Designer.cs generated

File diff suppressed because it is too large Load Diff

34
ErsatzTV.Infrastructure.MySql/Migrations/20250918112100_Add_ChannelSortNumber.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_ChannelSortNumber : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "SortNumber",
table: "Channel",
type: "double",
nullable: false,
defaultValue: 0.0);
migrationBuilder.Sql(
@"UPDATE `Channel`
SET `SortNumber` = CAST(`Number` AS DECIMAL(10,2))
WHERE `Number` IS NOT NULL AND TRIM(`Number`) != ''");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SortNumber",
table: "Channel");
}
}
}

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

@ -348,6 +348,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -348,6 +348,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("SongVideoMode")
.HasColumnType("int");
b.Property<double>("SortNumber")
.HasColumnType("double");
b.Property<string>("StreamSelector")
.HasColumnType("longtext");

6552
ErsatzTV.Infrastructure.Sqlite/Migrations/20250918112240_Add_ChannelSortNumber.Designer.cs generated

File diff suppressed because it is too large Load Diff

34
ErsatzTV.Infrastructure.Sqlite/Migrations/20250918112240_Add_ChannelSortNumber.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_ChannelSortNumber : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "SortNumber",
table: "Channel",
type: "REAL",
nullable: false,
defaultValue: 0.0);
migrationBuilder.Sql(
@"UPDATE `Channel`
SET `SortNumber` = CAST(`Number` AS REAL)
WHERE `Number` IS NOT NULL AND TRIM(`Number`) != ''");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SortNumber",
table: "Channel");
}
}
}

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

@ -335,6 +335,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -335,6 +335,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("SongVideoMode")
.HasColumnType("INTEGER");
b.Property<double>("SortNumber")
.HasColumnType("REAL");
b.Property<string>("StreamSelector")
.HasColumnType("TEXT");

42
ErsatzTV/Pages/Playouts.razor

@ -4,7 +4,6 @@ @@ -4,7 +4,6 @@
@using ErsatzTV.Application.Playouts
@using ErsatzTV.Core.Notifications
@using ErsatzTV.Core.Scheduling
@using ErsatzTV.Core.Domain.Filler
@using MediatR.Courier
@implements IDisposable
@inject IDialogService Dialog
@ -56,7 +55,8 @@ @@ -56,7 +55,8 @@
SelectedItemChanged="@(async (PlayoutNameViewModel x) => await PlayoutSelected(x))"
@bind-RowsPerPage="@_rowsPerPage"
ServerData="@(new Func<TableState, CancellationToken, Task<TableData<PlayoutNameViewModel>>>(ServerReload))"
@ref="_table">
@ref="_table"
RowClassFunc="@SelectedRowClassFunc">
<ColGroup>
<MudHidden Breakpoint="Breakpoint.Xs">
<col/>
@ -65,16 +65,21 @@ @@ -65,16 +65,21 @@
<col style="width: 225px;"/>
</MudHidden>
</ColGroup>
<ToolBarContent>
<MudTextField T="string"
ValueChanged="@(s => OnSearch(s))"
Placeholder="Search for playouts"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.FilterList"
Clearable="true">
</MudTextField>
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="new Func<PlayoutViewModel, object>(x => decimal.Parse(x.Channel.Number, CultureInfo.InvariantCulture))">
Channel
</MudTableSortLabel>
Channel
</MudTh>
<MudTh Class="d-none d-md-table-cell">
<MudTableSortLabel SortBy="new Func<PlayoutViewModel, object>(x => x.ProgramSchedule.Name)">
Default Schedule
</MudTableSortLabel>
Default Schedule
</MudTh>
<MudTh Class="d-none d-md-table-cell">Schedule Kind</MudTh>
<MudTh/>
@ -258,6 +263,7 @@ @@ -258,6 +263,7 @@
private MudTable<PlayoutItemViewModel> _detailTable;
private int _rowsPerPage = 10;
private int _detailRowsPerPage = 10;
private string _searchString;
private int? _selectedPlayoutId;
private bool _showFiller;
@ -409,15 +415,8 @@ @@ -409,15 +415,8 @@
{
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsPageSize, state.PageSize.ToString()), cancellationToken);
List<PlayoutNameViewModel> playouts = await Mediator.Send(new GetAllPlayouts(), cancellationToken);
IOrderedEnumerable<PlayoutNameViewModel> sorted = playouts.OrderBy(p => decimal.Parse(p.ChannelNumber, CultureInfo.InvariantCulture));
// TODO: properly page this data
return new TableData<PlayoutNameViewModel>
{
TotalItems = playouts.Count,
Items = sorted.Skip(state.Page * state.PageSize).Take(state.PageSize)
};
PagedPlayoutsViewModel data = await Mediator.Send(new GetPagedPlayouts(_searchString, state.Page, state.PageSize), cancellationToken);
return new TableData<PlayoutNameViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private async Task<TableData<PlayoutItemViewModel>> DetailServerReload(TableState state, CancellationToken cancellationToken)
@ -440,10 +439,19 @@ @@ -440,10 +439,19 @@
return new TableData<PlayoutItemViewModel> { TotalItems = 0 };
}
private void OnSearch(string query)
{
_selectedPlayoutId = null;
_searchString = query;
_table.ReloadServerData();
}
private string PlayoutItemViewRowClassFunc(PlayoutItemViewModel item, int index)
{
return "playout-filler-" + item.FillerKind.Match(
fk => fk.ToString().ToLower(),
() => "unscheduled");
}
private string SelectedRowClassFunc(PlayoutNameViewModel element, int rowNumber) => _selectedPlayoutId != null && _selectedPlayoutId == element.PlayoutId ? "selected" : string.Empty;
}

5
ErsatzTV/Pages/Schedules.razor

@ -22,7 +22,8 @@ @@ -22,7 +22,8 @@
SelectedItemChanged="@(async (ProgramScheduleViewModel x) => await ScheduleSelected(x))"
@bind-RowsPerPage="@_rowsPerPage"
ServerData="@(new Func<TableState, CancellationToken, Task<TableData<ProgramScheduleViewModel>>>(ServerReload))"
@ref="_table">
@ref="_table"
RowClassFunc="@SelectedRowClassFunc">
<ColGroup>
<MudHidden Breakpoint="Breakpoint.Xs">
<col/>
@ -211,4 +212,6 @@ @@ -211,4 +212,6 @@
_searchString = query;
_table.ReloadServerData();
}
private string SelectedRowClassFunc(ProgramScheduleViewModel element, int rowNumber) => _selectedSchedule != null && _selectedSchedule == element ? "selected" : string.Empty;
}

2
ErsatzTV/wwwroot/css/site.css

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
--fanart-background-rgb: 39, 39, 39;
--search-bar-background: rgba(255, 255, 255, .15);
--search-bar-text-color: #fafafa;
--selected-row-color: #009000;
--selected-row-color: #007000;
}
div.ersatztv-light {

Loading…
Cancel
Save