@ -305,26 +305,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var seriesTimers = await GetSeriesTimersAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
List < ChannelInfo > channels = null ;
foreach ( var timer in seriesTimers )
{
List < ProgramInfo > epgData ;
if ( timer . RecordAnyChannel )
{
if ( channels = = null )
{
channels = ( await GetChannelsAsync ( true , CancellationToken . None ) . ConfigureAwait ( false ) ) . ToList ( ) ;
}
var channelIds = channels . Select ( i = > i . Id ) . ToList ( ) ;
epgData = GetEpgDataForChannels ( channelIds ) ;
}
else
{
epgData = GetEpgDataForChannel ( timer . ChannelId ) ;
}
await UpdateTimersForSeriesTimer ( epgData , timer , false , true ) . ConfigureAwait ( false ) ;
await UpdateTimersForSeriesTimer ( timer , false , true ) . ConfigureAwait ( false ) ;
}
}
@ -332,6 +315,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var timers = await GetTimersAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
var tempChannelCache = new Dictionary < string , LiveTvChannel > ( ) ;
foreach ( var timer in timers )
{
if ( DateTime . UtcNow > timer . EndDate & & ! _activeRecordings . ContainsKey ( timer . Id ) )
@ -345,15 +330,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
continue ;
}
var epg = GetEpgDataForChannel ( timer . ChannelId ) ;
var program = epg . FirstOrDefault ( i = > string . Equals ( i . Id , timer . ProgramId , StringComparison . OrdinalIgnoreCase ) ) ;
var program = GetProgramInfoFromCache ( timer ) ;
if ( program = = null )
{
OnTimerOutOfDate ( timer ) ;
continue ;
}
RecordingHelper. CopyProgramInfoToTimerInfo( program , timer ) ;
CopyProgramInfoToTimerInfo( program , timer , tempChannelCache ) ;
_timerProvider . Update ( timer ) ;
}
}
@ -621,7 +605,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
ActiveRecordingInfo activeRecordingInfo ;
if ( _activeRecordings . TryGetValue ( timerId , out activeRecordingInfo ) )
{
{
activeRecordingInfo . Timer = timer ;
activeRecordingInfo . CancellationTokenSource . Cancel ( ) ;
}
@ -672,11 +656,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
timer . Id = Guid . NewGuid ( ) . ToString ( "N" ) ;
ProgramInfo programInfo = null ;
LiveTv Program programInfo = null ;
if ( ! string . IsNullOrWhiteSpace ( timer . ProgramId ) )
{
programInfo = GetProgramInfoFromCache ( timer .ChannelId , timer . ProgramId );
programInfo = GetProgramInfoFromCache ( timer );
}
if ( programInfo = = null )
{
@ -686,7 +670,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if ( programInfo ! = null )
{
RecordingHelper. CopyProgramInfoToTimerInfo( programInfo , timer ) ;
CopyProgramInfoToTimerInfo( programInfo , timer ) ;
}
timer . IsManual = true ;
@ -698,24 +682,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
info . Id = Guid . NewGuid ( ) . ToString ( "N" ) ;
List < ProgramInfo > epgData ;
if ( info . RecordAnyChannel )
{
var channels = await GetChannelsAsync ( true , CancellationToken . None ) . ConfigureAwait ( false ) ;
var channelIds = channels . Select ( i = > i . Id ) . ToList ( ) ;
epgData = GetEpgDataForChannels ( channelIds ) ;
}
else
{
epgData = GetEpgDataForChannel ( info . ChannelId ) ;
}
// populate info.seriesID
var program = epgData. FirstOrDefault ( i = > string . Equals ( i . Id , info . ProgramId , StringComparison . OrdinalIgnoreCase ) ) ;
var program = GetProgramInfoFromCache ( info . ProgramId ) ;
if ( program ! = null )
{
info . SeriesId = program . SeriesId;
info . SeriesId = program . ExternalSeriesId ;
}
else
{
@ -750,7 +722,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_timerProvider . AddOrUpdate ( timer , false ) ;
}
await UpdateTimersForSeriesTimer ( epgData, info, true , false ) . ConfigureAwait ( false ) ;
await UpdateTimersForSeriesTimer ( info, true , false ) . ConfigureAwait ( false ) ;
return info . Id ;
}
@ -779,19 +751,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_seriesTimerProvider . Update ( instance ) ;
List < ProgramInfo > epgData ;
if ( instance . RecordAnyChannel )
{
var channels = await GetChannelsAsync ( true , CancellationToken . None ) . ConfigureAwait ( false ) ;
var channelIds = channels . Select ( i = > i . Id ) . ToList ( ) ;
epgData = GetEpgDataForChannels ( channelIds ) ;
}
else
{
epgData = GetEpgDataForChannel ( instance . ChannelId ) ;
}
await UpdateTimersForSeriesTimer ( epgData , instance , true , true ) . ConfigureAwait ( false ) ;
await UpdateTimersForSeriesTimer ( instance , true , true ) . ConfigureAwait ( false ) ;
}
}
@ -962,23 +922,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return Task . FromResult ( ( IEnumerable < SeriesTimerInfo > ) _seriesTimerProvider . GetAll ( ) ) ;
}
public async Task < IEnumerable < ProgramInfo > > GetProgramsAsync ( string channelId , DateTime startDateUtc , DateTime endDateUtc , CancellationToken cancellationToken )
{
try
{
return await GetProgramsAsyncInternal ( channelId , startDateUtc , endDateUtc , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( OperationCanceledException )
{
throw ;
}
catch ( Exception ex )
{
_logger . ErrorException ( "Error getting programs" , ex ) ;
return GetEpgDataForChannel ( channelId ) . Where ( i = > i . StartDate < = endDateUtc & & i . EndDate > = startDateUtc ) ;
}
}
private bool IsListingProviderEnabledForTuner ( ListingsProviderInfo info , string tunerHostId )
{
if ( info . EnableAllTuners )
@ -994,7 +937,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return info . EnabledTuners . Contains ( tunerHostId , StringComparer . OrdinalIgnoreCase ) ;
}
p rivate async Task < IEnumerable < ProgramInfo > > GetProgramsAsync Internal ( string channelId , DateTime startDateUtc , DateTime endDateUtc , CancellationToken cancellationToken )
p ublic async Task < IEnumerable < ProgramInfo > > GetProgramsAsync ( string channelId , DateTime startDateUtc , DateTime endDateUtc , CancellationToken cancellationToken )
{
var channels = await GetChannelsAsync ( true , cancellationToken ) . ConfigureAwait ( false ) ;
var channel = channels . First ( i = > string . Equals ( i . Id , channelId , StringComparison . OrdinalIgnoreCase ) ) ;
@ -1037,8 +980,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if ( programs . Count > 0 )
{
SaveEpgDataForChannel ( channelId , programs ) ;
return programs ;
}
}
@ -1464,11 +1405,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
throw new ArgumentNullException ( "timer" ) ;
}
ProgramInfo programInfo = null ;
LiveTv Program programInfo = null ;
if ( ! string . IsNullOrWhiteSpace ( timer . ProgramId ) )
{
programInfo = GetProgramInfoFromCache ( timer .ChannelId , timer . ProgramId );
programInfo = GetProgramInfoFromCache ( timer );
}
if ( programInfo = = null )
{
@ -1478,8 +1419,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if ( programInfo ! = null )
{
RecordingHelper. CopyProgramInfoToTimerInfo( programInfo , timer ) ;
activeRecordingInfo . Program = programInfo ;
CopyProgramInfoToTimerInfo( programInfo , timer ) ;
//activeRecordingInfo.Program = programInfo ;
}
string seriesPath = null ;
@ -2336,18 +2277,49 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
private ProgramInfo GetProgramInfoFromCache ( string channelId , string programId )
private LiveTvProgram GetProgramInfoFromCache ( string programId )
{
var query = new InternalItemsQuery
{
ItemIds = new [ ] { _liveTvManager . GetInternalProgramId ( Name , programId ) . ToString ( "N" ) } ,
Limit = 1 ,
DtoOptions = new DtoOptions ( )
} ;
return _libraryManager . GetItemList ( query ) . Cast < LiveTvProgram > ( ) . FirstOrDefault ( ) ;
}
private LiveTvProgram GetProgramInfoFromCache ( TimerInfo timer )
{
var epgData = GetEpgDataForChannel ( channelId ) ;
return epgData . FirstOrDefault ( p = > string . Equals ( p . Id , programId , StringComparison . OrdinalIgnoreCase ) ) ;
return GetProgramInfoFromCache ( timer . ProgramId , timer . ChannelId ) ;
}
private ProgramInfo GetProgramInfoFromCache ( string channelId , DateTime startDateUtc )
private LiveTv Program GetProgramInfoFromCache ( string programId, string channelId )
{
var epgData = GetEpgDataForChannel ( channelId ) ;
var startDateTicks = startDateUtc . Ticks ;
// Find the first program that starts within 3 minutes
return epgData . FirstOrDefault ( p = > Math . Abs ( startDateTicks - p . StartDate . Ticks ) < = TimeSpan . FromMinutes ( 3 ) . Ticks ) ;
return GetProgramInfoFromCache ( programId ) ;
}
private LiveTvProgram GetProgramInfoFromCache ( string channelId , DateTime startDateUtc )
{
var query = new InternalItemsQuery
{
IncludeItemTypes = new string [ ] { typeof ( LiveTvProgram ) . Name } ,
Limit = 1 ,
DtoOptions = new DtoOptions ( true )
{
EnableImages = false
} ,
MinStartDate = startDateUtc . AddMinutes ( - 3 ) ,
MaxStartDate = startDateUtc . AddMinutes ( 3 ) ,
OrderBy = new [ ] { new Tuple < string , SortOrder > ( ItemSortBy . StartDate , SortOrder . Ascending ) }
} ;
if ( ! string . IsNullOrWhiteSpace ( channelId ) )
{
query . ChannelIds = new [ ] { _liveTvManager . GetInternalChannelId ( Name , channelId ) . ToString ( "N" ) } ;
}
return _libraryManager . GetItemList ( query ) . Cast < LiveTvProgram > ( ) . FirstOrDefault ( ) ;
}
private LiveTvOptions GetConfiguration ( )
@ -2421,9 +2393,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
private async Task UpdateTimersForSeriesTimer ( List< ProgramInfo > epgData , SeriesTimerInfo seriesTimer , bool updateTimerSettings , bool deleteInvalidTimers )
private async Task UpdateTimersForSeriesTimer ( SeriesTimerInfo seriesTimer , bool updateTimerSettings , bool deleteInvalidTimers )
{
var allTimers = GetTimersForSeries ( seriesTimer , epgData )
var allTimers = GetTimersForSeries ( seriesTimer )
. ToList ( ) ;
var registration = await _liveTvManager . GetRegistrationInfo ( "seriesrecordings" ) . ConfigureAwait ( false ) ;
@ -2520,23 +2492,160 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
private IEnumerable < TimerInfo > GetTimersForSeries ( SeriesTimerInfo seriesTimer , IEnumerable < ProgramInfo > allPrograms )
private IEnumerable < TimerInfo > GetTimersForSeries ( SeriesTimerInfo seriesTimer )
{
if ( seriesTimer = = null )
{
throw new ArgumentNullException ( "seriesTimer" ) ;
}
if ( allPrograms = = null )
if ( string . IsNullOrWhiteSpace ( seriesTimer . SeriesId ) )
{
return new List < TimerInfo > ( ) ;
}
var query = new InternalItemsQuery
{
IncludeItemTypes = new string [ ] { typeof ( LiveTvProgram ) . Name } ,
ExternalSeriesId = seriesTimer . SeriesId ,
DtoOptions = new DtoOptions ( true )
{
EnableImages = false
} ,
MinEndDate = DateTime . UtcNow
} ;
if ( ! seriesTimer . RecordAnyChannel )
{
query . ChannelIds = new [ ] { _liveTvManager . GetInternalChannelId ( Name , seriesTimer . ChannelId ) . ToString ( "N" ) } ;
}
var tempChannelCache = new Dictionary < string , LiveTvChannel > ( ) ;
return _libraryManager . GetItemList ( query ) . Cast < LiveTvProgram > ( ) . Select ( i = > CreateTimer ( i , seriesTimer , tempChannelCache ) ) ;
}
private TimerInfo CreateTimer ( LiveTvProgram parent , SeriesTimerInfo seriesTimer , Dictionary < string , LiveTvChannel > tempChannelCache )
{
string channelId = seriesTimer . RecordAnyChannel ? null : seriesTimer . ChannelId ;
if ( string . IsNullOrWhiteSpace ( channelId ) & & ! string . IsNullOrWhiteSpace ( parent . ChannelId ) )
{
LiveTvChannel channel ;
if ( ! tempChannelCache . TryGetValue ( parent . ChannelId , out channel ) )
{
channel = _libraryManager . GetItemList ( new InternalItemsQuery
{
IncludeItemTypes = new string [ ] { typeof ( LiveTvChannel ) . Name } ,
ItemIds = new [ ] { parent . ChannelId } ,
DtoOptions = new DtoOptions ( )
} ) . Cast < LiveTvChannel > ( ) . FirstOrDefault ( ) ;
if ( channel ! = null & & ! string . IsNullOrWhiteSpace ( channel . ExternalId ) )
{
tempChannelCache [ parent . ChannelId ] = channel ;
}
}
if ( channel ! = null | | tempChannelCache . TryGetValue ( parent . ChannelId , out channel ) )
{
channelId = channel . ExternalId ;
}
}
var timer = new TimerInfo
{
throw new ArgumentNullException ( "allPrograms" ) ;
ChannelId = channelId ,
Id = ( seriesTimer . Id + parent . ExternalId ) . GetMD5 ( ) . ToString ( "N" ) ,
StartDate = parent . StartDate ,
EndDate = parent . EndDate . Value ,
ProgramId = parent . ExternalId ,
PrePaddingSeconds = seriesTimer . PrePaddingSeconds ,
PostPaddingSeconds = seriesTimer . PostPaddingSeconds ,
IsPostPaddingRequired = seriesTimer . IsPostPaddingRequired ,
IsPrePaddingRequired = seriesTimer . IsPrePaddingRequired ,
KeepUntil = seriesTimer . KeepUntil ,
Priority = seriesTimer . Priority ,
Name = parent . Name ,
Overview = parent . Overview ,
SeriesId = parent . ExternalSeriesId ,
SeriesTimerId = seriesTimer . Id ,
ShowId = parent . ShowId
} ;
CopyProgramInfoToTimerInfo ( parent , timer , tempChannelCache ) ;
return timer ;
}
private void CopyProgramInfoToTimerInfo ( LiveTvProgram programInfo , TimerInfo timerInfo )
{
var tempChannelCache = new Dictionary < string , LiveTvChannel > ( ) ;
CopyProgramInfoToTimerInfo ( programInfo , timerInfo , tempChannelCache ) ;
}
private void CopyProgramInfoToTimerInfo ( LiveTvProgram programInfo , TimerInfo timerInfo , Dictionary < string , LiveTvChannel > tempChannelCache )
{
string channelId = null ;
if ( ! string . IsNullOrWhiteSpace ( programInfo . ChannelId ) )
{
LiveTvChannel channel ;
if ( ! tempChannelCache . TryGetValue ( programInfo . ChannelId , out channel ) )
{
channel = _libraryManager . GetItemList ( new InternalItemsQuery
{
IncludeItemTypes = new string [ ] { typeof ( LiveTvChannel ) . Name } ,
ItemIds = new [ ] { programInfo . ChannelId } ,
DtoOptions = new DtoOptions ( )
} ) . Cast < LiveTvChannel > ( ) . FirstOrDefault ( ) ;
if ( channel ! = null & & ! string . IsNullOrWhiteSpace ( channel . ExternalId ) )
{
tempChannelCache [ programInfo . ChannelId ] = channel ;
}
}
if ( channel ! = null | | tempChannelCache . TryGetValue ( programInfo . ChannelId , out channel ) )
{
channelId = channel . ExternalId ;
}
}
// Exclude programs that have already ended
allPrograms = allPrograms . Where ( i = > i . EndDate > DateTime . UtcNow ) ;
timerInfo . Name = programInfo . Name ;
timerInfo . StartDate = programInfo . StartDate ;
timerInfo . EndDate = programInfo . EndDate . Value ;
allPrograms = GetProgramsForSeries ( seriesTimer , allPrograms ) ;
if ( ! string . IsNullOrWhiteSpace ( channelId ) )
{
timerInfo . ChannelId = channelId ;
}
return allPrograms . Select ( i = > RecordingHelper . CreateTimer ( i , seriesTimer ) ) ;
timerInfo . SeasonNumber = programInfo . ParentIndexNumber ;
timerInfo . EpisodeNumber = programInfo . IndexNumber ;
timerInfo . IsMovie = programInfo . IsMovie ;
timerInfo . IsKids = programInfo . IsKids ;
timerInfo . IsNews = programInfo . IsNews ;
timerInfo . IsSports = programInfo . IsSports ;
timerInfo . ProductionYear = programInfo . ProductionYear ;
timerInfo . EpisodeTitle = programInfo . EpisodeTitle ;
timerInfo . OriginalAirDate = programInfo . PremiereDate ;
timerInfo . IsProgramSeries = programInfo . IsSeries ;
timerInfo . IsSeries = programInfo . IsSeries ;
timerInfo . IsLive = programInfo . IsLive ;
timerInfo . IsPremiere = programInfo . IsPremiere ;
timerInfo . HomePageUrl = programInfo . HomePageUrl ;
timerInfo . CommunityRating = programInfo . CommunityRating ;
timerInfo . Overview = programInfo . Overview ;
timerInfo . OfficialRating = programInfo . OfficialRating ;
timerInfo . IsRepeat = programInfo . IsRepeat ;
timerInfo . SeriesId = programInfo . ExternalSeriesId ;
}
private bool IsProgramAlreadyInLibrary ( TimerInfo program )
@ -2577,51 +2686,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return false ;
}
private IEnumerable < ProgramInfo > GetProgramsForSeries ( SeriesTimerInfo seriesTimer , IEnumerable < ProgramInfo > allPrograms )
{
if ( string . IsNullOrWhiteSpace ( seriesTimer . SeriesId ) )
{
_logger . Error ( "seriesTimer.SeriesId is null. Cannot find programs for series" ) ;
return new List < ProgramInfo > ( ) ;
}
return allPrograms . Where ( i = > string . Equals ( i . SeriesId , seriesTimer . SeriesId , StringComparison . OrdinalIgnoreCase ) ) ;
}
private string GetChannelEpgCachePath ( string channelId )
{
return Path . Combine ( _config . CommonApplicationPaths . CachePath , "embytvepg" , channelId + ".json" ) ;
}
private readonly object _epgLock = new object ( ) ;
private void SaveEpgDataForChannel ( string channelId , List < ProgramInfo > epgData )
{
var path = GetChannelEpgCachePath ( channelId ) ;
_fileSystem . CreateDirectory ( _fileSystem . GetDirectoryName ( path ) ) ;
lock ( _epgLock )
{
_jsonSerializer . SerializeToFile ( epgData , path ) ;
}
}
private List < ProgramInfo > GetEpgDataForChannel ( string channelId )
{
try
{
lock ( _epgLock )
{
return _jsonSerializer . DeserializeFromFile < List < ProgramInfo > > ( GetChannelEpgCachePath ( channelId ) ) ;
}
}
catch
{
return new List < ProgramInfo > ( ) ;
}
}
private List < ProgramInfo > GetEpgDataForChannels ( List < string > channelIds )
{
return channelIds . SelectMany ( GetEpgDataForChannel ) . ToList ( ) ;
}
private bool _disposed ;
public void Dispose ( )
{