@ -54,11 +54,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
new ConcurrentDictionary < string , LiveStreamData > ( ) ;
private List < Guid > _channelIdList = new List < Guid > ( ) ;
private Dictionary < Guid , LiveTvProgram > _programs = new Dictionary < Guid , LiveTvProgram > ( ) ;
private Dictionary < Guid , LiveTvProgram > _programs ;
private readonly ConcurrentDictionary < Guid , bool > _refreshedPrograms = new ConcurrentDictionary < Guid , bool > ( ) ;
private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
public LiveTvManager ( IApplicationHost appHost , IServerConfigurationManager config , ILogger logger , IItemRepository itemRepo , IImageProcessor imageProcessor , IUserDataManager userDataManager , IDtoService dtoService , IUserManager userManager , ILibraryManager libraryManager , ITaskManager taskManager , ILocalizationManager localization , IJsonSerializer jsonSerializer , IProviderManager providerManager )
{
_config = config ;
@ -109,6 +107,37 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_taskManager . CancelIfRunningAndQueue < RefreshChannelsScheduledTask > ( ) ;
}
private readonly object _programsDataLock = new object ( ) ;
private Dictionary < Guid , LiveTvProgram > GetProgramsDictionary ( )
{
if ( _programs = = null )
{
lock ( _programsDataLock )
{
if ( _programs = = null )
{
var dict = new Dictionary < Guid , LiveTvProgram > ( ) ;
foreach ( var item in _itemRepo . GetItemsOfType ( typeof ( LiveTvProgram ) )
. Cast < LiveTvProgram > ( )
. ToList ( ) )
{
dict [ item . Id ] = item ;
}
_programs = dict ;
}
}
}
return _programs ;
}
private IEnumerable < LiveTvProgram > GetPrograms ( )
{
return GetProgramsDictionary ( ) . Values ;
}
public async Task < QueryResult < LiveTvChannel > > GetInternalChannels ( LiveTvChannelQuery query , CancellationToken cancellationToken )
{
var user = string . IsNullOrEmpty ( query . UserId ) ? null : _userManager . GetUserById ( query . UserId ) ;
@ -260,7 +289,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
LiveTvProgram obj = null ;
_programs . TryGetValue ( guid , out obj ) ;
GetProgramsDictionary( ) . TryGetValue ( guid , out obj ) ;
if ( obj ! = null )
{
@ -597,7 +626,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item . ProductionYear = info . ProductionYear ;
item . PremiereDate = item . PremiereDate ? ? info . OriginalAirDate ;
await item . UpdateToRepository ( ItemUpdateType . MetadataImport , cancellationToken ) . ConfigureAwait ( false ) ;
return item ;
@ -691,7 +720,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task < QueryResult < ProgramInfoDto > > GetPrograms ( ProgramQuery query , CancellationToken cancellationToken )
{
IEnumerable < LiveTvProgram > programs = _programs. Values ;
IEnumerable < LiveTvProgram > programs = GetPrograms( ) ;
if ( query . MinEndDate . HasValue )
{
@ -806,7 +835,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task < QueryResult < LiveTvProgram > > GetRecommendedProgramsInternal ( RecommendedProgramQuery query , CancellationToken cancellationToken )
{
IEnumerable < LiveTvProgram > programs = _programs. Values ;
IEnumerable < LiveTvProgram > programs = GetPrograms( ) ;
var user = _userManager . GetUserById ( query . UserId ) ;
@ -995,24 +1024,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
internal async Task RefreshChannels ( IProgress < double > progress , CancellationToken cancellationToken )
{
await _refreshSemaphore . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
try
{
var innerProgress = new ActionableProgress < double > ( ) ;
innerProgress . RegisterAction ( p = > progress . Report ( p * . 9 ) ) ;
await RefreshChannelsInternal ( innerProgress , cancellationToken ) . ConfigureAwait ( false ) ;
var innerProgress = new ActionableProgress < double > ( ) ;
innerProgress . RegisterAction ( p = > progress . Report ( p * . 9 ) ) ;
await RefreshChannelsInternal ( innerProgress , cancellationToken ) . ConfigureAwait ( false ) ;
innerProgress = new ActionableProgress < double > ( ) ;
innerProgress . RegisterAction ( p = > progress . Report ( 90 + ( p * . 1 ) ) ) ;
await CleanDatabaseInternal ( progress , cancellationToken ) . ConfigureAwait ( false ) ;
innerProgress = new ActionableProgress < double > ( ) ;
innerProgress . RegisterAction ( p = > progress . Report ( 90 + ( p * . 1 ) ) ) ;
await CleanDatabaseInternal ( progress , cancellationToken ) . ConfigureAwait ( false ) ;
RefreshIfNeeded ( _programs . Values . Where ( i = > ( i . StartDate - DateTime . UtcNow ) . TotalDays < = 1 ) . ToList ( ) ) ;
}
finally
{
_refreshSemaphore . Release ( ) ;
}
RefreshIfNeeded ( GetPrograms ( ) . Where ( i = > ( i . StartDate - DateTime . UtcNow ) . TotalDays < = 1 ) . ToList ( ) ) ;
}
private async Task RefreshChannelsInternal ( IProgress < double > progress , CancellationToken cancellationToken )
@ -1136,7 +1156,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress . Report ( 80 * percent + 10 ) ;
}
_programs = programs . ToDictionary ( i = > i . Id ) ;
lock ( _programsDataLock )
{
_programs = programs . ToDictionary ( i = > i . Id ) ;
}
_refreshedPrograms . Clear ( ) ;
progress . Report ( 90 ) ;
@ -1147,28 +1171,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress . Report ( 100 ) ;
}
public async Task CleanDatabase ( IProgress < double > progress , CancellationToken cancellationToken )
{
await _refreshSemaphore . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
try
{
await DeleteOldPrograms ( _programs . Keys . ToList ( ) , progress , cancellationToken ) . ConfigureAwait ( false ) ;
}
finally
{
_refreshSemaphore . Release ( ) ;
}
}
private Task CleanDatabaseInternal ( IProgress < double > progress , CancellationToken cancellationToken )
{
return DeleteOldPrograms ( _programs . Keys . ToList ( ) , progress , cancellationToken ) ;
return DeleteOldPrograms ( GetProgramsDictionary ( ) . Keys . ToList ( ) , progress , cancellationToken ) ;
}
private async Task DeleteOldPrograms ( List < Guid > currentIdList , IProgress < double > progress , CancellationToken cancellationToken )
{
var list = _itemRepo . GetItem sOfType( typeof ( LiveTvProgram ) ) . ToList ( ) ;
var list = _itemRepo . GetItemIdsOfType ( typeof ( LiveTvProgram ) ) . ToList ( ) ;
var numComplete = 0 ;
@ -1549,7 +1559,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var now = DateTime . UtcNow ;
var program = _programs. Values
var program = GetPrograms( )
. Where ( i = > string . Equals ( externalChannelId , i . ExternalChannelId , StringComparison . OrdinalIgnoreCase ) )
. OrderBy ( i = > i . StartDate )
. SkipWhile ( i = > now > = ( i . EndDate ? ? DateTime . MinValue ) )
@ -1742,7 +1752,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var dtoOptions = new DtoOptions ( ) ;
dtoOptions . Fields . Remove ( ItemFields . SyncInfo ) ;
var recordingResult = await GetRecordings ( new RecordingQuery
{
UserId = query . UserId
@ -1855,13 +1865,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public GuideInfo GetGuideInfo ( )
{
var programs = _programs . ToList ( ) ;
var programs = GetPrograms( ) . OrderBy ( i = > i . StartDate ) . ToList ( ) ;
var startDate = _programs . Count = = 0 ? DateTime . MinValue :
programs . Select ( i = > i . Value . StartDate ) . Min ( ) ;
var startDate = programs . Count = = 0 ?
DateTime . MinValue :
programs [ 0 ] . StartDate ;
var endDate = programs . Count = = 0 ? DateTime . MinValue :
programs . Select ( i = > i . Value . StartDate ) . Max ( ) ;
var endDate = programs . Count = = 0 ?
DateTime . MinValue :
programs [ programs . Count - 1 ] . StartDate ;
return new GuideInfo
{