@ -340,22 +340,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_timerProvider . Delete ( timer ) ;
}
private List < ChannelInfo > _channelCache = null ;
private async Task < IEnumerable < ChannelInfo > > GetChannelsAsync ( bool enableCache , CancellationToken cancellationToken )
{
if ( enableCache & & _channelCache ! = null )
{
return _channelCache . ToList ( ) ;
}
var list = new List < ChannelInfo > ( ) ;
foreach ( var hostInstance in _liveTvManager . TunerHosts )
{
try
{
var channels = await hostInstance . GetChannels ( cancellationToken) . ConfigureAwait ( false ) ;
var channels = await hostInstance . GetChannels ( enableCache, cancellationToken) . ConfigureAwait ( false ) ;
list . AddRange ( channels ) ;
}
@ -388,7 +381,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
_channelCache = list . ToList ( ) ;
return list ;
}
@ -400,7 +392,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
try
{
var channels = await hostInstance . GetChannels ( cancellationToken ) . ConfigureAwait ( false ) ;
var channels = await hostInstance . GetChannels ( false , cancellationToken ) . ConfigureAwait ( false ) ;
list . AddRange ( channels ) ;
}
@ -632,6 +624,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
existingTimer . Genres = updatedTimer . Genres ;
existingTimer . HomePageUrl = updatedTimer . HomePageUrl ;
existingTimer . IsKids = updatedTimer . IsKids ;
existingTimer . IsNews = updatedTimer . IsNews ;
existingTimer . IsMovie = updatedTimer . IsMovie ;
existingTimer . IsProgramSeries = updatedTimer . IsProgramSeries ;
existingTimer . IsSports = updatedTimer . IsSports ;
@ -836,33 +829,68 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
var result = await GetChannelStreamInternal ( channelId , streamId , cancellationToken ) . ConfigureAwait ( false ) ;
return result . Item1 . PublicMediaSource ;
return result . Item2 ;
}
private MediaSourceInfo CloneMediaSource ( MediaSourceInfo mediaSource , int consumerId )
{
var json = _jsonSerializer . SerializeToString ( mediaSource ) ;
mediaSource = _jsonSerializer . DeserializeFromString < MediaSourceInfo > ( json ) ;
mediaSource . Id = consumerId . ToString ( CultureInfo . InvariantCulture ) + "_" + mediaSource . Id ;
return mediaSource ;
}
private async Task < Tuple < LiveStream , ITunerHost > > GetChannelStreamInternal ( string channelId , string streamId , CancellationToken cancellationToken )
private async Task < Tuple < LiveStream , MediaSourceInfo, ITunerHost> > GetChannelStreamInternal ( string channelId , string streamId , CancellationToken cancellationToken )
{
_logger . Info ( "Streaming Channel " + channelId ) ;
foreach ( var hostInstance in _liveTvManager . TunerHosts )
await _liveStreamsSemaphore . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
var result = _liveStreams . Values . FirstOrDefault ( i = > string . Equals ( i . OriginalStreamId , streamId , StringComparison . OrdinalIgnoreCase ) ) ;
if ( result ! = null )
{
try
{
var result = await hostInstance . GetChannelStream ( channelId , streamId , cancellationToken ) . ConfigureAwait ( false ) ;
//result.ConsumerCount++;
await _liveStreamsSemaphore . WaitAsync ( ) . ConfigureAwait ( false ) ;
_liveStreams [ result . Id ] = result ;
_liveStreamsSemaphore . Release ( ) ;
//_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
return new Tuple < LiveStream , ITunerHost > ( result , hostInstance ) ;
}
catch ( FileNotFoundException )
{
}
catch ( Exception e )
//_liveStreamsSemaphore.Release();
//return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost);
}
try
{
foreach ( var hostInstance in _liveTvManager . TunerHosts )
{
_logger . ErrorException ( "Error getting channel stream" , e ) ;
try
{
result = await hostInstance . GetChannelStream ( channelId , streamId , cancellationToken ) . ConfigureAwait ( false ) ;
_liveStreams [ result . OpenedMediaSource . Id ] = result ;
result . ConsumerCount + + ;
result . TunerHost = hostInstance ;
result . OriginalStreamId = streamId ;
_logger . Info ( "Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}" ,
streamId , result . OpenedMediaSource . Id , result . OpenedMediaSource . LiveStreamId ) ;
return new Tuple < LiveStream , MediaSourceInfo , ITunerHost > ( result , CloneMediaSource ( result . OpenedMediaSource , 0 ) , hostInstance ) ;
}
catch ( FileNotFoundException )
{
}
catch ( OperationCanceledException )
{
}
}
}
finally
{
_liveStreamsSemaphore . Release ( ) ;
}
throw new ApplicationException ( "Tuner not found." ) ;
}
@ -896,25 +924,41 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public async Task CloseLiveStream ( string id , CancellationToken cancellationToken )
{
await _liveStreamsSemaphore . WaitAsync ( ) . ConfigureAwait ( false ) ;
// Ignore the consumer id
id = id . Substring ( id . IndexOf ( '_' ) + 1 ) ;
await _liveStreamsSemaphore . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
try
{
LiveStream stream ;
if ( _liveStreams . TryGetValue ( id , out stream ) )
{
_liveStreams. Remove ( id ) ;
stream. ConsumerCount - - ;
try
_logger . Info ( "Live stream {0} consumer count is now {1}" , id , stream . ConsumerCount ) ;
if ( stream . ConsumerCount < = 0 )
{
_liveStreams . Remove ( id ) ;
_logger . Info ( "Closing live stream {0}" , id ) ;
await stream . Close ( ) . ConfigureAwait ( false ) ;
_logger . Info ( "Live stream {0} closed successfully" , id ) ;
}
catch ( Exception ex )
{
_logger . ErrorException ( "Error closing live stream" , ex ) ;
}
}
else
{
_logger . Warn ( "Live stream not found: {0}, unable to close" , id ) ;
}
}
catch ( OperationCanceledException )
{
}
catch ( Exception ex )
{
_logger . ErrorException ( "Error closing live stream" , ex ) ;
}
finally
{
@ -1095,20 +1139,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var recordPath = GetRecordingPath ( timer , out seriesPath ) ;
var recordingStatus = RecordingStatus . New ;
LiveStream liveStream = null ;
string liveStreamId = null ;
try
{
var allMediaSources =
await GetChannelStreamMediaSources ( timer . ChannelId , CancellationToken . None ) . ConfigureAwait ( false ) ;
var liveStreamInfo =
await
GetChannelStreamInternal ( timer . ChannelId , allMediaSources [ 0 ] . Id , CancellationToken . None )
var liveStreamInfo = await GetChannelStreamInternal ( timer . ChannelId , allMediaSources [ 0 ] . Id , CancellationToken . None )
. ConfigureAwait ( false ) ;
liveStream = liveStreamInfo . Item1 ;
var mediaStreamInfo = liveStreamInfo . Item 1. PublicMediaSource ;
var tunerHost = liveStreamInfo . Item2 ;
var mediaStreamInfo = liveStreamInfo . Item 2 ;
liveStreamId = mediaStreamInfo . Id ;
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
//await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
@ -1140,15 +1182,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
EnforceKeepUpTo ( timer ) ;
} ;
var pathWithDuration = tunerHost . ApplyDuration ( mediaStreamInfo . Path , duration ) ;
// If it supports supplying duration via url
if ( ! string . Equals ( pathWithDuration , mediaStreamInfo . Path , StringComparison . OrdinalIgnoreCase ) )
{
mediaStreamInfo . Path = pathWithDuration ;
mediaStreamInfo . RunTimeTicks = duration . Ticks ;
}
await recorder . Record ( mediaStreamInfo , recordPath , duration , onStarted , cancellationToken )
. ConfigureAwait ( false ) ;
@ -1166,11 +1199,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recordingStatus = RecordingStatus . Error ;
}
if ( liveStream ! = null )
if ( ! string . IsNullOrWhiteSpace ( liveStreamId ) )
{
try
{
await CloseLiveStream ( liveStream . Id, CancellationToken . None ) . ConfigureAwait ( false ) ;
await CloseLiveStream ( liveStream Id, CancellationToken . None ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex )
{
@ -1251,7 +1284,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
private async Task DeleteLibraryItemsForTimers ( List < TimerInfo > timers )
{
foreach ( var timer in timers )
@ -1295,7 +1328,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
catch ( FileNotFoundException )
{
}
}
@ -1492,6 +1525,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
AddGenre ( timer . Genres , "Kids" ) ;
}
if ( timer . IsNews )
{
AddGenre ( timer . Genres , "News" ) ;
}
foreach ( var genre in timer . Genres )
{