#pragma warning disable CS1591
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text.Json ;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Common.Configuration ;
using MediaBrowser.Controller.Configuration ;
using MediaBrowser.Controller.Library ;
using MediaBrowser.Controller.LiveTv ;
using MediaBrowser.Model.Dto ;
using MediaBrowser.Model.IO ;
using MediaBrowser.Model.LiveTv ;
using Microsoft.Extensions.Caching.Memory ;
using Microsoft.Extensions.Logging ;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public abstract class BaseTunerHost
{
protected readonly IServerConfigurationManager Config ;
protected readonly ILogger < BaseTunerHost > Logger ;
protected readonly IFileSystem FileSystem ;
private readonly IMemoryCache _memoryCache ;
protected BaseTunerHost ( IServerConfigurationManager config , ILogger < BaseTunerHost > logger , IFileSystem fileSystem , IMemoryCache memoryCache )
{
Config = config ;
Logger = logger ;
_memoryCache = memoryCache ;
FileSystem = fileSystem ;
}
public virtual bool IsSupported = > true ;
protected abstract Task < List < ChannelInfo > > GetChannelsInternal ( TunerHostInfo tuner , CancellationToken cancellationToken ) ;
public abstract string Type { get ; }
public async Task < List < ChannelInfo > > GetChannels ( TunerHostInfo tuner , bool enableCache , CancellationToken cancellationToken )
{
var key = tuner . Id ;
if ( enableCache & & ! string . IsNullOrEmpty ( key ) & & _memoryCache . TryGetValue ( key , out List < ChannelInfo > cache ) )
{
return cache ;
}
var list = await GetChannelsInternal ( tuner , cancellationToken ) . ConfigureAwait ( false ) ;
// logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
if ( ! string . IsNullOrEmpty ( key ) & & list . Count > 0 )
{
_memoryCache . Set ( key , list ) ;
}
return list ;
}
protected virtual List < TunerHostInfo > GetTunerHosts ( )
{
return GetConfiguration ( ) . TunerHosts
. Where ( i = > string . Equals ( i . Type , Type , StringComparison . OrdinalIgnoreCase ) )
. ToList ( ) ;
}
public async Task < List < ChannelInfo > > GetChannels ( bool enableCache , CancellationToken cancellationToken )
{
var list = new List < ChannelInfo > ( ) ;
var hosts = GetTunerHosts ( ) ;
foreach ( var host in hosts )
{
var channelCacheFile = Path . Combine ( Config . ApplicationPaths . CachePath , host . Id + "_channels" ) ;
try
{
var channels = await GetChannels ( host , enableCache , cancellationToken ) . ConfigureAwait ( false ) ;
var newChannels = channels . Where ( i = > ! list . Any ( l = > string . Equals ( i . Id , l . Id , StringComparison . OrdinalIgnoreCase ) ) ) . ToList ( ) ;
list . AddRange ( newChannels ) ;
if ( ! enableCache )
{
try
{
Directory . CreateDirectory ( Path . GetDirectoryName ( channelCacheFile ) ) ;
await using var writeStream = File . OpenWrite ( channelCacheFile ) ;
await JsonSerializer . SerializeAsync ( writeStream , channels , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( IOException )
{
}
}
}
catch ( Exception ex )
{
Logger . LogError ( ex , "Error getting channel list" ) ;
if ( enableCache )
{
try
{
await using var readStream = File . OpenRead ( channelCacheFile ) ;
var channels = await JsonSerializer . DeserializeAsync < List < ChannelInfo > > ( readStream , cancellationToken : cancellationToken )
. ConfigureAwait ( false ) ;
list . AddRange ( channels ) ;
}
catch ( IOException )
{
}
}
}
}
return list ;
}
protected abstract Task < List < MediaSourceInfo > > GetChannelStreamMediaSources ( TunerHostInfo tuner , ChannelInfo channel , CancellationToken cancellationToken ) ;
public async Task < List < MediaSourceInfo > > GetChannelStreamMediaSources ( string channelId , CancellationToken cancellationToken )
{
if ( string . IsNullOrEmpty ( channelId ) )
{
throw new ArgumentNullException ( nameof ( channelId ) ) ;
}
if ( IsValidChannelId ( channelId ) )
{
var hosts = GetTunerHosts ( ) ;
foreach ( var host in hosts )
{
try
{
var channels = await GetChannels ( host , true , cancellationToken ) . ConfigureAwait ( false ) ;
var channelInfo = channels . FirstOrDefault ( i = > string . Equals ( i . Id , channelId , StringComparison . OrdinalIgnoreCase ) ) ;
if ( channelInfo ! = null )
{
return await GetChannelStreamMediaSources ( host , channelInfo , cancellationToken ) . ConfigureAwait ( false ) ;
}
}
catch ( Exception ex )
{
Logger . LogError ( ex , "Error getting channels" ) ;
}
}
}
return new List < MediaSourceInfo > ( ) ;
}
protected abstract Task < ILiveStream > GetChannelStream ( TunerHostInfo tuner , ChannelInfo channel , string streamId , List < ILiveStream > currentLiveStreams , CancellationToken cancellationToken ) ;
public async Task < ILiveStream > GetChannelStream ( string channelId , string streamId , List < ILiveStream > currentLiveStreams , CancellationToken cancellationToken )
{
if ( string . IsNullOrEmpty ( channelId ) )
{
throw new ArgumentNullException ( nameof ( channelId ) ) ;
}
if ( ! IsValidChannelId ( channelId ) )
{
throw new FileNotFoundException ( ) ;
}
var hosts = GetTunerHosts ( ) ;
var hostsWithChannel = new List < Tuple < TunerHostInfo , ChannelInfo > > ( ) ;
foreach ( var host in hosts )
{
try
{
var channels = await GetChannels ( host , true , cancellationToken ) . ConfigureAwait ( false ) ;
var channelInfo = channels . FirstOrDefault ( i = > string . Equals ( i . Id , channelId , StringComparison . OrdinalIgnoreCase ) ) ;
if ( channelInfo ! = null )
{
hostsWithChannel . Add ( new Tuple < TunerHostInfo , ChannelInfo > ( host , channelInfo ) ) ;
}
}
catch ( Exception ex )
{
Logger . LogError ( ex , "Error getting channels" ) ;
}
}
foreach ( var hostTuple in hostsWithChannel )
{
var host = hostTuple . Item1 ;
var channelInfo = hostTuple . Item2 ;
try
{
var liveStream = await GetChannelStream ( host , channelInfo , streamId , currentLiveStreams , cancellationToken ) . ConfigureAwait ( false ) ;
var startTime = DateTime . UtcNow ;
await liveStream . Open ( cancellationToken ) . ConfigureAwait ( false ) ;
var endTime = DateTime . UtcNow ;
Logger . LogInformation ( "Live stream opened after {0}ms" , ( endTime - startTime ) . TotalMilliseconds ) ;
return liveStream ;
}
catch ( Exception ex )
{
Logger . LogError ( ex , "Error opening tuner" ) ;
}
}
throw new LiveTvConflictException ( ) ;
}
protected virtual string ChannelIdPrefix = > Type + "_" ;
protected virtual bool IsValidChannelId ( string channelId )
{
if ( string . IsNullOrEmpty ( channelId ) )
{
throw new ArgumentNullException ( nameof ( channelId ) ) ;
}
return channelId . StartsWith ( ChannelIdPrefix , StringComparison . OrdinalIgnoreCase ) ;
}
protected LiveTvOptions GetConfiguration ( )
{
return Config . GetConfiguration < LiveTvOptions > ( "livetv" ) ;
}
}
}