2011-03-06 22:45:35 +02:00
using System ;
2011-07-09 21:19:33 +03:00
using System.Collections.Generic ;
2011-03-06 23:36:09 +02:00
using System.IO ;
2011-03-06 22:45:35 +02:00
using System.Linq ;
2011-07-09 21:19:33 +03:00
using System.Web.Script.Serialization ;
2011-03-06 23:36:09 +02:00
using System.Xml.Linq ;
2012-01-05 03:41:42 +03:00
using Newtonsoft.Json ;
using Newtonsoft.Json.Linq ;
2011-06-14 04:23:04 +03:00
using Ninject ;
2011-03-06 22:45:35 +02:00
using NLog ;
2011-07-09 21:19:33 +03:00
using NzbDrone.Core.Model.Xbmc ;
2011-04-04 06:50:12 +03:00
using NzbDrone.Core.Providers.Core ;
2011-07-09 21:19:33 +03:00
using NzbDrone.Core.Providers.Xbmc ;
using NzbDrone.Core.Repository ;
2011-03-06 22:45:35 +02:00
namespace NzbDrone.Core.Providers
{
2011-04-08 18:15:51 +03:00
public class XbmcProvider
2011-03-06 22:45:35 +02:00
{
2011-04-10 05:44:01 +03:00
private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
2011-04-10 04:34:36 +03:00
private readonly ConfigProvider _configProvider ;
2011-04-07 05:25:52 +03:00
private readonly HttpProvider _httpProvider ;
2011-07-09 21:19:33 +03:00
private readonly EventClientProvider _eventClientProvider ;
2011-03-06 22:45:35 +02:00
2011-06-14 04:23:04 +03:00
[Inject]
2011-07-09 21:19:33 +03:00
public XbmcProvider ( ConfigProvider configProvider , HttpProvider httpProvider , EventClientProvider eventClientProvider )
2011-03-06 22:45:35 +02:00
{
_configProvider = configProvider ;
2011-03-07 08:33:59 +02:00
_httpProvider = httpProvider ;
2011-07-09 21:19:33 +03:00
_eventClientProvider = eventClientProvider ;
2011-03-06 22:45:35 +02:00
}
2011-04-08 18:15:51 +03:00
public virtual void Notify ( string header , string message )
2011-03-06 22:45:35 +02:00
{
2011-07-09 21:19:33 +03:00
//Always use EventServer, until Json has real support for it
foreach ( var host in _configProvider . XbmcHosts . Split ( ',' ) )
{
Logger . Trace ( "Sending Notifcation to XBMC Host: {0}" , host ) ;
_eventClientProvider . SendNotification ( header , message , IconType . Jpeg , "NzbDrone.jpg" , GetHostWithoutPort ( host ) ) ;
}
}
public XbmcProvider ( )
{
}
public virtual void Update ( Series series )
{
//Use Json for Eden/Nightly or depricated HTTP for 10.x (Dharma) to get the proper path
//Perform update with EventServer (Json currently doesn't support updating a specific path only - July 2011)
var username = _configProvider . XbmcUsername ;
var password = _configProvider . XbmcPassword ;
2011-03-06 22:45:35 +02:00
2011-07-09 21:19:33 +03:00
foreach ( var host in _configProvider . XbmcHosts . Split ( ',' ) )
2011-03-06 22:45:35 +02:00
{
2011-07-09 21:19:33 +03:00
Logger . Trace ( "Determining version of XBMC Host: {0}" , host ) ;
var version = GetJsonVersion ( host , username , password ) ;
2011-03-06 22:45:35 +02:00
2011-07-09 21:19:33 +03:00
//If Dharma
if ( version = = 2 )
2011-12-31 23:57:02 +03:00
{
2012-01-04 22:48:48 +03:00
//Check for active player only when we should skip updates when playing
if ( ! _configProvider . XbmcUpdateWhenPlaying )
2011-12-31 23:57:02 +03:00
{
2012-01-04 22:48:48 +03:00
Logger . Trace ( "Determining if there are any active players on XBMC host: {0}" , host ) ;
var activePlayers = GetActivePlayersDharma ( host , username , password ) ;
//If video is currently playing, then skip update
if ( activePlayers [ "video" ] )
{
Logger . Debug ( "Video is currently playing, skipping library update" ) ;
continue ;
}
2011-12-31 23:57:02 +03:00
}
2011-07-09 21:19:33 +03:00
UpdateWithHttp ( series , host , username , password ) ;
2011-12-31 23:57:02 +03:00
}
2011-07-09 21:19:33 +03:00
//If Eden or newer (attempting to make it future compatible)
else if ( version > = 3 )
2011-12-31 23:57:02 +03:00
{
2012-01-04 22:48:48 +03:00
//Check for active player only when we should skip updates when playing
if ( ! _configProvider . XbmcUpdateWhenPlaying )
2011-12-31 23:57:02 +03:00
{
2012-01-04 22:48:48 +03:00
Logger . Trace ( "Determining if there are any active players on XBMC host: {0}" , host ) ;
var activePlayers = GetActivePlayersEden ( host , username , password ) ;
//If video is currently playing, then skip update
if ( activePlayers . Any ( a = > a . Type . Equals ( "video" ) ) )
{
Logger . Debug ( "Video is currently playing, skipping library update" ) ;
continue ;
}
2011-12-31 23:57:02 +03:00
}
2012-01-05 04:29:05 +03:00
UpdateWithJson ( series , host , username , password ) ;
2011-12-31 23:57:02 +03:00
}
2012-01-05 03:41:42 +03:00
//Log Version zero if check failed
else
Logger . Trace ( "Unknown version: [{0}], skipping." , version ) ;
2011-03-06 22:45:35 +02:00
}
2011-07-09 21:19:33 +03:00
}
2011-03-06 22:45:35 +02:00
2011-07-09 21:19:33 +03:00
public virtual bool UpdateWithJson ( Series series , string host , string username , string password )
{
try
2011-03-06 23:36:09 +02:00
{
2011-07-09 21:19:33 +03:00
//Use Json!
var xbmcShows = GetTvShowsJson ( host , username , password ) ;
2012-01-06 09:29:02 +03:00
TvShow path = null ;
//Log if response is null, otherwise try to find XBMC's path for series
if ( xbmcShows = = null )
Logger . Trace ( "Failed to get TV Shows from XBMC" ) ;
else
path = xbmcShows . FirstOrDefault ( s = > s . ImdbNumber = = series . SeriesId | | s . Label = = series . Title ) ;
2011-07-09 21:19:33 +03:00
2012-01-10 10:10:53 +03:00
//var hostOnly = GetHostWithoutPort(host);
2011-07-09 21:19:33 +03:00
if ( path ! = null )
{
2012-01-06 09:29:02 +03:00
Logger . Trace ( "Updating series [{0}] (Path: {1}) on XBMC host: {2}" , series . Title , path . File , host ) ;
2012-01-10 10:10:53 +03:00
//var command = String.Format("ExecBuiltIn(UpdateLibrary(video, {0}))", path.File);
//_eventClientProvider.SendAction(hostOnly, ActionType.ExecBuiltin, command);
var command = String . Format ( "ExecBuiltIn(UpdateLibrary(video,{0}))" , path . File ) ;
SendCommand ( host , command , username , password ) ;
2011-07-09 21:19:33 +03:00
}
else
{
Logger . Trace ( "Series [{0}] doesn't exist on XBMC host: {1}, Updating Entire Library" , series . Title , host ) ;
var command = String . Format ( "ExecBuiltIn(UpdateLibrary(video))" ) ;
2012-01-10 10:10:53 +03:00
//_eventClientProvider.SendAction(hostOnly, ActionType.ExecBuiltin, command);
SendCommand ( host , "ExecBuiltIn(UpdateLibrary(video))" , username , password ) ;
2011-07-09 21:19:33 +03:00
}
2011-03-06 23:36:09 +02:00
}
2011-07-09 21:19:33 +03:00
catch ( Exception ex )
{
Logger . DebugException ( ex . Message , ex ) ;
return false ;
}
return true ;
2011-03-06 22:45:35 +02:00
}
2011-07-09 21:19:33 +03:00
public virtual bool UpdateWithHttp ( Series series , string host , string username , string password )
2011-03-06 22:45:35 +02:00
{
2011-07-09 21:19:33 +03:00
try
2011-03-06 23:36:09 +02:00
{
Logger . Trace ( "Sending Update DB Request to XBMC Host: {0}" , host ) ;
2011-07-09 21:19:33 +03:00
var xbmcSeriesPath = GetXbmcSeriesPath ( host , series . SeriesId , username , password ) ;
2011-03-06 23:36:09 +02:00
2011-07-09 21:19:33 +03:00
//If the path is found update it, else update the whole library
if ( ! String . IsNullOrEmpty ( xbmcSeriesPath ) )
{
Logger . Trace ( "Updating series [{0}] on XBMC host: {1}" , series . Title , host ) ;
var command = String . Format ( "ExecBuiltIn(UpdateLibrary(video,{0}))" , xbmcSeriesPath ) ;
SendCommand ( host , command , username , password ) ;
}
else
2011-03-06 23:36:09 +02:00
{
//Update the entire library
2011-07-09 21:19:33 +03:00
Logger . Trace ( "Series [{0}] doesn't exist on XBMC host: {1}, Updating Entire Library" , series . Title , host ) ;
SendCommand ( host , "ExecBuiltIn(UpdateLibrary(video))" , username , password ) ;
2011-03-06 23:36:09 +02:00
}
2011-07-09 21:19:33 +03:00
}
2011-03-06 23:36:09 +02:00
2011-07-09 21:19:33 +03:00
catch ( Exception ex )
{
Logger . DebugException ( ex . Message , ex ) ;
return false ;
2011-03-06 23:36:09 +02:00
}
2011-07-09 21:19:33 +03:00
return true ;
2011-03-06 22:45:35 +02:00
}
2011-04-08 18:15:51 +03:00
public virtual void Clean ( )
2011-03-06 22:45:35 +02:00
{
2011-07-09 21:19:33 +03:00
//Use EventServer, once Dharma is extinct use Json?
foreach ( var host in _configProvider . XbmcHosts . Split ( ',' ) )
2011-03-06 23:36:09 +02:00
{
Logger . Trace ( "Sending DB Clean Request to XBMC Host: {0}" , host ) ;
2011-07-09 21:19:33 +03:00
var command = "ExecBuiltIn(CleanLibrary(video))" ;
_eventClientProvider . SendAction ( GetHostWithoutPort ( host ) , ActionType . ExecBuiltin , command ) ;
2011-03-06 23:36:09 +02:00
}
2011-03-06 22:45:35 +02:00
}
2011-07-09 21:19:33 +03:00
public virtual string SendCommand ( string host , string command , string username , string password )
2011-03-06 22:45:35 +02:00
{
var url = String . Format ( "http://{0}/xbmcCmds/xbmcHttp?command={1}" , host , command ) ;
2011-03-07 08:33:59 +02:00
if ( ! String . IsNullOrEmpty ( username ) )
2011-03-06 22:45:35 +02:00
{
2011-03-07 08:33:59 +02:00
return _httpProvider . DownloadString ( url , username , password ) ;
2011-03-06 22:45:35 +02:00
}
2011-04-10 05:44:01 +03:00
2011-03-07 08:33:59 +02:00
return _httpProvider . DownloadString ( url ) ;
2011-03-06 22:45:35 +02:00
}
2011-03-06 23:36:09 +02:00
2011-07-09 21:19:33 +03:00
public virtual string GetXbmcSeriesPath ( string host , int seriesId , string username , string password )
2011-03-06 23:36:09 +02:00
{
2011-04-10 05:44:01 +03:00
var query =
String . Format (
"select path.strPath from path, tvshow, tvshowlinkpath where tvshow.c12 = {0} and tvshowlinkpath.idShow = tvshow.idShow and tvshowlinkpath.idPath = path.idPath" ,
seriesId ) ;
2011-03-06 23:36:09 +02:00
var command = String . Format ( "QueryVideoDatabase({0})" , query ) ;
2011-07-09 21:19:33 +03:00
const string setResponseCommand =
2011-04-10 05:44:01 +03:00
"SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;opentag;<tag>;closetag;</tag>;closefinaltag;false)" ;
2011-07-09 21:19:33 +03:00
const string resetResponseCommand = "SetResponseFormat()" ;
2011-03-06 23:36:09 +02:00
2011-07-09 21:19:33 +03:00
SendCommand ( host , setResponseCommand , username , password ) ;
var response = SendCommand ( host , command , username , password ) ;
SendCommand ( host , resetResponseCommand , username , password ) ;
2011-03-06 23:36:09 +02:00
if ( String . IsNullOrEmpty ( response ) )
return String . Empty ;
2011-09-29 07:39:05 +03:00
var xDoc = XDocument . Load ( new StringReader ( response . Replace ( "&" , "&" ) ) ) ;
2011-03-06 23:36:09 +02:00
var xml = ( from x in xDoc . Descendants ( "xml" ) select x ) . FirstOrDefault ( ) ;
if ( xml = = null )
return String . Empty ;
var field = xml . Descendants ( "field" ) . FirstOrDefault ( ) ;
if ( field = = null )
return String . Empty ;
return field . Value ;
}
2011-07-09 21:19:33 +03:00
public virtual int GetJsonVersion ( string host , string username , string password )
{
//2 = Dharma
//3 = Eden/Nightly (as of July 2011)
var version = 0 ;
try
{
2012-01-05 03:41:42 +03:00
var postJson = new JObject ( ) ;
postJson . Add ( new JProperty ( "jsonrpc" , "2.0" ) ) ;
postJson . Add ( new JProperty ( "method" , "JSONRPC.Version" ) ) ;
postJson . Add ( new JProperty ( "id" , 10 ) ) ;
var response = _httpProvider . PostCommand ( host , username , password , postJson . ToString ( ) ) ;
2011-07-09 21:19:33 +03:00
if ( CheckForJsonError ( response ) )
return version ;
2011-09-27 03:17:41 +03:00
Logger . Trace ( "Getting version from response" ) ;
2012-01-05 03:41:42 +03:00
var result = JsonConvert . DeserializeObject < VersionResult > ( response ) ;
2011-07-09 21:19:33 +03:00
result . Result . TryGetValue ( "version" , out version ) ;
}
catch ( Exception ex )
{
Logger . DebugException ( ex . Message , ex ) ;
}
return version ;
}
2011-12-31 23:57:02 +03:00
public virtual Dictionary < string , bool > GetActivePlayersDharma ( string host , string username , string password )
2011-07-09 21:19:33 +03:00
{
2011-12-31 23:57:02 +03:00
try
{
2012-01-05 03:41:42 +03:00
var postJson = new JObject ( ) ;
postJson . Add ( new JProperty ( "jsonrpc" , "2.0" ) ) ;
postJson . Add ( new JProperty ( "method" , "Player.GetActivePlayers" ) ) ;
postJson . Add ( new JProperty ( "id" , 10 ) ) ;
var response = _httpProvider . PostCommand ( host , username , password , postJson . ToString ( ) ) ;
2011-12-31 23:57:02 +03:00
if ( CheckForJsonError ( response ) )
return null ;
2012-01-05 03:41:42 +03:00
var result = JsonConvert . DeserializeObject < ActivePlayersDharmaResult > ( response ) ;
2011-12-31 23:57:02 +03:00
return result . Result ;
}
2011-07-09 21:19:33 +03:00
2011-12-31 23:57:02 +03:00
catch ( Exception ex )
{
Logger . DebugException ( ex . Message , ex ) ;
}
return null ;
}
public virtual List < ActivePlayer > GetActivePlayersEden ( string host , string username , string password )
{
2011-07-09 21:19:33 +03:00
try
{
2012-01-05 03:41:42 +03:00
var postJson = new JObject ( ) ;
postJson . Add ( new JProperty ( "jsonrpc" , "2.0" ) ) ;
postJson . Add ( new JProperty ( "method" , "Player.GetActivePlayers" ) ) ;
postJson . Add ( new JProperty ( "id" , 10 ) ) ;
var response = _httpProvider . PostCommand ( host , username , password , postJson . ToString ( ) ) ;
2011-07-09 21:19:33 +03:00
if ( CheckForJsonError ( response ) )
return null ;
2012-01-05 03:41:42 +03:00
var result = JsonConvert . DeserializeObject < ActivePlayersEdenResult > ( response ) ;
2011-07-09 21:19:33 +03:00
return result . Result ;
}
catch ( Exception ex )
{
Logger . DebugException ( ex . Message , ex ) ;
}
return null ;
}
public virtual List < TvShow > GetTvShowsJson ( string host , string username , string password )
{
try
{
2012-01-05 03:41:42 +03:00
var postJson = new JObject ( ) ;
postJson . Add ( new JProperty ( "jsonrpc" , "2.0" ) ) ;
postJson . Add ( new JProperty ( "method" , "VideoLibrary.GetTvShows" ) ) ;
postJson . Add ( new JProperty ( "params" , new JObject { new JProperty ( "properties" , new string [ ] { "file" , "imdbnumber" } ) } ) ) ;
postJson . Add ( new JProperty ( "id" , 10 ) ) ;
var response = _httpProvider . PostCommand ( host , username , password , postJson . ToString ( ) ) ;
2011-07-09 21:19:33 +03:00
if ( CheckForJsonError ( response ) )
return null ;
2012-01-05 03:41:42 +03:00
var result = JsonConvert . DeserializeObject < TvShowResponse > ( response ) ;
var shows = result . Result . TvShows ;
2011-07-09 21:19:33 +03:00
return shows ;
}
catch ( Exception ex )
{
Logger . DebugException ( ex . Message , ex ) ;
}
return null ;
}
public virtual bool CheckForJsonError ( string response )
{
2011-09-27 03:17:41 +03:00
Logger . Trace ( "Looking for error in response: {0}" , response ) ;
2011-07-09 21:19:33 +03:00
if ( response . StartsWith ( "{\"error\"" ) )
{
var serializer = new JavaScriptSerializer ( ) ;
var error = serializer . Deserialize < ErrorResult > ( response ) ;
var code = error . Error [ "code" ] ;
var message = error . Error [ "message" ] ;
Logger . Debug ( "XBMC Json Error. Code = {0}, Message: {1}" , code , message ) ;
return true ;
}
2011-09-27 20:41:36 +03:00
if ( String . IsNullOrWhiteSpace ( response ) )
{
Logger . Debug ( "Invalid response from XBMC, the response is not valid JSON" ) ;
return true ;
}
2011-07-09 21:19:33 +03:00
return false ;
}
private string GetHostWithoutPort ( string address )
{
return address . Split ( ':' ) [ 0 ] ;
}
2011-03-06 22:45:35 +02:00
}
2011-04-10 05:44:01 +03:00
}