@ -11,30 +11,18 @@ using Microsoft.IO;
@@ -11,30 +11,18 @@ using Microsoft.IO;
namespace ErsatzTV.Application.Channels ;
public partial class GetChannelGuideHandler : IRequestHandler < GetChannelGuide , Either < BaseError , ChannelGuide > >
public partial class GetChannelGuideHandler (
IDbContextFactory < TvContext > dbContextFactory ,
RecyclableMemoryStreamManager recyclableMemoryStreamManager ,
IFileSystem fileSystem ,
ILocalFileSystem localFileSystem )
: IRequestHandler < GetChannelGuide , Either < BaseError , ChannelGuide > >
{
private readonly IDbContextFactory < TvContext > _d bContextFactory ;
private readonly ILocalFileSystem _l ocalFileSystem ;
private readonly RecyclableMemoryStreamManager _ recyclableMemoryStreamManager ;
private readonly IFileSystem _f ileSystem ;
public GetChannelGuideHandler (
IDbContextFactory < TvContext > dbContextFactory ,
RecyclableMemoryStreamManager recyclableMemoryStreamManager ,
IFileSystem fileSystem ,
ILocalFileSystem localFileSystem )
{
_d bContextFactory = dbContextFactory ;
_ recyclableMemoryStreamManager = recyclableMemoryStreamManager ;
_f ileSystem = fileSystem ;
_l ocalFileSystem = localFileSystem ;
}
public async Task < Either < BaseError , ChannelGuide > > Handle (
GetChannelGuide request ,
CancellationToken cancellationToken )
{
await using TvContext dbContext = await _d bContextFactory . CreateDbContextAsync ( cancellationToken ) ;
await using TvContext dbContext = await dbContextFactory . CreateDbContextAsync ( cancellationToken ) ;
var hiddenChannelNumbers = dbContext . Channels
. Where ( c = > c . ShowInEpg = = false )
. Select ( c = > c . Number )
@ -42,13 +30,13 @@ public partial class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, E
@@ -42,13 +30,13 @@ public partial class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, E
. Select ( n = > $"{n}.xml" )
. ToImmutableHashSet ( ) ;
string channelsFile = Path . Combine ( FileSystemLayout . ChannelGuideCacheFolder , "channels.xml" ) ;
if ( ! _f ileSystem . File . Exists ( channelsFile ) )
string channelsFile = fileSystem . Path . Combine ( FileSystemLayout . ChannelGuideCacheFolder , "channels.xml" ) ;
if ( ! f ileSystem. File . Exists ( channelsFile ) )
{
return BaseError . New ( $"Required file {channelsFile} is missing" ) ;
}
long mtime = File . GetLastWriteTime ( channelsFile ) . Ticks ;
long mtime = fileSystem . File . GetLastWriteTime ( channelsFile ) . Ticks ;
var accessTokenUri = $"?v={mtime}" ;
if ( ! string . IsNullOrWhiteSpace ( request . AccessToken ) )
@ -56,7 +44,7 @@ public partial class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, E
@@ -56,7 +44,7 @@ public partial class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, E
accessTokenUri + = $"&access_token={request.AccessToken}" ;
}
string channelsFragment = await File . ReadAllTextAsync ( channelsFile , Encoding . UTF8 , cancellationToken ) ;
string channelsFragment = await ReadAllTextShared ( channelsFile , cancellationToken ) ;
// TODO: is regex faster?
channelsFragment = channelsFragment
@ -65,30 +53,52 @@ public partial class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, E
@@ -65,30 +53,52 @@ public partial class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, E
var channelDataFragments = new Dictionary < string , string > ( ) ;
foreach ( string fileName in _l ocalFileSystem . ListFiles ( FileSystemLayout . ChannelGuideCacheFolder ) )
foreach ( string fileName in l ocalFileSystem. ListFiles ( FileSystemLayout . ChannelGuideCacheFolder ) )
{
if ( fileName . Contains ( "channels" ) )
{
continue ;
}
if ( hiddenChannelNumbers . Contains ( Path . GetFileName ( fileName ) ) )
if ( hiddenChannelNumbers . Contains ( fileSystem . Path . GetFileName ( fileName ) ) )
{
continue ;
}
string channelDataFragment = await File . ReadAllTextAsync ( fileName , Encoding . UTF8 , cancellationToken ) ;
try
{
string channelDataFragment = await ReadAllTextShared ( fileName , cancellationToken ) ;
channelDataFragment = channelDataFragment
. Replace ( "{RequestBase}" , $"{request.Scheme}://{request.Host}{request.BaseUrl}" )
. Replace ( "{AccessTokenUri}" , accessTokenUri ) ;
channelDataFragment = channelDataFragment
. Replace ( "{RequestBase}" , $"{request.Scheme}://{request.Host}{request.BaseUrl}" )
. Replace ( "{AccessTokenUri}" , accessTokenUri ) ;
channelDataFragment = EtvTagRegex ( ) . Replace ( channelDataFragment , string . Empty ) ;
channelDataFragment = EtvTagRegex ( ) . Replace ( channelDataFragment , string . Empty ) ;
channelDataFragments . Add ( Path . GetFileNameWithoutExtension ( fileName ) , channelDataFragment ) ;
channelDataFragments . Add ( fileSystem . Path . GetFileNameWithoutExtension ( fileName ) , channelDataFragment ) ;
}
catch ( FileNotFoundException )
{
// ignore this channel fragment
}
catch ( IOException )
{
// ignore this channel fragment
}
}
return new ChannelGuide ( _ recyclableMemoryStreamManager , channelsFragment , channelDataFragments ) ;
return new ChannelGuide ( recyclableMemoryStreamManager , channelsFragment , channelDataFragments ) ;
}
private async Task < string > ReadAllTextShared ( string fileName , CancellationToken cancellationToken )
{
await using var stream = fileSystem . FileStream . New (
fileName ,
FileMode . Open ,
FileAccess . Read ,
FileShare . ReadWrite ) ;
using var reader = new StreamReader ( stream , Encoding . UTF8 , leaveOpen : true ) ;
return await reader . ReadToEndAsync ( cancellationToken ) ;
}
[GeneratedRegex(@"<etv:[^>] + ? > . * ? < \ / etv : [ ^ > ] + ? > | < etv : [ ^ > ] + ? \ / > ", RegexOptions.Singleline)]