mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
4.3 KiB
123 lines
4.3 KiB
using ErsatzTV.Core.Domain; |
|
using ErsatzTV.Core.Extensions; |
|
using ErsatzTV.Infrastructure.Data; |
|
using Microsoft.EntityFrameworkCore; |
|
using Microsoft.Extensions.Logging; |
|
|
|
namespace ErsatzTV.Application.Channels; |
|
|
|
public class GetChannelFramerateHandler : IRequestHandler<GetChannelFramerate, Option<int>> |
|
{ |
|
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
|
private readonly ILogger<GetChannelFramerateHandler> _logger; |
|
|
|
public GetChannelFramerateHandler( |
|
IDbContextFactory<TvContext> dbContextFactory, |
|
ILogger<GetChannelFramerateHandler> logger) |
|
{ |
|
_dbContextFactory = dbContextFactory; |
|
_logger = logger; |
|
} |
|
|
|
public async Task<Option<int>> Handle(GetChannelFramerate request, CancellationToken cancellationToken) |
|
{ |
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); |
|
|
|
FFmpegProfile ffmpegProfile = await dbContext.Channels |
|
.Filter(c => c.Number == request.ChannelNumber) |
|
.Include(c => c.FFmpegProfile) |
|
.Map(c => c.FFmpegProfile) |
|
.SingleAsync(cancellationToken); |
|
|
|
if (!ffmpegProfile.NormalizeFramerate) |
|
{ |
|
return Option<int>.None; |
|
} |
|
|
|
// TODO: expand to check everything in collection rather than what's scheduled? |
|
_logger.LogDebug("Checking frame rates for channel {ChannelNumber}", request.ChannelNumber); |
|
|
|
List<Playout> playouts = await dbContext.Playouts |
|
.Include(p => p.Items) |
|
.ThenInclude(pi => pi.MediaItem) |
|
.ThenInclude(mi => (mi as Movie).MediaVersions) |
|
.Include(p => p.Items) |
|
.ThenInclude(pi => pi.MediaItem) |
|
.ThenInclude(mi => (mi as Episode).MediaVersions) |
|
.Include(p => p.Items) |
|
.ThenInclude(pi => pi.MediaItem) |
|
.ThenInclude(mi => (mi as Song).MediaVersions) |
|
.Include(p => p.Items) |
|
.ThenInclude(pi => pi.MediaItem) |
|
.ThenInclude(mi => (mi as MusicVideo).MediaVersions) |
|
.Include(p => p.Items) |
|
.ThenInclude(pi => pi.MediaItem) |
|
.ThenInclude(mi => (mi as OtherVideo).MediaVersions) |
|
.Filter(p => p.Channel.Number == request.ChannelNumber) |
|
.ToListAsync(cancellationToken); |
|
|
|
var frameRates = playouts.Map(p => p.Items.Map(i => i.MediaItem.GetHeadVersion())) |
|
.Flatten() |
|
.Map(mv => mv.RFrameRate) |
|
.ToList(); |
|
|
|
var distinct = frameRates.Distinct().ToList(); |
|
if (distinct.Count > 1) |
|
{ |
|
// TODO: something more intelligent than minimum framerate? |
|
int result = frameRates.Map(ParseFrameRate).Min(); |
|
if (result < 24) |
|
{ |
|
_logger.LogInformation( |
|
"Normalizing frame rate for channel {ChannelNumber} from {Distinct} to {FrameRate} instead of min value {MinFrameRate}", |
|
request.ChannelNumber, |
|
distinct, |
|
24, |
|
result); |
|
|
|
return 24; |
|
} |
|
|
|
_logger.LogInformation( |
|
"Normalizing frame rate for channel {ChannelNumber} from {Distinct} to {FrameRate}", |
|
request.ChannelNumber, |
|
distinct, |
|
result); |
|
return result; |
|
} |
|
|
|
if (distinct.Count != 0) |
|
{ |
|
_logger.LogInformation( |
|
"All content on channel {ChannelNumber} has the same frame rate of {FrameRate}; will not normalize", |
|
request.ChannelNumber, |
|
distinct[0]); |
|
} |
|
else |
|
{ |
|
_logger.LogInformation( |
|
"No content on channel {ChannelNumber} has frame rate information; will not normalize", |
|
request.ChannelNumber); |
|
} |
|
|
|
return None; |
|
} |
|
|
|
private int ParseFrameRate(string frameRate) |
|
{ |
|
if (!int.TryParse(frameRate, out int fr)) |
|
{ |
|
string[] split = (frameRate ?? string.Empty).Split("/"); |
|
if (int.TryParse(split[0], out int left) && int.TryParse(split[1], out int right)) |
|
{ |
|
fr = (int)Math.Round(left / (double)right); |
|
} |
|
else |
|
{ |
|
fr = 24; |
|
} |
|
} |
|
|
|
return fr; |
|
} |
|
}
|
|
|