Browse Source

use custom mpegts script

pull/2609/head
Jason Dove 9 months ago
parent
commit
49390ddabe
No known key found for this signature in database
  1. 5
      ErsatzTV.Application/Configuration/Queries/GetMpegTsScripts.cs
  2. 14
      ErsatzTV.Application/Configuration/Queries/GetMpegTsScriptsHandler.cs
  3. 4
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs
  4. 1
      ErsatzTV.Application/FFmpegProfiles/FFmpegSettingsViewModel.cs
  5. 55
      ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs
  6. 2
      ErsatzTV.Application/Streaming/HlsSessionWorker.cs
  7. 3
      ErsatzTV.Application/Streaming/Queries/GetWrappedProcessByChannelNumberHandler.cs
  8. 7
      ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs
  9. 1
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  10. 8
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  11. 3
      ErsatzTV.Core/FFmpeg/MpegTsScript.cs
  12. 3
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs
  13. 1
      ErsatzTV.Infrastructure/FFmpeg/MpegTsScriptService.cs
  14. 17
      ErsatzTV/Pages/Settings/FFmpegSettings.razor

5
ErsatzTV.Application/Configuration/Queries/GetMpegTsScripts.cs

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.Application.Configuration;
public record GetMpegTsScripts : IRequest<List<MpegTsScript>>;

14
ErsatzTV.Application/Configuration/Queries/GetMpegTsScriptsHandler.cs

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg;
namespace ErsatzTV.Application.Configuration;
public class GetMpegTsScriptsHandler(IMpegTsScriptService mpegTsScriptService)
: IRequestHandler<GetMpegTsScripts, List<MpegTsScript>>
{
public async Task<List<MpegTsScript>> Handle(GetMpegTsScripts request, CancellationToken cancellationToken)
{
await mpegTsScriptService.RefreshScripts();
return mpegTsScriptService.GetScripts().OrderBy(x => x.Name).ToList();
}
}

4
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs

@ -85,6 +85,10 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler<UpdateFFmpegSettings, @@ -85,6 +85,10 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler<UpdateFFmpegSettings,
ConfigElementKey.FFmpegHlsDirectOutputFormat,
request.Settings.HlsDirectOutputFormat,
cancellationToken);
await _configElementRepository.Upsert(
ConfigElementKey.FFmpegDefaultMpegTsScript,
request.Settings.DefaultMpegTsScript,
cancellationToken);
if (request.Settings.SaveReports && !Directory.Exists(FileSystemLayout.FFmpegReportsFolder))
{

1
ErsatzTV.Application/FFmpegProfiles/FFmpegSettingsViewModel.cs

@ -17,4 +17,5 @@ public class FFmpegSettingsViewModel @@ -17,4 +17,5 @@ public class FFmpegSettingsViewModel
public int WorkAheadSegmenterLimit { get; set; }
public int InitialSegmentCount { get; set; }
public OutputFormatKind HlsDirectOutputFormat { get; set; }
public string DefaultMpegTsScript { get; set; }
}

55
ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs

@ -4,41 +4,55 @@ using ErsatzTV.FFmpeg.OutputFormat; @@ -4,41 +4,55 @@ using ErsatzTV.FFmpeg.OutputFormat;
namespace ErsatzTV.Application.FFmpegProfiles;
public class GetFFmpegSettingsHandler : IRequestHandler<GetFFmpegSettings, FFmpegSettingsViewModel>
public class GetFFmpegSettingsHandler(IConfigElementRepository configElementRepository)
: IRequestHandler<GetFFmpegSettings, FFmpegSettingsViewModel>
{
private readonly IConfigElementRepository _configElementRepository;
public GetFFmpegSettingsHandler(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
public async Task<FFmpegSettingsViewModel> Handle(
GetFFmpegSettings request,
CancellationToken cancellationToken)
{
Option<string> ffmpegPath = await _configElementRepository.GetValue<string>(ConfigElementKey.FFmpegPath, cancellationToken);
Option<string> ffprobePath = await _configElementRepository.GetValue<string>(ConfigElementKey.FFprobePath, cancellationToken);
Option<string> ffmpegPath = await configElementRepository.GetValue<string>(
ConfigElementKey.FFmpegPath,
cancellationToken);
Option<string> ffprobePath = await configElementRepository.GetValue<string>(
ConfigElementKey.FFprobePath,
cancellationToken);
Option<int> defaultFFmpegProfileId =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegDefaultProfileId, cancellationToken);
await configElementRepository.GetValue<int>(ConfigElementKey.FFmpegDefaultProfileId, cancellationToken);
Option<bool> saveReports =
await _configElementRepository.GetValue<bool>(ConfigElementKey.FFmpegSaveReports, cancellationToken);
await configElementRepository.GetValue<bool>(ConfigElementKey.FFmpegSaveReports, cancellationToken);
Option<string> preferredAudioLanguageCode =
await _configElementRepository.GetValue<string>(ConfigElementKey.FFmpegPreferredLanguageCode, cancellationToken);
await configElementRepository.GetValue<string>(
ConfigElementKey.FFmpegPreferredLanguageCode,
cancellationToken);
Option<bool> useEmbeddedSubtitles =
await _configElementRepository.GetValue<bool>(ConfigElementKey.FFmpegUseEmbeddedSubtitles, cancellationToken);
await configElementRepository.GetValue<bool>(
ConfigElementKey.FFmpegUseEmbeddedSubtitles,
cancellationToken);
Option<bool> extractEmbeddedSubtitles =
await _configElementRepository.GetValue<bool>(ConfigElementKey.FFmpegExtractEmbeddedSubtitles, cancellationToken);
await configElementRepository.GetValue<bool>(
ConfigElementKey.FFmpegExtractEmbeddedSubtitles,
cancellationToken);
Option<int> watermark =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegGlobalWatermarkId, cancellationToken);
await configElementRepository.GetValue<int>(ConfigElementKey.FFmpegGlobalWatermarkId, cancellationToken);
Option<int> fallbackFiller =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegGlobalFallbackFillerId, cancellationToken);
await configElementRepository.GetValue<int>(
ConfigElementKey.FFmpegGlobalFallbackFillerId,
cancellationToken);
Option<int> hlsSegmenterIdleTimeout =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegSegmenterTimeout, cancellationToken);
await configElementRepository.GetValue<int>(ConfigElementKey.FFmpegSegmenterTimeout, cancellationToken);
Option<int> workAheadSegmenterLimit =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegWorkAheadSegmenters, cancellationToken);
await configElementRepository.GetValue<int>(ConfigElementKey.FFmpegWorkAheadSegmenters, cancellationToken);
Option<int> initialSegmentCount =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegInitialSegmentCount, cancellationToken);
await configElementRepository.GetValue<int>(ConfigElementKey.FFmpegInitialSegmentCount, cancellationToken);
Option<OutputFormatKind> outputFormatKind =
await _configElementRepository.GetValue<OutputFormatKind>(ConfigElementKey.FFmpegHlsDirectOutputFormat, cancellationToken);
await configElementRepository.GetValue<OutputFormatKind>(
ConfigElementKey.FFmpegHlsDirectOutputFormat,
cancellationToken);
Option<string> defaultMpegTsScript =
await configElementRepository.GetValue<string>(
ConfigElementKey.FFmpegDefaultMpegTsScript,
cancellationToken);
var result = new FFmpegSettingsViewModel
{
@ -52,7 +66,8 @@ public class GetFFmpegSettingsHandler : IRequestHandler<GetFFmpegSettings, FFmpe @@ -52,7 +66,8 @@ public class GetFFmpegSettingsHandler : IRequestHandler<GetFFmpegSettings, FFmpe
HlsSegmenterIdleTimeout = await hlsSegmenterIdleTimeout.IfNoneAsync(60),
WorkAheadSegmenterLimit = await workAheadSegmenterLimit.IfNoneAsync(1),
InitialSegmentCount = await initialSegmentCount.IfNoneAsync(1),
HlsDirectOutputFormat = await outputFormatKind.IfNoneAsync(OutputFormatKind.MpegTs)
HlsDirectOutputFormat = await outputFormatKind.IfNoneAsync(OutputFormatKind.MpegTs),
DefaultMpegTsScript = await defaultMpegTsScript.IfNoneAsync("Default"),
};
foreach (int watermarkId in watermark)

2
ErsatzTV.Application/Streaming/HlsSessionWorker.cs

@ -791,7 +791,7 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -791,7 +791,7 @@ public class HlsSessionWorker : IHlsSessionWorker
foreach (PtsTime pts in queryResult.RightToSeq())
{
_logger.LogWarning("Last pts offset is {Pts}", pts.Value);
_logger.LogDebug("Last pts offset is {Pts}", pts.Value);
result = pts.Value;
}

3
ErsatzTV.Application/Streaming/Queries/GetWrappedProcessByChannelNumberHandler.cs

@ -37,7 +37,8 @@ public class GetWrappedProcessByChannelNumberHandler : FFmpegProcessHandler<GetW @@ -37,7 +37,8 @@ public class GetWrappedProcessByChannelNumberHandler : FFmpegProcessHandler<GetW
channel,
request.Scheme,
request.Host,
request.AccessToken);
request.AccessToken,
cancellationToken);
return new PlayoutItemProcessModel(
process,

7
ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs

@ -236,6 +236,10 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -236,6 +236,10 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
await _configElementRepository.GetValue<OutputFormatKind>(
ConfigElementKey.FFmpegHlsDirectOutputFormat,
cancellationToken);
Option<string> defaultMpegTsScript =
await _configElementRepository.GetValue<string>(
ConfigElementKey.FFmpegDefaultMpegTsScript,
cancellationToken);
var result = new FFmpegSettingsViewModel
{
@ -249,7 +253,8 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -249,7 +253,8 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
HlsSegmenterIdleTimeout = await hlsSegmenterIdleTimeout.IfNoneAsync(60),
WorkAheadSegmenterLimit = await workAheadSegmenterLimit.IfNoneAsync(1),
InitialSegmentCount = await initialSegmentCount.IfNoneAsync(1),
HlsDirectOutputFormat = await outputFormatKind.IfNoneAsync(OutputFormatKind.MpegTs)
HlsDirectOutputFormat = await outputFormatKind.IfNoneAsync(OutputFormatKind.MpegTs),
DefaultMpegTsScript = await defaultMpegTsScript.IfNoneAsync("Default")
};
foreach (int watermarkId in watermark)

1
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -26,6 +26,7 @@ public class ConfigElementKey @@ -26,6 +26,7 @@ public class ConfigElementKey
public static ConfigElementKey FFmpegWorkAheadSegmenters => new("ffmpeg.segmenter.work_ahead_limit");
public static ConfigElementKey FFmpegInitialSegmentCount => new("ffmpeg.segmenter.initial_segment_count");
public static ConfigElementKey FFmpegHlsDirectOutputFormat => new("ffmpeg.hls_direct.output_format");
public static ConfigElementKey FFmpegDefaultMpegTsScript => new("ffmpeg.default_mpegts_script");
public static ConfigElementKey SearchIndexVersion => new("search_index.version");
public static ConfigElementKey HDHRTunerCount => new("hdhr.tuner_count");
public static ConfigElementKey HDHRUUID => new("hdhr.uuid");

8
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -826,7 +826,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -826,7 +826,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
Channel channel,
string scheme,
string host,
string accessToken)
string accessToken,
CancellationToken cancellationToken)
{
var resolution = new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height);
@ -844,10 +845,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -844,10 +845,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
}
// TODO: save reports?
string defaultScript = await _configElementRepository
.GetValue<string>(ConfigElementKey.FFmpegDefaultMpegTsScript, cancellationToken)
.IfNoneAsync("Default");
List<MpegTsScript> allScripts = _mpegTsScriptService.GetScripts();
foreach (var script in allScripts.Where(s => string.Equals(
s.Name,
"Default",
defaultScript,
StringComparison.OrdinalIgnoreCase)))
{
Option<Command> maybeCommand = await _mpegTsScriptService.Execute(

3
ErsatzTV.Core/FFmpeg/MpegTsScript.cs

@ -4,6 +4,9 @@ namespace ErsatzTV.Core.FFmpeg; @@ -4,6 +4,9 @@ namespace ErsatzTV.Core.FFmpeg;
public class MpegTsScript
{
[YamlIgnore]
public string Id { get; set; }
[YamlMember(Alias = "name", ApplyNamingConventions = false)]
public string Name { get; set; }

3
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs

@ -65,7 +65,8 @@ public interface IFFmpegProcessService @@ -65,7 +65,8 @@ public interface IFFmpegProcessService
Channel channel,
string scheme,
string host,
string accessToken);
string accessToken,
CancellationToken cancellationToken);
Task<Command> ResizeImage(string ffmpegPath, string inputFile, string outputFile, int height);

1
ErsatzTV.Infrastructure/FFmpeg/MpegTsScriptService.cs

@ -31,6 +31,7 @@ public class MpegTsScriptService( @@ -31,6 +31,7 @@ public class MpegTsScriptService(
Option<MpegTsScript> maybeScript = FromYaml(await localFileSystem.ReadAllText(definition));
foreach (var script in maybeScript)
{
script.Id = Path.GetFileName(folder);
Scripts[folder] = script;
}
}

17
ErsatzTV/Pages/Settings/FFmpegSettings.razor

@ -1,10 +1,12 @@ @@ -1,10 +1,12 @@
@page "/settings/ffmpeg"
@using ErsatzTV.Application.Configuration
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.Filler
@using ErsatzTV.Application.MediaItems
@using ErsatzTV.Application.Resolutions
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Core.Domain.Filler
@using ErsatzTV.Core.FFmpeg
@using ErsatzTV.FFmpeg.OutputFormat
@implements IDisposable
@inject IMediator Mediator
@ -124,6 +126,17 @@ @@ -124,6 +126,17 @@
<MudSelectItem T="OutputFormatKind" Value="@OutputFormatKind.Mkv">MKV</MudSelectItem>
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Default MPEG-TS Script</MudText>
</div>
<MudSelect @bind-Value="_ffmpegSettings.DefaultMpegTsScript" HelperText="The MPEG-TS script to use when streaming">
@foreach (MpegTsScript script in _mpegTsScripts)
{
<MudSelectItem Value="@script.Id">@script.Name</MudSelectItem>
}
</MudSelect>
</MudStack>
<MudText Typo="Typo.h5" Class="mb-2 mt-10">Custom Resolutions</MudText>
<MudDivider Class="mb-6"/>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => AddCustomResolution())" StartIcon="@Icons.Material.Filled.Add">
@ -162,6 +175,7 @@ @@ -162,6 +175,7 @@
private List<WatermarkViewModel> _watermarks = [];
private List<FillerPresetViewModel> _fillerPresets = [];
private List<ResolutionViewModel> _customResolutions = [];
private readonly List<MpegTsScript> _mpegTsScripts = [];
public void Dispose()
{
@ -187,6 +201,9 @@ @@ -187,6 +201,9 @@
_fillerPresets = await Mediator.Send(new GetAllFillerPresets(), token)
.Map(list => list.Filter(fp => fp.FillerKind == FillerKind.Fallback).ToList());
_mpegTsScripts.Clear();
_mpegTsScripts.AddRange(await Mediator.Send(new GetMpegTsScripts(), token));
await RefreshCustomResolutions(token);
}
catch (OperationCanceledException)

Loading…
Cancel
Save