@ -16,8 +16,6 @@ using System.Linq;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Model.Extensions ;
using MediaBrowser.Controller.Entities.TV ;
using MediaBrowser.Controller.Providers ;
using MediaBrowser.Model.Entities ;
namespace Emby.Server.Implementations.LiveTv.Listings
@ -32,9 +30,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private const string ApiUrl = "https://json.schedulesdirect.org/20141201" ;
private readonly Dictionary < string , Dictionary < string , ScheduleDirect . Station > > _channelPairingCache =
new Dictionary < string , Dictionary < string , ScheduleDirect . Station > > ( StringComparer . OrdinalIgnoreCase ) ;
public SchedulesDirect ( ILogger logger , IJsonSerializer jsonSerializer , IHttpClient httpClient , IApplicationHost appHost )
{
_logger = logger ;
@ -74,33 +69,29 @@ namespace Emby.Server.Implementations.LiveTv.Listings
// Normalize incoming input
channelId = channelId . Replace ( ".json.schedulesdirect.org" , string . Empty , StringComparison . OrdinalIgnoreCase ) . TrimStart ( 'I' ) ;
List < ProgramInfo > programsInfo = new List < ProgramInfo > ( ) ;
var token = await GetToken ( info , cancellationToken ) . ConfigureAwait ( false ) ;
if ( string . IsNullOrEmpty ( token ) )
{
_logger . LogWarning ( "SchedulesDirect token is empty, returning empty program list" ) ;
return programsInfo ;
return Enumerable . Empty < ProgramInfo > ( ) ;
}
var dates = GetScheduleRequestDates ( startDateUtc , endDateUtc ) ;
string stationID = channelId ;
_logger . LogInformation ( "Channel Station ID is: " + stationID ) ;
List < ScheduleDirect . RequestScheduleForChannel > requestList =
new List < ScheduleDirect . RequestScheduleForChannel > ( )
_logger . LogInformation ( "Channel Station ID is: {ChannelID}" , channelId ) ;
var requestList = new List < ScheduleDirect . RequestScheduleForChannel > ( )
{
new ScheduleDirect . RequestScheduleForChannel ( )
{
new ScheduleDirect . RequestScheduleForChannel ( )
{
stationID = stationID ,
date = dates
}
} ;
stationID = channelId ,
date = dates
}
} ;
var requestString = _jsonSerializer . SerializeToString ( requestList ) ;
_logger . LogDebug ( "Request string for schedules is: " + requestString ) ;
_logger . LogDebug ( "Request string for schedules is: {RequestString}", requestString ) ;
var httpOptions = new HttpRequestOptions ( )
{
@ -109,16 +100,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken = cancellationToken ,
// The data can be large so give it some extra time
TimeoutMs = 60000 ,
LogErrorResponseBody = true
LogErrorResponseBody = true ,
RequestContent = requestString
} ;
httpOptions . RequestHeaders [ "token" ] = token ;
httpOptions . RequestContent = requestString ;
using ( var response = await Post ( httpOptions , true , info ) . ConfigureAwait ( false ) )
using ( StreamReader reader = new StreamReader ( response . Content ) )
{
var dailySchedules = await _jsonSerializer . DeserializeFromStreamAsync < List < ScheduleDirect . Day > > ( response . Content ) . ConfigureAwait ( false ) ;
_logger . LogDebug ( "Found {ScheduleCount} programs on { StationID} ScheduleDirect", dailySchedules . Count , stationID ) ;
_logger . LogDebug ( "Found {ScheduleCount} programs on { ChannelID} ScheduleDirect", dailySchedules . Count , channelId ) ;
httpOptions = new HttpRequestOptions ( )
{
@ -132,14 +124,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions . RequestHeaders [ "token" ] = token ;
List < string > programsID = new List < string > ( ) ;
programsID = dailySchedules . SelectMany ( d = > d . programs . Select ( s = > s . programID ) ) . Distinct ( ) . ToList ( ) ;
var requestBody = "[\"" + string . Join ( "\", \"" , programsID ) + "\"]" ;
httpOptions . RequestContent = requestBody ;
double wideAspect = 1.77777778 ;
var programsID = dailySchedules . SelectMany ( d = > d . programs . Select ( s = > s . programID ) ) . Distinct ( ) ;
httpOptions . RequestContent = "[\"" + string . Join ( "\", \"" , programsID ) + "\"]" ;
using ( var innerResponse = await Post ( httpOptions , true , info ) . ConfigureAwait ( false ) )
using ( StreamReader innerReader = new StreamReader ( innerResponse . Content ) )
{
var programDetails = await _jsonSerializer . DeserializeFromStreamAsync < List < ScheduleDirect . ProgramDetails > > ( innerResponse . Content ) . ConfigureAwait ( false ) ;
var programDict = programDetails . ToDictionary ( p = > p . programID , y = > y ) ;
@ -150,8 +139,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var images = await GetImageForPrograms ( info , programIdsWithImages , cancellationToken ) . ConfigureAwait ( false ) ;
var schedules = dailySchedules . SelectMany ( d = > d . programs ) ;
foreach ( ScheduleDirect . Program schedule in schedules )
List < ProgramInfo > programsInfo = new List < ProgramInfo > ( ) ;
foreach ( ScheduleDirect . Program schedule in dailySchedules. SelectMany ( d = > d . programs ) )
{
//_logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
// " which corresponds to channel " + channelNumber + " and program id " +
@ -165,15 +154,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
var programEntry = programDict [ schedule . programID ] ;
var allImages = ( images [ imageIndex ] . data ? ? new List < ScheduleDirect . ImageData > ( ) ) . ToList ( ) ;
var imagesWithText = allImages . Where ( i = > string . Equals ( i . text , "yes" , StringComparison . OrdinalIgnoreCase ) ) .ToList ( ) ;
var imagesWithoutText = allImages . Where ( i = > string . Equals ( i . text , "no" , StringComparison . OrdinalIgnoreCase ) ) .ToList ( ) ;
var allImages = images [ imageIndex ] . data ? ? new List < ScheduleDirect . ImageData > ( ) ;
var imagesWithText = allImages . Where ( i = > string . Equals ( i . text , "yes" , StringComparison . OrdinalIgnoreCase ) ) ;
var imagesWithoutText = allImages . Where ( i = > string . Equals ( i . text , "no" , StringComparison . OrdinalIgnoreCase ) ) ;
double desiredAspect = 0.666666667 ;
const double desiredAspect = 0.666666667 ;
programEntry . primaryImage = GetProgramImage ( ApiUrl , imagesWithText , true , desiredAspect ) ? ?
GetProgramImage ( ApiUrl , allImages , true , desiredAspect ) ;
const double wideAspect = 1.77777778 ;
programEntry . thumbImage = GetProgramImage ( ApiUrl , imagesWithText , true , wideAspect ) ;
// Don't supply the same image twice
@ -193,18 +184,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programsInfo . Add ( GetProgram ( channelId , schedule , programDict [ schedule . programID ] ) ) ;
}
return programsInfo ;
}
}
return programsInfo ;
}
private int GetSizeOrder ( ScheduleDirect . ImageData image )
{
if ( ! string . IsNullOrWhiteSpace ( image . height ) )
{
int value ;
if ( int . TryParse ( image . height , out value ) )
if ( int . TryParse ( image . height , out int value ) )
{
return value ;
}
@ -225,9 +214,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
channelNumber = map . atscMajor + "." + map . atscMinor ;
}
channelNumber = channelNumber . TrimStart ( '0' ) ;
return channelNumber ;
return channelNumber .TrimStart ( '0' ) ;
}
private bool IsMovie ( ScheduleDirect . ProgramDetails programInfo )
@ -382,8 +370,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if ( details . movie ! = null )
{
int year ;
if ( ! string . IsNullOrEmpty ( details . movie . year ) & & int . TryParse ( details . movie . year , out year ) )
if ( ! string . IsNullOrEmpty ( details . movie . year ) & & int . TryParse ( details . movie . year , out int year ) )
{
info . ProductionYear = year ;
}
@ -414,18 +401,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return date ;
}
private string GetProgramImage ( string apiUrl , List < ScheduleDirect . ImageData > images , bool returnDefaultImage , double desiredAspect )
private string GetProgramImage ( string apiUrl , IEnumerable < ScheduleDirect . ImageData > images , bool returnDefaultImage , double desiredAspect )
{
string url = null ;
var matches = images ;
matches = matches
. OrderBy ( i = > Math . Abs ( desiredAspect - GetApsectRatio ( i ) ) )
var match = images
. OrderBy ( i = > Math . Abs ( desiredAspect - GetAspectRatio ( i ) ) )
. ThenByDescending ( GetSizeOrder )
. ToList ( ) ;
var match = matches . FirstOrDefault ( ) ;
. FirstOrDefault ( ) ;
if ( match = = null )
{
@ -434,22 +415,21 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var uri = match . uri ;
if ( ! string . IsNullOrWhiteSpace ( uri ) )
if ( string . IsNullOrWhiteSpace ( uri ) )
{
if ( uri . IndexOf ( "http" , StringComparison . OrdinalIgnoreCase ) ! = - 1 )
{
url = uri ;
}
else
{
url = apiUrl + "/image/" + uri ;
}
return null ;
}
else if ( uri . IndexOf ( "http" , StringComparison . OrdinalIgnoreCase ) ! = - 1 )
{
return uri ;
}
else
{
return apiUrl + "/image/" + uri ;
}
//_logger.LogDebug("URL for image is : " + url);
return url ;
}
private double GetA p sectRatio( ScheduleDirect . ImageData i )
private double GetA sp ectRatio( ScheduleDirect . ImageData i )
{
int width = 0 ;
int height = 0 ;
@ -614,8 +594,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if ( ! string . IsNullOrEmpty ( savedToken . Name ) & & ! string . IsNullOrEmpty ( savedToken . Value ) )
{
long ticks ;
if ( long . TryParse ( savedToken . Value , NumberStyles . Any , CultureInfo . InvariantCulture , out ticks ) )
if ( long . TryParse ( savedToken . Value , NumberStyles . Any , CultureInfo . InvariantCulture , out long ticks ) )
{
// If it's under 24 hours old we can still use it
if ( DateTime . UtcNow . Ticks - ticks < TimeSpan . FromHours ( 20 ) . Ticks )
@ -685,8 +664,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
var newToken = await GetToken ( providerInfo , options . CancellationToken ) . ConfigureAwait ( false ) ;
options . RequestHeaders [ "token" ] = newToken ;
options . RequestHeaders [ "token" ] = await GetToken ( providerInfo , options . CancellationToken ) . ConfigureAwait ( false ) ; ;
return await Post ( options , false , providerInfo ) . ConfigureAwait ( false ) ;
}
@ -724,8 +702,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
var newToken = await GetToken ( providerInfo , options . CancellationToken ) . ConfigureAwait ( false ) ;
options . RequestHeaders [ "token" ] = newToken ;
options . RequestHeaders [ "token" ] = await GetToken ( providerInfo , options . CancellationToken ) . ConfigureAwait ( false ) ;
return await Get ( options , false , providerInfo ) . ConfigureAwait ( false ) ;
}
@ -743,9 +720,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
//_logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
// httpOptions.RequestContent);
using ( var respon c e = await Post ( httpOptions , false , null ) . ConfigureAwait ( false ) )
using ( var respon s e = await Post ( httpOptions , false , null ) . ConfigureAwait ( false ) )
{
var root = await _jsonSerializer . DeserializeFromStreamAsync < ScheduleDirect . Token > ( respon c e. Content ) . ConfigureAwait ( false ) ;
var root = await _jsonSerializer . DeserializeFromStreamAsync < ScheduleDirect . Token > ( respon s e. Content ) . ConfigureAwait ( false ) ;
if ( root . message = = "OK" )
{
_logger . LogInformation ( "Authenticated with Schedules Direct token: " + root . token ) ;
@ -828,13 +805,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
try
{
using ( var httpResponse = await Get ( options , false , null ) . ConfigureAwait ( false ) )
using ( var response = httpResponse . Content )
{
using ( var response = httpResponse . Content )
{
var root = await _jsonSerializer . DeserializeFromStreamAsync < ScheduleDirect . Lineups > ( response ) . ConfigureAwait ( false ) ;
var root = await _jsonSerializer . DeserializeFromStreamAsync < ScheduleDirect . Lineups > ( response ) . ConfigureAwait ( false ) ;
return root . lineups . Any ( i = > string . Equals ( info . ListingsId , i . lineup , StringComparison . OrdinalIgnoreCase ) ) ;
}
return root . lineups . Any ( i = > string . Equals ( info . ListingsId , i . lineup , StringComparison . OrdinalIgnoreCase ) ) ;
}
}
catch ( HttpException ex )
@ -913,54 +888,41 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var list = new List < ChannelInfo > ( ) ;
using ( var httpResponse = await Get ( httpOptions , true , info ) . ConfigureAwait ( false ) )
using ( var response = httpResponse . Content )
{
using ( var response = httpResponse . Content )
{
var root = await _jsonSerializer . DeserializeFromStreamAsync < ScheduleDirect . Channel > ( response ) . ConfigureAwait ( false ) ;
_logger . LogInformation ( "Found " + root . map . Count + " channels on the lineup on ScheduleDirect" ) ;
_logger . LogInformation ( "Mapping Stations to Channel" ) ;
var allStations = root . stations ? ? new List < ScheduleDirect . Station > ( ) ;
var root = await _jsonSerializer . DeserializeFromStreamAsync < ScheduleDirect . Channel > ( response ) . ConfigureAwait ( false ) ;
_logger . LogInformation ( "Found {ChannelCount} channels on the lineup on ScheduleDirect" , root . map . Count ) ;
_logger . LogInformation ( "Mapping Stations to Channel" ) ;
foreach ( ScheduleDirect . Map map in root . map )
{
var channelNumber = GetChannelNumber ( map ) ;
var allStations = root . stations ? ? Enumerable . Empty < ScheduleDirect . Station > ( ) ;
var station = allStations . FirstOrDefault ( item = > string . Equals ( item . stationID , map . stationID , StringComparison . OrdinalIgnoreCase ) ) ;
if ( station = = null )
{
station = new ScheduleDirect . Station
{
stationID = map . stationID
} ;
}
var name = channelNumber ;
foreach ( ScheduleDirect . Map map in root . map )
{
var channelNumber = GetChannelNumber ( map ) ;
var channelInfo = new ChannelInfo
var station = allStations . FirstOrDefault ( item = > string . Equals ( item . stationID , map . stationID , StringComparison . OrdinalIgnoreCase ) ) ;
if ( station = = null )
{
station = new ScheduleDirect . Station
{
Number = channelNumber ,
Name = name
stationID = map . stationID
} ;
}
if ( station ! = null )
{
if ( ! string . IsNullOrWhiteSpace ( station . name ) )
{
channelInfo . Name = station . name ;
}
channelInfo . Id = station . stationID ;
channelInfo . CallSign = station . callsign ;
if ( station . logo ! = null )
{
channelInfo . ImageUrl = station . logo . URL ;
}
}
var channelInfo = new ChannelInfo
{
Id = station . stationID ,
CallSign = station . callsign ,
Number = channelNumber ,
Name = string . IsNullOrWhiteSpace ( station . name ) ? channelNumber : station . name
} ;
list . Add ( channelInfo ) ;
if ( station . logo ! = null )
{
channelInfo . ImageUrl = station . logo . URL ;
}
list . Add ( channelInfo ) ;
}
}