using System; using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; using Serilog; namespace ErsatzTV.Application.Streaming.Queries { public class GetHlsPlaylistByChannelNumberHandler : IRequestHandler> { private readonly IChannelRepository _channelRepository; private readonly IPlayoutRepository _playoutRepository; public GetHlsPlaylistByChannelNumberHandler( IChannelRepository channelRepository, IPlayoutRepository playoutRepository) { _channelRepository = channelRepository; _playoutRepository = playoutRepository; } public Task> Handle( GetHlsPlaylistByChannelNumber request, CancellationToken cancellationToken) => ChannelMustExist(request) .Map(v => v.ToEither()) .BindT(channel => GetPlaylist(request, channel)); private async Task> GetPlaylist( GetHlsPlaylistByChannelNumber request, Channel channel) { DateTimeOffset now = DateTimeOffset.Now; Option maybePlayoutItem = await _playoutRepository.GetPlayoutItem(channel.Id, now); return maybePlayoutItem.Match>( playoutItem => { double timeRemaining = Math.Abs((playoutItem.Finish - now).TotalSeconds); return $@"#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:18000 #EXTINF:{timeRemaining:F2}, {request.Scheme}://{request.Host}/ffmpeg/stream/{request.ChannelNumber} "; }, () => { // TODO: playlist for error stream Log.Logger.Error("Unable to locate playout item for m3u8"); return BaseError.New($"Unable to locate playout item for channel {channel.Number}"); }); } private async Task> ChannelMustExist(GetHlsPlaylistByChannelNumber request) => (await _channelRepository.GetByNumber(request.ChannelNumber)) .ToValidation($"Channel number {request.ChannelNumber} does not exist."); } }