Browse Source

add channel streaming engine setting (#2852)

pull/2853/head
Jason Dove 1 month ago committed by GitHub
parent
commit
ea4d3b651d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 1
      ErsatzTV.Application/Channels/ChannelViewModel.cs
  3. 1
      ErsatzTV.Application/Channels/Commands/CreateChannel.cs
  4. 6
      ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs
  5. 1
      ErsatzTV.Application/Channels/Commands/UpdateChannel.cs
  6. 6
      ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs
  7. 1
      ErsatzTV.Application/Channels/Mapper.cs
  8. 1
      ErsatzTV.Core/Domain/Channel.cs
  9. 7
      ErsatzTV.Core/Domain/StreamingEngine.cs
  10. 3
      ErsatzTV.Core/SystemEnvironment.cs
  11. 7048
      ErsatzTV.Infrastructure.MySql/Migrations/20260419030532_Add_ChannelStreamingEngine.Designer.cs
  12. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20260419030532_Add_ChannelStreamingEngine.cs
  13. 5
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  14. 6875
      ErsatzTV.Infrastructure.Sqlite/Migrations/20260419030448_Add_ChannelStreamingEngine.Designer.cs
  15. 29
      ErsatzTV.Infrastructure.Sqlite/Migrations/20260419030448_Add_ChannelStreamingEngine.cs
  16. 5
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  17. 26
      ErsatzTV/Pages/ChannelEditor.razor
  18. 3
      ErsatzTV/Properties/launchSettings.json
  19. 3
      ErsatzTV/ViewModels/ChannelEditViewModel.cs

4
CHANGELOG.md

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Added
- Add `Streaming Engine` setting to Channel
- `Legacy` - (default) uses existing streaming engine
- `Next` - will use ErsatzTV Next streaming engine, when it is compatible with ErsatzTV Legacy
## [26.4.0] - 2026-04-18 ## [26.4.0] - 2026-04-18
### Changed ### Changed

1
ErsatzTV.Application/Channels/ChannelViewModel.cs

@ -21,6 +21,7 @@ public record ChannelViewModel(
ChannelPlayoutMode PlayoutMode, ChannelPlayoutMode PlayoutMode,
int? MirrorSourceChannelId, int? MirrorSourceChannelId,
TimeSpan? PlayoutOffset, TimeSpan? PlayoutOffset,
StreamingEngine StreamingEngine,
StreamingMode StreamingMode, StreamingMode StreamingMode,
int? WatermarkId, int? WatermarkId,
int? FallbackFillerId, int? FallbackFillerId,

1
ErsatzTV.Application/Channels/Commands/CreateChannel.cs

@ -20,6 +20,7 @@ public record CreateChannel(
ChannelPlayoutMode PlayoutMode, ChannelPlayoutMode PlayoutMode,
int? MirrorSourceChannelId, int? MirrorSourceChannelId,
TimeSpan? PlayoutOffset, TimeSpan? PlayoutOffset,
StreamingEngine StreamingEngine,
StreamingMode StreamingMode, StreamingMode StreamingMode,
int? WatermarkId, int? WatermarkId,
int? FallbackFillerId, int? FallbackFillerId,

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

@ -85,6 +85,7 @@ public class CreateChannelHandler(
PlayoutMode = request.PlayoutMode, PlayoutMode = request.PlayoutMode,
MirrorSourceChannelId = request.MirrorSourceChannelId, MirrorSourceChannelId = request.MirrorSourceChannelId,
PlayoutOffset = request.PlayoutOffset, PlayoutOffset = request.PlayoutOffset,
StreamingEngine = request.StreamingEngine,
StreamingMode = request.StreamingMode, StreamingMode = request.StreamingMode,
Artwork = artwork, Artwork = artwork,
StreamSelectorMode = request.StreamSelectorMode, StreamSelectorMode = request.StreamSelectorMode,
@ -112,6 +113,11 @@ public class CreateChannelHandler(
channel.PlayoutOffset = null; channel.PlayoutOffset = null;
} }
if (channel.StreamingEngine is StreamingEngine.Next)
{
channel.StreamingMode = StreamingMode.HttpLiveStreamingSegmenter;
}
foreach (int id in watermarkId) foreach (int id in watermarkId)
{ {
channel.WatermarkId = id; channel.WatermarkId = id;

1
ErsatzTV.Application/Channels/Commands/UpdateChannel.cs

@ -21,6 +21,7 @@ public record UpdateChannel(
ChannelPlayoutMode PlayoutMode, ChannelPlayoutMode PlayoutMode,
int? MirrorSourceChannelId, int? MirrorSourceChannelId,
TimeSpan? PlayoutOffset, TimeSpan? PlayoutOffset,
StreamingEngine StreamingEngine,
StreamingMode StreamingMode, StreamingMode StreamingMode,
int? WatermarkId, int? WatermarkId,
int? FallbackFillerId, int? FallbackFillerId,

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

@ -131,10 +131,16 @@ public class UpdateChannelHandler(
c.MirrorSourceChannelId = update.MirrorSourceChannelId; c.MirrorSourceChannelId = update.MirrorSourceChannelId;
c.PlayoutOffset = update.PlayoutOffset; c.PlayoutOffset = update.PlayoutOffset;
c.StreamingEngine = update.StreamingEngine;
c.StreamingMode = update.StreamingMode; c.StreamingMode = update.StreamingMode;
c.WatermarkId = update.WatermarkId; c.WatermarkId = update.WatermarkId;
c.FallbackFillerId = update.FallbackFillerId; c.FallbackFillerId = update.FallbackFillerId;
if (c.StreamingEngine is StreamingEngine.Next)
{
c.StreamingMode = StreamingMode.HttpLiveStreamingSegmenter;
}
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
searchTargets.SearchTargetsChanged(); searchTargets.SearchTargetsChanged();

1
ErsatzTV.Application/Channels/Mapper.cs

@ -24,6 +24,7 @@ internal static class Mapper
channel.PlayoutMode, channel.PlayoutMode,
channel.MirrorSourceChannelId, channel.MirrorSourceChannelId,
channel.PlayoutOffset, channel.PlayoutOffset,
channel.StreamingEngine,
channel.StreamingMode, channel.StreamingMode,
channel.WatermarkId, channel.WatermarkId,
channel.FallbackFillerId, channel.FallbackFillerId,

1
ErsatzTV.Core/Domain/Channel.cs

@ -22,6 +22,7 @@ public class Channel
public ChannelWatermark Watermark { get; set; } public ChannelWatermark Watermark { get; set; }
public int? FallbackFillerId { get; set; } public int? FallbackFillerId { get; set; }
public FillerPreset FallbackFiller { get; set; } public FillerPreset FallbackFiller { get; set; }
public StreamingEngine StreamingEngine { get; set; }
public StreamingMode StreamingMode { get; set; } public StreamingMode StreamingMode { get; set; }
public List<Playout> Playouts { get; set; } public List<Playout> Playouts { get; set; }
public List<Artwork> Artwork { get; set; } public List<Artwork> Artwork { get; set; }

7
ErsatzTV.Core/Domain/StreamingEngine.cs

@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Domain;
public enum StreamingEngine
{
Legacy = 0,
Next = 1
}

3
ErsatzTV.Core/SystemEnvironment.cs

@ -60,6 +60,8 @@ public class SystemEnvironment
JellyfinPageSize = jellyfinPageSize; JellyfinPageSize = jellyfinPageSize;
JellyfinEnableStats = !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ETV_JF_ENABLE_STATS")); JellyfinEnableStats = !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ETV_JF_ENABLE_STATS"));
NextFolder = Environment.GetEnvironmentVariable("ETV_NEXT_FOLDER");
} }
public static string BaseUrl { get; } public static string BaseUrl { get; }
@ -74,4 +76,5 @@ public class SystemEnvironment
public static int? SlowApiMs { get; } public static int? SlowApiMs { get; }
public static int JellyfinPageSize { get; } public static int JellyfinPageSize { get; }
public static bool JellyfinEnableStats { get; } public static bool JellyfinEnableStats { get; }
public static string NextFolder { get; }
} }

7048
ErsatzTV.Infrastructure.MySql/Migrations/20260419030532_Add_ChannelStreamingEngine.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20260419030532_Add_ChannelStreamingEngine.cs

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_ChannelStreamingEngine : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "StreamingEngine",
table: "Channel",
type: "int",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "StreamingEngine",
table: "Channel");
}
}
}

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

@ -17,7 +17,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "9.0.12") .HasAnnotation("ProductVersion", "9.0.15")
.HasAnnotation("Relational:MaxIdentifierLength", 64); .HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
@ -372,6 +372,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("StreamSelectorMode") b.Property<int>("StreamSelectorMode")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int>("StreamingEngine")
.HasColumnType("int");
b.Property<int>("StreamingMode") b.Property<int>("StreamingMode")
.HasColumnType("int"); .HasColumnType("int");

6875
ErsatzTV.Infrastructure.Sqlite/Migrations/20260419030448_Add_ChannelStreamingEngine.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.Sqlite/Migrations/20260419030448_Add_ChannelStreamingEngine.cs

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_ChannelStreamingEngine : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "StreamingEngine",
table: "Channel",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "StreamingEngine",
table: "Channel");
}
}
}

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

@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.12"); modelBuilder.HasAnnotation("ProductVersion", "9.0.15");
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b => modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
{ {
@ -359,6 +359,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("StreamSelectorMode") b.Property<int>("StreamSelectorMode")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("StreamingEngine")
.HasColumnType("INTEGER");
b.Property<int>("StreamingMode") b.Property<int>("StreamingMode")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");

26
ErsatzTV/Pages/ChannelEditor.razor

@ -131,15 +131,31 @@ else
</div> </div>
<MudCheckBox @bind-Value="_model.ShowInEpg" For="@(() => _model.ShowInEpg)" Dense="true"/> <MudCheckBox @bind-Value="_model.ShowInEpg" For="@(() => _model.ShowInEpg)" Dense="true"/>
</MudStack> </MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Streaming Engine</MudText>
</div>
<MudSelect @bind-Value="_model.StreamingEngine" For="@(() => _model.StreamingEngine)">
<MudSelectItem Value="@(StreamingEngine.Legacy)">Legacy</MudSelectItem>
<MudSelectItem Value="@(StreamingEngine.Next)">Next</MudSelectItem>
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex"> <div class="d-flex">
<MudText>Streaming Mode</MudText> <MudText>Streaming Mode</MudText>
</div> </div>
<MudSelect @bind-Value="_model.StreamingMode" For="@(() => _model.StreamingMode)"> <MudSelect @bind-Value="_model.StreamingMode" For="@(() => _model.StreamingMode)">
<MudSelectItem Value="@(StreamingMode.TransportStreamHybrid)">MPEG-TS</MudSelectItem> @if (_model.StreamingEngine is StreamingEngine.Next)
<MudSelectItem Value="@(StreamingMode.TransportStream)">MPEG-TS (Legacy)</MudSelectItem> {
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingDirect)">HLS Direct</MudSelectItem> <MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenter)">HLS Segmenter</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenter)">HLS Segmenter</MudSelectItem> }
else
{
<MudSelectItem Value="@(StreamingMode.TransportStreamHybrid)">MPEG-TS</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.TransportStream)">MPEG-TS (Legacy)</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingDirect)">HLS Direct</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenter)">HLS Segmenter</MudSelectItem>
}
</MudSelect> </MudSelect>
</MudStack> </MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
@ -404,6 +420,7 @@ else
_model.PlayoutMode = channelViewModel.PlayoutMode; _model.PlayoutMode = channelViewModel.PlayoutMode;
_model.MirrorSourceChannelId = channelViewModel.MirrorSourceChannelId; _model.MirrorSourceChannelId = channelViewModel.MirrorSourceChannelId;
_model.PlayoutOffset = channelViewModel.PlayoutOffset; _model.PlayoutOffset = channelViewModel.PlayoutOffset;
_model.StreamingEngine = channelViewModel.StreamingEngine;
_model.StreamingMode = channelViewModel.StreamingMode; _model.StreamingMode = channelViewModel.StreamingMode;
_model.StreamSelectorMode = channelViewModel.StreamSelectorMode; _model.StreamSelectorMode = channelViewModel.StreamSelectorMode;
_model.StreamSelector = channelViewModel.StreamSelector; _model.StreamSelector = channelViewModel.StreamSelector;
@ -435,6 +452,7 @@ else
_model.Name = "New Channel"; _model.Name = "New Channel";
_model.Group = "ErsatzTV"; _model.Group = "ErsatzTV";
_model.FFmpegProfileId = ffmpegSettings.DefaultFFmpegProfileId; _model.FFmpegProfileId = ffmpegSettings.DefaultFFmpegProfileId;
_model.StreamingEngine = StreamingEngine.Legacy;
_model.StreamingMode = StreamingMode.TransportStreamHybrid; _model.StreamingMode = StreamingMode.TransportStreamHybrid;
_model.IsEnabled = true; _model.IsEnabled = true;
_model.ShowInEpg = true; _model.ShowInEpg = true;

3
ErsatzTV/Properties/launchSettings.json

@ -7,7 +7,8 @@
"ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development",
"ETV_STREAMING_PORT": "8409", "ETV_STREAMING_PORT": "8409",
"ETV_UI_PORT": "8410" "ETV_UI_PORT": "8410",
"ETV_NEXT_FOLDER": "~/code/ErsatzTV/next/target/debug"
} }
} }
} }

3
ErsatzTV/ViewModels/ChannelEditViewModel.cs

@ -31,6 +31,7 @@ public class ChannelEditViewModel
set => PlayoutOffset = new TimeSpan(hours: value, minutes: 0, seconds: 0); set => PlayoutOffset = new TimeSpan(hours: value, minutes: 0, seconds: 0);
} }
public StreamingEngine StreamingEngine { get; set; }
public StreamingMode StreamingMode { get; set; } public StreamingMode StreamingMode { get; set; }
public int? WatermarkId { get; set; } public int? WatermarkId { get; set; }
public int? FallbackFillerId { get; set; } public int? FallbackFillerId { get; set; }
@ -72,6 +73,7 @@ public class ChannelEditViewModel
PlayoutMode, PlayoutMode,
MirrorSourceChannelId, MirrorSourceChannelId,
PlayoutOffset, PlayoutOffset,
StreamingEngine,
StreamingMode, StreamingMode,
WatermarkId, WatermarkId,
FallbackFillerId, FallbackFillerId,
@ -104,6 +106,7 @@ public class ChannelEditViewModel
PlayoutMode, PlayoutMode,
MirrorSourceChannelId, MirrorSourceChannelId,
PlayoutOffset, PlayoutOffset,
StreamingEngine,
StreamingMode, StreamingMode,
WatermarkId, WatermarkId,
FallbackFillerId, FallbackFillerId,

Loading…
Cancel
Save