add codec info to multivariant playlist (#2472)
* add codec info to multivariant playlist * upgrade dependencies
This commit is contained in:
@@ -89,6 +89,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- GIFs flagged to loop forever will loop forever
|
- GIFs flagged to loop forever will loop forever
|
||||||
- GIFs with a specific loop count will loop the specified number of times and then hold the final frame
|
- GIFs with a specific loop count will loop the specified number of times and then hold the final frame
|
||||||
- Note that looping is relative to the start of the content, so this works best with permanent watermarks
|
- Note that looping is relative to the start of the content, so this works best with permanent watermarks
|
||||||
|
- Fix some more hls.js warnins by adding codec information to multi-variant playlists
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Filler presets: use separate text fields for `hours`, `minutes` and `seconds` duration
|
- Filler presets: use separate text fields for `hours`, `minutes` and `seconds` duration
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Channels;
|
||||||
|
|
||||||
|
public record ChannelStreamingSpecsViewModel(
|
||||||
|
int Height,
|
||||||
|
int Width,
|
||||||
|
int Bitrate,
|
||||||
|
FFmpegProfileVideoFormat VideoFormat,
|
||||||
|
string VideoProfile,
|
||||||
|
FFmpegProfileAudioFormat AudioFormat);
|
||||||
@@ -49,8 +49,14 @@ internal static class Mapper
|
|||||||
internal static ResolutionViewModel ProjectToViewModel(Resolution resolution) =>
|
internal static ResolutionViewModel ProjectToViewModel(Resolution resolution) =>
|
||||||
new(resolution.Height, resolution.Width);
|
new(resolution.Height, resolution.Width);
|
||||||
|
|
||||||
internal static ResolutionAndBitrateViewModel ProjectToViewModel(Resolution resolution, int bitrate) =>
|
internal static ChannelStreamingSpecsViewModel ProjectToSpecsViewModel(Channel channel) =>
|
||||||
new(resolution.Height, resolution.Width, bitrate);
|
new(
|
||||||
|
channel.FFmpegProfile.Resolution.Height,
|
||||||
|
channel.FFmpegProfile.Resolution.Width,
|
||||||
|
(int)((channel.FFmpegProfile.VideoBitrate * 1000 + channel.FFmpegProfile.AudioBitrate * 1000) * 1.2),
|
||||||
|
channel.FFmpegProfile.VideoFormat,
|
||||||
|
channel.FFmpegProfile.VideoProfile,
|
||||||
|
channel.FFmpegProfile.AudioFormat);
|
||||||
|
|
||||||
private static ArtworkContentTypeModel GetLogo(Channel channel)
|
private static ArtworkContentTypeModel GetLogo(Channel channel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace ErsatzTV.Application.Channels;
|
|
||||||
|
|
||||||
public record GetChannelResolutionAndBitrate(string ChannelNumber) : IRequest<Option<ResolutionAndBitrateViewModel>>;
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ErsatzTV.Application.Channels;
|
||||||
|
|
||||||
|
public record GetChannelStreamingSpecs(string ChannelNumber) : IRequest<Option<ChannelStreamingSpecsViewModel>>;
|
||||||
+5
-7
@@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace ErsatzTV.Application.Channels;
|
namespace ErsatzTV.Application.Channels;
|
||||||
|
|
||||||
public class GetChannelResolutionAndBitrateHandler(IDbContextFactory<TvContext> dbContextFactory)
|
public class GetChannelStreamingSpecsHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||||
: IRequestHandler<GetChannelResolutionAndBitrate, Option<ResolutionAndBitrateViewModel>>
|
: IRequestHandler<GetChannelStreamingSpecs, Option<ChannelStreamingSpecsViewModel>>
|
||||||
{
|
{
|
||||||
public async Task<Option<ResolutionAndBitrateViewModel>> Handle(
|
public async Task<Option<ChannelStreamingSpecsViewModel>> Handle(
|
||||||
GetChannelResolutionAndBitrate request,
|
GetChannelStreamingSpecs request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
@@ -20,8 +20,6 @@ public class GetChannelResolutionAndBitrateHandler(IDbContextFactory<TvContext>
|
|||||||
.ThenInclude(ff => ff.Resolution)
|
.ThenInclude(ff => ff.Resolution)
|
||||||
.SelectOneAsync(c => c.Number, c => c.Number == request.ChannelNumber, cancellationToken);
|
.SelectOneAsync(c => c.Number, c => c.Number == request.ChannelNumber, cancellationToken);
|
||||||
|
|
||||||
return maybeChannel.Map(c => Mapper.ProjectToViewModel(
|
return maybeChannel.Map(Mapper.ProjectToSpecsViewModel);
|
||||||
c.FFmpegProfile.Resolution,
|
|
||||||
(int)((c.FFmpegProfile.VideoBitrate * 1000 + c.FFmpegProfile.AudioBitrate * 1000) * 1.2)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace ErsatzTV.Application.Channels;
|
|
||||||
|
|
||||||
public record ResolutionAndBitrateViewModel(int Height, int Width, int Bitrate);
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<PackageReference Include="CliWrap" Version="3.9.0" />
|
<PackageReference Include="CliWrap" Version="3.9.0" />
|
||||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.1" />
|
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.1" />
|
||||||
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.7" />
|
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.9" />
|
||||||
<PackageReference Include="Jint" Version="4.4.1" />
|
<PackageReference Include="Jint" Version="4.4.1" />
|
||||||
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00017" />
|
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00017" />
|
||||||
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00017" />
|
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00017" />
|
||||||
|
|||||||
@@ -316,14 +316,41 @@ public class IptvController : ControllerBase
|
|||||||
"Failed to return ffmpeg multi-variant playlist; falling back to generated playlist");
|
"Failed to return ffmpeg multi-variant playlist; falling back to generated playlist");
|
||||||
}
|
}
|
||||||
|
|
||||||
Option<ResolutionAndBitrateViewModel> maybeResolutionAndBitrate =
|
Option<ChannelStreamingSpecsViewModel> maybeStreamingSpecs =
|
||||||
await _mediator.Send(new GetChannelResolutionAndBitrate(channelNumber));
|
await _mediator.Send(new GetChannelStreamingSpecs(channelNumber));
|
||||||
string resolution = string.Empty;
|
string resolution = string.Empty;
|
||||||
var bitrate = "10000000";
|
var bitrate = "10000000";
|
||||||
foreach (ResolutionAndBitrateViewModel res in maybeResolutionAndBitrate)
|
foreach (ChannelStreamingSpecsViewModel streamingSpecs in maybeStreamingSpecs)
|
||||||
{
|
{
|
||||||
resolution = $",RESOLUTION={res.Width}x{res.Height}";
|
string videoCodec = streamingSpecs.VideoFormat switch
|
||||||
bitrate = res.Bitrate.ToString(CultureInfo.InvariantCulture);
|
{
|
||||||
|
FFmpegProfileVideoFormat.Av1 => "av01.0.01M.08",
|
||||||
|
FFmpegProfileVideoFormat.Hevc => "hvc1.1.6.L93.B0",
|
||||||
|
FFmpegProfileVideoFormat.H264 => "avc1.4D4028",
|
||||||
|
_ => string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
string audioCodec = streamingSpecs.AudioFormat switch
|
||||||
|
{
|
||||||
|
FFmpegProfileAudioFormat.Ac3 => "ac-3",
|
||||||
|
FFmpegProfileAudioFormat.Aac => "mp4a.40.2",
|
||||||
|
_ => string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
List<string> codecStrings = [];
|
||||||
|
if (!string.IsNullOrWhiteSpace(videoCodec))
|
||||||
|
{
|
||||||
|
codecStrings.Add(videoCodec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(audioCodec))
|
||||||
|
{
|
||||||
|
codecStrings.Add(audioCodec);
|
||||||
|
}
|
||||||
|
|
||||||
|
string codecs = codecStrings.Count > 0 ? $",CODECS=\"{string.Join(",", codecStrings)}\"" : string.Empty;
|
||||||
|
resolution = $",RESOLUTION={streamingSpecs.Width}x{streamingSpecs.Height}{codecs}";
|
||||||
|
bitrate = streamingSpecs.Bitrate.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $@"#EXTM3U
|
return $@"#EXTM3U
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
<PackageReference Include="MudBlazor" Version="8.12.0" />
|
<PackageReference Include="MudBlazor" Version="8.12.0" />
|
||||||
<PackageReference Include="NaturalSort.Extension" Version="4.4.0" />
|
<PackageReference Include="NaturalSort.Extension" Version="4.4.0" />
|
||||||
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
|
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.8.6" />
|
<PackageReference Include="Scalar.AspNetCore" Version="2.8.8" />
|
||||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user