XbmcProvider updated to include new Json API methods.

EventClient is used for sending CleanLibrary and Notifications (With NzbDrone Logo - Internal Resource).
Support for Dharma's HTTP Server (Deprecated), since Dharma doesn't support Json as well.
This commit is contained in:
Mark McDowall 2011-07-09 11:19:33 -07:00
parent 5bbc9a6f59
commit 348ff5a386
26 changed files with 1312 additions and 158 deletions

View File

@ -0,0 +1,103 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using AutoMoq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.Xbmc;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class EventClientProviderTest : TestBase
{
[Test]
public void SendNotification_true()
{
//Setup
var mocker = new AutoMoqer();
var header = "NzbDrone Test";
var message = "Test Message!";
var address = "localhost";
var fakeUdp = mocker.GetMock<UdpProvider>();
fakeUdp.Setup(s => s.Send(address, UdpProvider.PacketType.Notification, It.IsAny<byte[]>())).Returns(true);
//Act
var result = mocker.Resolve<EventClientProvider>().SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", address);
//Assert
Assert.AreEqual(true, result);
}
[Test]
public void SendNotification_false()
{
//Setup
var mocker = new AutoMoqer();
var header = "NzbDrone Test";
var message = "Test Message!";
var address = "localhost";
var fakeUdp = mocker.GetMock<UdpProvider>();
fakeUdp.Setup(s => s.Send(address, UdpProvider.PacketType.Notification, It.IsAny<byte[]>())).Returns(false);
//Act
var result = mocker.Resolve<EventClientProvider>().SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", address);
//Assert
Assert.AreEqual(false, result);
}
[Test]
public void SendAction_Update_true()
{
//Setup
var mocker = new AutoMoqer();
var path = @"C:\Test\TV\30 Rock";
var command = String.Format("ExecBuiltIn(UpdateLibrary(video,{0}))", path);
var address = "localhost";
var fakeUdp = mocker.GetMock<UdpProvider>();
fakeUdp.Setup(s => s.Send(address, UdpProvider.PacketType.Action, It.IsAny<byte[]>())).Returns(true);
//Act
var result = mocker.Resolve<EventClientProvider>().SendAction(address, ActionType.ExecBuiltin, command);
//Assert
Assert.AreEqual(true, result);
}
[Test]
public void SendAction_Update_false()
{
//Setup
var mocker = new AutoMoqer();
var path = @"C:\Test\TV\30 Rock";
var command = String.Format("ExecBuiltIn(UpdateLibrary(video,{0}))", path);
var address = "localhost";
var fakeUdp = mocker.GetMock<UdpProvider>();
fakeUdp.Setup(s => s.Send(address, UdpProvider.PacketType.Action, It.IsAny<byte[]>())).Returns(false);
//Act
var result = mocker.Resolve<EventClientProvider>().SendAction(address, ActionType.ExecBuiltin, command);
//Assert
Assert.AreEqual(false, result);
}
}
}

View File

@ -85,6 +85,8 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="EventClientProviderTest.cs" />
<Compile Include="XbmcProviderTest.cs" />
<Compile Include="DiskScanProviderTest.cs" /> <Compile Include="DiskScanProviderTest.cs" />
<Compile Include="EpisodeProviderTest_GetEpisodesByParseResult.cs" /> <Compile Include="EpisodeProviderTest_GetEpisodesByParseResult.cs" />
<Compile Include="DiskScanProviderTest_ImportFile.cs" /> <Compile Include="DiskScanProviderTest_ImportFile.cs" />

View File

@ -0,0 +1,399 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using AutoMoq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.Xbmc;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class XbmcProviderTest : TestBase
{
[Test]
public void JsonEror_true()
{
//Setup
var mocker = new AutoMoqer();
var response = "{\"error\":{\"code\":-32601,\"message\":\"Method not found.\"},\"id\":10,\"jsonrpc\":\"2.0\"}";
//Act
var result = mocker.Resolve<XbmcProvider>().CheckForJsonError(response);
//Assert
Assert.AreEqual(true, result);
}
[Test]
public void JsonEror_false()
{
//Setup
var mocker = new AutoMoqer();
var reposnse = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":3}}";
//Act
var result = mocker.Resolve<XbmcProvider>().CheckForJsonError(reposnse);
//Assert
Assert.AreEqual(false, result);
}
[TestCase(3)]
[TestCase(2)]
[TestCase(0)]
public void GetJsonVersion(int number)
{
//Setup
var mocker = new AutoMoqer();
var message = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":" + number + "}}";
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
//Act
var result = mocker.Resolve<XbmcProvider>().GetJsonVersion("localhost:8080", "xbmc", "xbmc");
//Assert
Assert.AreEqual(number, result);
}
[Test]
public void GetJsonVersion_error()
{
//Setup
var mocker = new AutoMoqer();
var message = "{\"error\":{\"code\":-32601,\"message\":\"Method not found.\"},\"id\":10,\"jsonrpc\":\"2.0\"}";
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
//Act
var result = mocker.Resolve<XbmcProvider>().GetJsonVersion("localhost:8080", "xbmc", "xbmc");
//Assert
Assert.AreEqual(0, result);
}
[TestCase(false, false, false)]
[TestCase(true, true, true)]
[TestCase(true, false, false)]
[TestCase(true, true, false)]
[TestCase(false, true, false)]
[TestCase(false, true, true)]
[TestCase(false, false, true)]
[TestCase(true, false, true)]
public void GetActivePlayers(bool audio, bool picture, bool video)
{
//Setup
var mocker = new AutoMoqer();
var message = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"audio\":"
+ audio.ToString().ToLower()
+ ",\"picture\":"
+ picture.ToString().ToLower()
+ ",\"video\":"
+ video.ToString().ToLower()
+ "}}";
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
//Act
var result = mocker.Resolve<XbmcProvider>().GetActivePlayers("localhost:8080", "xbmc", "xbmc");
//Assert
Assert.AreEqual(audio, result["audio"]);
Assert.AreEqual(picture, result["picture"]);
Assert.AreEqual(video, result["video"]);
}
[Test]
public void GetTvShowsJson()
{
//Setup
var mocker = new AutoMoqer();
var message = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"limits\":{\"end\":5,\"start\":0,\"total\":5},\"tvshows\":[{\"file\":\"smb://HOMESERVER/TV/7th Heaven/\",\"imdbnumber\":\"73928\",\"label\":\"7th Heaven\",\"tvshowid\":3},{\"file\":\"smb://HOMESERVER/TV/8 Simple Rules/\",\"imdbnumber\":\"78461\",\"label\":\"8 Simple Rules\",\"tvshowid\":4},{\"file\":\"smb://HOMESERVER/TV/24-7 Penguins-Capitals- Road to the NHL Winter Classic/\",\"imdbnumber\":\"213041\",\"label\":\"24/7 Penguins/Capitals: Road to the NHL Winter Classic\",\"tvshowid\":1},{\"file\":\"smb://HOMESERVER/TV/30 Rock/\",\"imdbnumber\":\"79488\",\"label\":\"30 Rock\",\"tvshowid\":2},{\"file\":\"smb://HOMESERVER/TV/90210/\",\"imdbnumber\":\"82716\",\"label\":\"90210\",\"tvshowid\":5}]}}";
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
//Act
var result = mocker.Resolve<XbmcProvider>().GetTvShowsJson("localhost:8080", "xbmc", "xbmc");
//Assert
Assert.AreEqual(5, result.Count);
result.Should().Contain(s => s.ImdbNumber == 79488);
}
[Test]
public void Notify_true()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Strict);
var header = "NzbDrone Test";
var message = "Test Message!";
var fakeConfig = mocker.GetMock<ConfigProvider>();
fakeConfig.SetupGet(s => s.XbmcHosts).Returns("localhost:8080");
//var fakeUdpProvider = mocker.GetMock<EventClient>();
var fakeEventClient = mocker.GetMock<EventClientProvider>();
fakeEventClient.Setup(s => s.SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", "localhost")).Returns(true);
//Act
mocker.Resolve<XbmcProvider>().Notify(header, message);
//Assert
mocker.VerifyAllMocks();
}
[Test]
public void SendCommand()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Strict);
var host = "localhost:8080";
var command = "ExecBuiltIn(CleanLibrary(video))";
var username = "xbmc";
var password = "xbmc";
var url = String.Format("http://localhost:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn(CleanLibrary(video))");
//var fakeUdpProvider = mocker.GetMock<EventClient>();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.DownloadString(url, username, password)).Returns("Ok\n");
//Act
var result = mocker.Resolve<XbmcProvider>().SendCommand(host, command, username, username);
//Assert
mocker.VerifyAllMocks();
Assert.AreEqual("Ok\n", result);
}
[Test]
public void GetXbmcSeriesPath_true()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Strict);
var queryResult = @"<xml><record><field>smb://xbmc:xbmc@HOMESERVER/TV/30 Rock/</field></record></xml>";
var host = "localhost:8080";
var username = "xbmc";
var password = "xbmc";
var setResponseUrl = "http://localhost:8080/xbmcCmds/xbmcHttp?command=SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;opentag;<tag>;closetag;</tag>;closefinaltag;false)";
var resetResponseUrl = "http://localhost:8080/xbmcCmds/xbmcHttp?command=SetResponseFormat()";
var query = String.Format("http://localhost:8080/xbmcCmds/xbmcHttp?command=QueryVideoDatabase(select path.strPath from path, tvshow, tvshowlinkpath where tvshow.c12 = 79488 and tvshowlinkpath.idShow = tvshow.idShow and tvshowlinkpath.idPath = path.idPath)");
//var fakeUdpProvider = mocker.GetMock<EventClient>();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.DownloadString(setResponseUrl, username, password)).Returns("<xml><tag>OK</xml>");
fakeHttp.Setup(s => s.DownloadString(resetResponseUrl, username, password)).Returns(@"<html>
<li>OK
</html>");
fakeHttp.Setup(s => s.DownloadString(query, username, password)).Returns(queryResult);
//Act
var result = mocker.Resolve<XbmcProvider>().GetXbmcSeriesPath(host, 79488, username, username);
//Assert
mocker.VerifyAllMocks();
Assert.AreEqual("smb://xbmc:xbmc@HOMESERVER/TV/30 Rock/", result);
}
[Test]
public void GetXbmcSeriesPath_false()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Strict);
var queryResult = @"<xml></xml>";
var host = "localhost:8080";
var username = "xbmc";
var password = "xbmc";
var setResponseUrl = "http://localhost:8080/xbmcCmds/xbmcHttp?command=SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;opentag;<tag>;closetag;</tag>;closefinaltag;false)";
var resetResponseUrl = "http://localhost:8080/xbmcCmds/xbmcHttp?command=SetResponseFormat()";
var query = String.Format("http://localhost:8080/xbmcCmds/xbmcHttp?command=QueryVideoDatabase(select path.strPath from path, tvshow, tvshowlinkpath where tvshow.c12 = 79488 and tvshowlinkpath.idShow = tvshow.idShow and tvshowlinkpath.idPath = path.idPath)");
//var fakeUdpProvider = mocker.GetMock<EventClient>();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.DownloadString(setResponseUrl, username, password)).Returns("<xml><tag>OK</xml>");
fakeHttp.Setup(s => s.DownloadString(resetResponseUrl, username, password)).Returns(@"<html>
<li>OK
</html>");
fakeHttp.Setup(s => s.DownloadString(query, username, password)).Returns(queryResult);
//Act
var result = mocker.Resolve<XbmcProvider>().GetXbmcSeriesPath(host, 79488, username, username);
//Assert
mocker.VerifyAllMocks();
Assert.AreEqual("", result);
}
[Test]
public void Clean()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Strict);
var fakeConfig = mocker.GetMock<ConfigProvider>();
fakeConfig.SetupGet(s => s.XbmcHosts).Returns("localhost:8080");
var fakeEventClient = mocker.GetMock<EventClientProvider>();
fakeEventClient.Setup(s => s.SendAction("localhost", ActionType.ExecBuiltin, "ExecBuiltIn(CleanLibrary(video))")).Returns(true);
//Act
mocker.Resolve<XbmcProvider>().Clean();
//Assert
mocker.VerifyAllMocks();
}
[Test]
public void UpdateWithHttp_Single()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Default);
var host = "localhost:8080";
var username = "xbmc";
var password = "xbmc";
var queryResult = @"<xml><record><field>smb://xbmc:xbmc@HOMESERVER/TV/30 Rock/</field></record></xml>";
var queryUrl = "http://localhost:8080/xbmcCmds/xbmcHttp?command=QueryVideoDatabase(select path.strPath from path, tvshow, tvshowlinkpath where tvshow.c12 = 79488 and tvshowlinkpath.idShow = tvshow.idShow and tvshowlinkpath.idPath = path.idPath)";
var url = "http://localhost:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn(UpdateLibrary(video,smb://xbmc:xbmc@HOMESERVER/TV/30 Rock/))";
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 79488)
.With(s => s.Title = "30 Rock")
.Build();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.DownloadString(queryUrl, username, password)).Returns(queryResult);
fakeHttp.Setup(s => s.DownloadString(url, username, password));
//Act
mocker.Resolve<XbmcProvider>().UpdateWithHttp(fakeSeries, host, username, password);
//Assert
mocker.VerifyAllMocks();
}
[Test]
public void UpdateWithHttp_All()
{
//Setup
var mocker = new AutoMoqer(MockBehavior.Default);
var host = "localhost:8080";
var username = "xbmc";
var password = "xbmc";
var queryResult = @"<xml></xml>";
var queryUrl = "http://localhost:8080/xbmcCmds/xbmcHttp?command=QueryVideoDatabase(select path.strPath from path, tvshow, tvshowlinkpath where tvshow.c12 = 79488 and tvshowlinkpath.idShow = tvshow.idShow and tvshowlinkpath.idPath = path.idPath)";
var url = "http://localhost:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn(UpdateLibrary(video))";
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 79488)
.With(s => s.Title = "30 Rock")
.Build();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.DownloadString(queryUrl, username, password)).Returns(queryResult);
fakeHttp.Setup(s => s.DownloadString(url, username, password));
//Act
mocker.Resolve<XbmcProvider>().UpdateWithHttp(fakeSeries, host, username, password);
//Assert
mocker.VerifyAllMocks();
}
[Test]
public void UpdateWithJson_Single()
{
//Setup
var mocker = new AutoMoqer();
var host = "localhost:8080";
var username = "xbmc";
var password = "xbmc";
var serializedQuery = "{\"jsonrpc\":\"2.0\",\"method\":\"VideoLibrary.GetTvShows\",\"params\":{\"fields\":[\"file\",\"imdbnumber\"]},\"id\":10}";
var tvshows = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"limits\":{\"end\":5,\"start\":0,\"total\":5},\"tvshows\":[{\"file\":\"smb://HOMESERVER/TV/7th Heaven/\",\"imdbnumber\":\"73928\",\"label\":\"7th Heaven\",\"tvshowid\":3},{\"file\":\"smb://HOMESERVER/TV/8 Simple Rules/\",\"imdbnumber\":\"78461\",\"label\":\"8 Simple Rules\",\"tvshowid\":4},{\"file\":\"smb://HOMESERVER/TV/24-7 Penguins-Capitals- Road to the NHL Winter Classic/\",\"imdbnumber\":\"213041\",\"label\":\"24/7 Penguins/Capitals: Road to the NHL Winter Classic\",\"tvshowid\":1},{\"file\":\"smb://HOMESERVER/TV/30 Rock/\",\"imdbnumber\":\"79488\",\"label\":\"30 Rock\",\"tvshowid\":2},{\"file\":\"smb://HOMESERVER/TV/90210/\",\"imdbnumber\":\"82716\",\"label\":\"90210\",\"tvshowid\":5}]}}";
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 79488)
.With(s => s.Title = "30 Rock")
.Build();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.PostCommand(host, username, password, serializedQuery))
.Returns(tvshows);
var fakeEventClient = mocker.GetMock<EventClientProvider>();
fakeEventClient.Setup(s => s.SendAction("localhost", ActionType.ExecBuiltin, "ExecBuiltIn(UpdateLibrary(video,smb://HOMESERVER/TV/30 Rock/))"));
//Act
mocker.Resolve<XbmcProvider>().UpdateWithJson(fakeSeries, host, username, password);
//Assert
mocker.VerifyAllMocks();
}
[Test]
public void UpdateWithJson_All()
{
//Setup
var mocker = new AutoMoqer();
var host = "localhost:8080";
var username = "xbmc";
var password = "xbmc";
var serializedQuery = "{\"jsonrpc\":\"2.0\",\"method\":\"VideoLibrary.GetTvShows\",\"params\":{\"fields\":[\"file\",\"imdbnumber\"]},\"id\":10}";
var tvshows = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"limits\":{\"end\":5,\"start\":0,\"total\":5},\"tvshows\":[{\"file\":\"smb://HOMESERVER/TV/7th Heaven/\",\"imdbnumber\":\"73928\",\"label\":\"7th Heaven\",\"tvshowid\":3},{\"file\":\"smb://HOMESERVER/TV/8 Simple Rules/\",\"imdbnumber\":\"78461\",\"label\":\"8 Simple Rules\",\"tvshowid\":4},{\"file\":\"smb://HOMESERVER/TV/24-7 Penguins-Capitals- Road to the NHL Winter Classic/\",\"imdbnumber\":\"213041\",\"label\":\"24/7 Penguins/Capitals: Road to the NHL Winter Classic\",\"tvshowid\":1},{\"file\":\"smb://HOMESERVER/TV/90210/\",\"imdbnumber\":\"82716\",\"label\":\"90210\",\"tvshowid\":5}]}}";
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 79488)
.With(s => s.Title = "30 Rock")
.Build();
var fakeHttp = mocker.GetMock<HttpProvider>();
fakeHttp.Setup(s => s.PostCommand(host, username, password, serializedQuery))
.Returns(tvshows);
var fakeEventClient = mocker.GetMock<EventClientProvider>();
fakeEventClient.Setup(s => s.SendAction("localhost", ActionType.ExecBuiltin, "ExecBuiltIn(UpdateLibrary(video))"));
//Act
mocker.Resolve<XbmcProvider>().UpdateWithJson(fakeSeries, host, username, password);
//Assert
mocker.VerifyAllMocks();
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public enum ActionType
{
ExecBuiltin = 0x01,
Button = 0x02
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class ActivePlayersResult
{
public string Id { get; set; }
public string JsonRpc { get; set; }
public Dictionary<string, bool> Result { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class Command
{
public string jsonrpc
{
get { return "2.0"; }
}
public string method { get; set; }
public Params @params { get; set; }
public long id { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class ErrorResult
{
public string Id { get; set; }
public string JsonRpc { get; set; }
public Dictionary<string, string> Error { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public enum IconType
{
None = 0x00,
Jpeg = 0x01,
Png = 0x02,
Gif = 0x03
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class Params
{
public string[] fields { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class TvShow
{
public int TvShowId { get; set; }
public string Label { get; set; }
public int ImdbNumber { get; set; }
public string File { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class TvShowResult
{
public string Id { get; set; }
public string JsonRpc { get; set; }
public Dictionary<string, List<TvShow>> Result { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model.Xbmc
{
public class VersionResult
{
public string Id { get; set; }
public string JsonRpc { get; set; }
public Dictionary<string, int> Result { get; set; }
}
}

View File

@ -191,6 +191,18 @@
<Compile Include="Model\LanguageType.cs" /> <Compile Include="Model\LanguageType.cs" />
<Compile Include="Model\Quality.cs" /> <Compile Include="Model\Quality.cs" />
<Compile Include="Model\SabnzbdInfoModel.cs" /> <Compile Include="Model\SabnzbdInfoModel.cs" />
<Compile Include="Model\Xbmc\ActionType.cs" />
<Compile Include="Model\Xbmc\ActivePlayersResult.cs" />
<Compile Include="Model\Xbmc\ErrorResult.cs" />
<Compile Include="Model\Xbmc\IconType.cs" />
<Compile Include="Providers\Core\UdpProvider.cs" />
<None Include="Providers\Xbmc\EventClient_old.cs" />
<Compile Include="Providers\Xbmc\ResourceManager.cs" />
<Compile Include="Model\Xbmc\TvShowResult.cs" />
<Compile Include="Model\Xbmc\Params.cs" />
<Compile Include="Model\Xbmc\Command.cs" />
<Compile Include="Model\Xbmc\TvShow.cs" />
<Compile Include="Model\Xbmc\VersionResult.cs" />
<Compile Include="Providers\DiskScanProvider.cs" /> <Compile Include="Providers\DiskScanProvider.cs" />
<Compile Include="Providers\DownloadProvider.cs" /> <Compile Include="Providers\DownloadProvider.cs" />
<Compile Include="Providers\ExternalNotification\ExternalNotificationProviderBase.cs" /> <Compile Include="Providers\ExternalNotification\ExternalNotificationProviderBase.cs" />
@ -213,6 +225,7 @@
<Compile Include="Providers\Jobs\RssSyncJob.cs" /> <Compile Include="Providers\Jobs\RssSyncJob.cs" />
<Compile Include="Providers\Jobs\UpdateInfoJob.cs" /> <Compile Include="Providers\Jobs\UpdateInfoJob.cs" />
<Compile Include="Providers\SceneMappingProvider.cs" /> <Compile Include="Providers\SceneMappingProvider.cs" />
<Compile Include="Providers\Xbmc\EventClientProvider.cs" />
<Compile Include="Repository\ExternalNotificationSetting.cs" /> <Compile Include="Repository\ExternalNotificationSetting.cs" />
<Compile Include="Repository\JobSetting.cs" /> <Compile Include="Repository\JobSetting.cs" />
<Compile Include="Repository\IndexerSetting.cs" /> <Compile Include="Repository\IndexerSetting.cs" />
@ -292,6 +305,9 @@
<ItemGroup> <ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="NzbDrone.jpg" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent> <PostBuildEvent>

BIN
NzbDrone.Core/NzbDrone.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -120,13 +120,6 @@ namespace NzbDrone.Core.Providers.Core
set { SetValue("DownloadPropers", value); } set { SetValue("DownloadPropers", value); }
} }
public virtual Int32 Retention
{
get { return GetValueInt("Retention"); }
set { SetValue("Retention", value); }
}
public virtual String SabHost public virtual String SabHost
{ {
get { return GetValue("SabHost", "localhost"); } get { return GetValue("SabHost", "localhost"); }
@ -259,6 +252,59 @@ namespace NzbDrone.Core.Providers.Core
set { SetValue("DefaultQualityProfile", value); } set { SetValue("DefaultQualityProfile", value); }
} }
public virtual Boolean XbmcEnabled
{
get { return GetValueBoolean("XbmcEnabled"); }
set { SetValue("XbmcEnabled", value); }
}
public virtual Boolean XbmcNotifyOnGrab
{
get { return GetValueBoolean("XbmcNotifyOnGrab"); }
set { SetValue("XbmcNotifyOnGrab", value); }
}
public virtual Boolean XbmcNotifyOnDownload
{
get { return GetValueBoolean("XbmcNotifyOnDownload"); }
set { SetValue("XbmcNotifyOnDownload", value); }
}
public virtual Boolean XbmcUpdateLibrary
{
get { return GetValueBoolean("XbmcUpdateLibrary"); }
set { SetValue("XbmcUpdateLibrary", value); }
}
public virtual Boolean XbmcCleanLibrary
{
get { return GetValueBoolean("XbmcCleanLibrary"); }
set { SetValue("XbmcCleanLibrary", value); }
}
public virtual string XbmcHosts
{
get { return GetValue("XbmcHosts", "localhost:8080"); }
set { SetValue("XbmcHosts", value); }
}
public virtual string XbmcUsername
{
get { return GetValue("XbmcUsername", "xbmc"); }
set { SetValue("XbmcUsername", value); }
}
public virtual string XbmcPassword
{
get { return GetValue("XbmcPassword", String.Empty); }
set { SetValue("XbmcPassword", value); }
}
private string GetValue(string key) private string GetValue(string key)
{ {
return GetValue(key, String.Empty); return GetValue(key, String.Empty);

View File

@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text;
using NLog; using NLog;
namespace NzbDrone.Core.Providers.Core namespace NzbDrone.Core.Providers.Core
@ -64,5 +65,33 @@ namespace NzbDrone.Core.Providers.Core
return false; return false;
} }
} }
public virtual string PostCommand(string address, string username, string password, string command)
{
address += "/jsonrpc";
byte[] byteArray = Encoding.ASCII.GetBytes(command);
var request = WebRequest.Create(address);
request.Method = "POST";
request.Credentials = new NetworkCredential(username, password);
request.ContentLength = byteArray.Length;
request.ContentType = "application/x-www-form-urlencoded";
var dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
var response = request.GetResponse();
dataStream = response.GetResponseStream();
var reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
reader.Close();
dataStream.Close();
response.Close();
return responseFromServer.Replace("&nbsp;", " ");
}
} }
} }

View File

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Ninject;
namespace NzbDrone.Core.Providers.Core
{
public class UdpProvider
{
[Inject]
public UdpProvider()
{
}
private const int StandardPort = 9777;
private const int MaxPacketSize = 1024;
private const int HeaderSize = 32;
private const int MaxPayloadSize = MaxPacketSize - HeaderSize;
private const byte MajorVersion = 2;
private const byte MinorVersion = 0;
public enum PacketType
{
Helo = 0x01,
Bye = 0x02,
Button = 0x03,
Mouse = 0x04,
Ping = 0x05,
Broadcast = 0x06, //Currently not implemented
Notification = 0x07,
Blob = 0x08,
Log = 0x09,
Action = 0x0A,
Debug = 0xFF //Currently not implemented
}
private byte[] Header(PacketType packetType, int numberOfPackets, int currentPacket, int payloadSize, uint uniqueToken)
{
byte[] header = new byte[HeaderSize];
header[0] = (byte)'X';
header[1] = (byte)'B';
header[2] = (byte)'M';
header[3] = (byte)'C';
header[4] = MajorVersion;
header[5] = MinorVersion;
if (currentPacket == 1)
{
header[6] = (byte)(((ushort)packetType & 0xff00) >> 8);
header[7] = (byte)((ushort)packetType & 0x00ff);
}
else
{
header[6] = (byte)(((ushort)PacketType.Blob & 0xff00) >> 8);
header[7] = (byte)((ushort)PacketType.Blob & 0x00ff);
}
header[8] = (byte)((currentPacket & 0xff000000) >> 24);
header[9] = (byte)((currentPacket & 0x00ff0000) >> 16);
header[10] = (byte)((currentPacket & 0x0000ff00) >> 8);
header[11] = (byte)(currentPacket & 0x000000ff);
header[12] = (byte)((numberOfPackets & 0xff000000) >> 24);
header[13] = (byte)((numberOfPackets & 0x00ff0000) >> 16);
header[14] = (byte)((numberOfPackets & 0x0000ff00) >> 8);
header[15] = (byte)(numberOfPackets & 0x000000ff);
header[16] = (byte)((payloadSize & 0xff00) >> 8);
header[17] = (byte)(payloadSize & 0x00ff);
header[18] = (byte)((uniqueToken & 0xff000000) >> 24);
header[19] = (byte)((uniqueToken & 0x00ff0000) >> 16);
header[20] = (byte)((uniqueToken & 0x0000ff00) >> 8);
header[21] = (byte)(uniqueToken & 0x000000ff);
return header;
}
public virtual bool Send(string address, PacketType packetType, byte[] payload)
{
var uniqueToken = (uint)DateTime.Now.TimeOfDay.Milliseconds;
var socket = Connect(address, StandardPort);
if (socket == null || !socket.Connected)
{
return false;
}
try
{
bool successfull = true;
int packetCount = (payload.Length / MaxPayloadSize) + 1;
int bytesToSend = 0;
int bytesSent = 0;
int bytesLeft = payload.Length;
for (int Package = 1; Package <= packetCount; Package++)
{
if (bytesLeft > MaxPayloadSize)
{
bytesToSend = MaxPayloadSize;
bytesLeft -= bytesToSend;
}
else
{
bytesToSend = bytesLeft;
bytesLeft = 0;
}
byte[] header = Header(packetType, packetCount, Package, bytesToSend, uniqueToken);
byte[] packet = new byte[MaxPacketSize];
Array.Copy(header, 0, packet, 0, header.Length);
Array.Copy(payload, bytesSent, packet, header.Length, bytesToSend);
int sendSize = socket.Send(packet, header.Length + bytesToSend, SocketFlags.None);
if (sendSize != (header.Length + bytesToSend))
{
successfull = false;
break;
}
bytesSent += bytesToSend;
}
Disconnect(socket);
return successfull;
}
catch
{
Disconnect(socket);
return false;
}
}
private Socket Connect(string address, int port)
{
try
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress ip;
if (!IPAddress.TryParse(address, out ip))
{
IPHostEntry ipHostEntry = Dns.GetHostEntry(address);
foreach (IPAddress ipAddress in ipHostEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
ip = ipAddress;
break;
}
}
}
socket.Connect(new IPEndPoint(ip, port));
return socket;
}
catch (Exception exc)
{
Console.WriteLine(exc);
return null;
}
}
private void Disconnect(Socket socket)
{
try
{
if (socket != null)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
catch
{
}
}
}
}

View File

@ -1,4 +1,5 @@
using NLog; using System;
using NLog;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
@ -37,10 +38,19 @@ namespace NzbDrone.Core.Providers.ExternalNotification
OnGrab(message); OnGrab(message);
else if (type == ExternalNotificationType.Download) else if (type == ExternalNotificationType.Download)
OnDownload(message, seriesId); {
throw new NotImplementedException();
var series = new Series();
OnDownload(message, series);
}
else if (type == ExternalNotificationType.Rename) else if (type == ExternalNotificationType.Rename)
OnRename(message, seriesId); {
throw new NotImplementedException();
var series = new Series();
OnRename(message, series);
}
} }
/// <summary> /// <summary>
@ -53,14 +63,14 @@ namespace NzbDrone.Core.Providers.ExternalNotification
/// Performs the on download action /// Performs the on download action
/// </summary> /// </summary>
/// <param name = "message">The message to send to the receiver</param> /// <param name = "message">The message to send to the receiver</param>
/// <param name = "seriesId">The Series ID for the new download</param> /// <param name = "series">The Series for the new download</param>
public abstract void OnDownload(string message, int seriesId); public abstract void OnDownload(string message, Series series);
/// <summary> /// <summary>
/// Performs the on rename action /// Performs the on rename action
/// </summary> /// </summary>
/// <param name = "message">The message to send to the receiver</param> /// <param name = "message">The message to send to the receiver</param>
/// <param name = "seriesId">The Series ID for the new download</param> /// <param name = "series">The Series for the new download</param>
public abstract void OnRename(string message, int seriesId); public abstract void OnRename(string message, Series series);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.ExternalNotification namespace NzbDrone.Core.Providers.ExternalNotification
{ {
@ -37,7 +38,7 @@ namespace NzbDrone.Core.Providers.ExternalNotification
_logger.Trace("XBMC Notifier is not enabled"); _logger.Trace("XBMC Notifier is not enabled");
} }
public override void OnDownload(string message, int seriesId) public override void OnDownload(string message, Series series)
{ {
const string header = "NzbDrone [TV] - Downloaded"; const string header = "NzbDrone [TV] - Downloaded";
@ -52,7 +53,7 @@ namespace NzbDrone.Core.Providers.ExternalNotification
if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnDownload", false))) if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnDownload", false)))
{ {
_logger.Trace("Sending Update Request to XBMC"); _logger.Trace("Sending Update Request to XBMC");
_xbmcProvider.Update(seriesId); _xbmcProvider.Update(series);
} }
if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnDownload", false))) if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnDownload", false)))
@ -65,7 +66,7 @@ namespace NzbDrone.Core.Providers.ExternalNotification
_logger.Trace("XBMC Notifier is not enabled"); _logger.Trace("XBMC Notifier is not enabled");
} }
public override void OnRename(string message, int seriesId) public override void OnRename(string message, Series series)
{ {
const string header = "NzbDrone [TV] - Renamed"; const string header = "NzbDrone [TV] - Renamed";
@ -78,7 +79,7 @@ namespace NzbDrone.Core.Providers.ExternalNotification
if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnRename", false))) if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnRename", false)))
{ {
_logger.Trace("Sending Update Request to XBMC"); _logger.Trace("Sending Update Request to XBMC");
_xbmcProvider.Update(seriesId); _xbmcProvider.Update(series);
} }
if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnRename", false))) if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnRename", false)))

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Model.Xbmc;
namespace NzbDrone.Core.Providers.Xbmc
{
public class EventClientProvider
{
private readonly UdpProvider _udpProvider;
[Inject]
public EventClientProvider(UdpProvider udpProvider)
{
_udpProvider = udpProvider;
}
public EventClientProvider()
{
}
public virtual bool SendNotification(string caption, string message, IconType iconType, string iconFile, string address)
{
byte[] icon = new byte[0];
if (iconType != IconType.None)
{
icon = ResourceManager.GetRawLogo(iconFile);
}
byte[] payload = new byte[caption.Length + message.Length + 7 + icon.Length];
int offset = 0;
for (int i = 0; i < caption.Length; i++)
payload[offset++] = (byte)caption[i];
payload[offset++] = (byte)'\0';
for (int i = 0; i < message.Length; i++)
payload[offset++] = (byte)message[i];
payload[offset++] = (byte)'\0';
payload[offset++] = (byte)iconType;
for (int i = 0; i < 4; i++)
payload[offset++] = (byte)0;
Array.Copy(icon, 0, payload, caption.Length + message.Length + 7, icon.Length);
return _udpProvider.Send(address, UdpProvider.PacketType.Notification, payload);
}
public virtual bool SendAction(string address, ActionType action, string messages)
{
var payload = new byte[messages.Length + 2];
int offset = 0;
payload[offset++] = (byte)action;
for (int i = 0; i < messages.Length; i++)
payload[offset++] = (byte)messages[i];
payload[offset++] = (byte)'\0';
return _udpProvider.Send(address, UdpProvider.PacketType.Action, payload);
}
}
}

View File

@ -0,0 +1,55 @@
namespace NzbDrone.Core.Providers.Xbmc
{
public class ResourceManager
{
public static System.Drawing.Icon GetIcon(string Name)
{
System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name));
if (stm == null) return null;
return new System.Drawing.Icon(stm);
}
public static byte[] GetRawData(string Name)
{
byte[] data;
using (System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name)))
{
if (stm == null) return null;
data = new byte[stm.Length];
stm.Read(data, 0, data.Length);
}
return data;
}
public static byte[] GetRawLogo(string Name)
{
byte[] data;
using (System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}", Name)))
{
if (stm == null) return null;
data = new byte[stm.Length];
stm.Read(data, 0, data.Length);
}
return data;
}
public static System.Drawing.Bitmap GetIconAsImage(string Name)
{
System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("{0}.Icons.{1}.ico", typeof(ResourceManager).Namespace, Name));
if (stm == null) return null;
System.Drawing.Bitmap bmp;
using (System.Drawing.Icon ico = new System.Drawing.Icon(stm))
{
bmp = new System.Drawing.Bitmap(ico.Width, ico.Height);
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
{
g.DrawIcon(ico, 0, 0);
}
}
return bmp;
}
}
}

View File

@ -1,10 +1,15 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Web.Script.Serialization;
using System.Xml.Linq; using System.Xml.Linq;
using Ninject; using Ninject;
using NLog; using NLog;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.Xbmc;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
@ -13,72 +18,134 @@ namespace NzbDrone.Core.Providers
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
private readonly HttpProvider _httpProvider; private readonly HttpProvider _httpProvider;
private readonly EventClientProvider _eventClientProvider;
[Inject] [Inject]
public XbmcProvider(ConfigProvider configProvider, HttpProvider httpProvider) public XbmcProvider(ConfigProvider configProvider, HttpProvider httpProvider, EventClientProvider eventClientProvider)
{ {
_configProvider = configProvider; _configProvider = configProvider;
_httpProvider = httpProvider; _httpProvider = httpProvider;
_eventClientProvider = eventClientProvider;
} }
public virtual void Notify(string header, string message) public virtual void Notify(string header, string message)
{ {
//Get time in seconds and convert to ms //Always use EventServer, until Json has real support for it
var time = Convert.ToInt32(_configProvider.GetValue("XbmcDisplayTime", "3")) * 1000; foreach (var host in _configProvider.XbmcHosts.Split(','))
var command = String.Format("ExecBuiltIn(Notification({0},{1},{2}))", header, message, time);
if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotificationImage", false)))
{
//Todo: Get the actual port that NzbDrone is running on...
var serverInfo = String.Format("http://{0}:{1}", Environment.MachineName, "8989");
var imageUrl = String.Format("{0}/Content/XbmcNotification.png", serverInfo);
command = String.Format("ExecBuiltIn(Notification({0},{1},{2}, {3}))", header, message, time, imageUrl);
}
foreach (var host in _configProvider.GetValue("XbmcHosts", "localhost:80").Split(','))
{ {
Logger.Trace("Sending Notifcation to XBMC Host: {0}", host); Logger.Trace("Sending Notifcation to XBMC Host: {0}", host);
SendCommand(host, command); _eventClientProvider.SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", GetHostWithoutPort(host));
} }
} }
public virtual void Update(int seriesId) public XbmcProvider()
{ {
foreach (var host in _configProvider.GetValue("XbmcHosts", "localhost:80").Split(','))
{ }
Logger.Trace("Sending Update DB Request to XBMC Host: {0}", host);
var xbmcSeriesPath = GetXbmcSeriesPath(host, seriesId);
//If the path is not found & the user wants to update the entire library, do it now. public virtual void Update(Series series)
if (String.IsNullOrEmpty(xbmcSeriesPath) && {
Convert.ToBoolean(_configProvider.GetValue("XbmcFullUpdate", false))) //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;
foreach (var host in _configProvider.XbmcHosts.Split(','))
{
Logger.Trace("Determining version of XBMC Host: {0}", host);
var version = GetJsonVersion(host, username, password);
//If Dharma
if (version == 2)
UpdateWithHttp(series, host, username, password);
//If Eden or newer (attempting to make it future compatible)
else if (version >= 3)
UpdateWithJson(series, password, host, username);
}
}
public virtual bool UpdateWithJson(Series series, string host, string username, string password)
{
try
{
//Use Json!
var xbmcShows = GetTvShowsJson(host, username, password);
var path = xbmcShows.Where(s => s.ImdbNumber == series.SeriesId || s.Label == series.Title).FirstOrDefault();
var hostOnly = GetHostWithoutPort(host);
if (path != null)
{ {
//Update the entire library Logger.Trace("Updating series [{0}] on XBMC host: {1}", series.Title, host);
Logger.Trace("Series [{0}] doesn't exist on XBMC host: {1}, Updating Entire Library", seriesId, host); var command = String.Format("ExecBuiltIn(UpdateLibrary(video,{0}))", path.File);
SendCommand(host, "ExecBuiltIn(UpdateLibrary(video))"); _eventClientProvider.SendAction(hostOnly, ActionType.ExecBuiltin, command);
return;
} }
var command = String.Format("ExecBuiltIn(UpdateLibrary(video,{0}))", xbmcSeriesPath); else
SendCommand(host, command); {
Logger.Trace("Series [{0}] doesn't exist on XBMC host: {1}, Updating Entire Library", series.Title, host);
var command = String.Format("ExecBuiltIn(UpdateLibrary(video))");
_eventClientProvider.SendAction(hostOnly, ActionType.ExecBuiltin, command);
}
} }
catch (Exception ex)
{
Logger.DebugException(ex.Message, ex);
return false;
}
return true;
}
public virtual bool UpdateWithHttp(Series series, string host, string username, string password)
{
try
{
Logger.Trace("Sending Update DB Request to XBMC Host: {0}", host);
var xbmcSeriesPath = GetXbmcSeriesPath(host, series.SeriesId, username, password);
//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
{
//Update the entire library
Logger.Trace("Series [{0}] doesn't exist on XBMC host: {1}, Updating Entire Library", series.Title, host);
SendCommand(host, "ExecBuiltIn(UpdateLibrary(video))", username, password);
}
}
catch (Exception ex)
{
Logger.DebugException(ex.Message, ex);
return false;
}
return true;
} }
public virtual void Clean() public virtual void Clean()
{ {
foreach (var host in _configProvider.GetValue("XbmcHosts", "localhost:80").Split(',')) //Use EventServer, once Dharma is extinct use Json?
foreach (var host in _configProvider.XbmcHosts.Split(','))
{ {
Logger.Trace("Sending DB Clean Request to XBMC Host: {0}", host); Logger.Trace("Sending DB Clean Request to XBMC Host: {0}", host);
var command = String.Format("ExecBuiltIn(CleanLibrary(video))"); var command = "ExecBuiltIn(CleanLibrary(video))";
SendCommand(host, command); _eventClientProvider.SendAction(GetHostWithoutPort(host), ActionType.ExecBuiltin, command);
} }
} }
private string SendCommand(string host, string command) public virtual string SendCommand(string host, string command, string username, string password)
{ {
var username = _configProvider.GetValue("XbmcUsername", String.Empty);
var password = _configProvider.GetValue("XbmcPassword", String.Empty);
var url = String.Format("http://{0}/xbmcCmds/xbmcHttp?command={1}", host, command); var url = String.Format("http://{0}/xbmcCmds/xbmcHttp?command={1}", host, command);
if (!String.IsNullOrEmpty(username)) if (!String.IsNullOrEmpty(username))
@ -89,7 +156,7 @@ namespace NzbDrone.Core.Providers
return _httpProvider.DownloadString(url); return _httpProvider.DownloadString(url);
} }
private string GetXbmcSeriesPath(string host, int seriesId) public virtual string GetXbmcSeriesPath(string host, int seriesId, string username, string password)
{ {
var query = var query =
String.Format( String.Format(
@ -97,13 +164,13 @@ namespace NzbDrone.Core.Providers
seriesId); seriesId);
var command = String.Format("QueryVideoDatabase({0})", query); var command = String.Format("QueryVideoDatabase({0})", query);
var setResponseCommand = const string setResponseCommand =
"SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;opentag;<tag>;closetag;</tag>;closefinaltag;false)"; "SetResponseFormat(webheader;false;webfooter;false;header;<xml>;footer;</xml>;opentag;<tag>;closetag;</tag>;closefinaltag;false)";
var resetResponseCommand = "SetResponseFormat()"; const string resetResponseCommand = "SetResponseFormat()";
SendCommand(host, setResponseCommand); SendCommand(host, setResponseCommand, username, password);
var response = SendCommand(host, command); var response = SendCommand(host, command, username, password);
SendCommand(host, resetResponseCommand); SendCommand(host, resetResponseCommand, username, password);
if (String.IsNullOrEmpty(response)) if (String.IsNullOrEmpty(response))
return String.Empty; return String.Empty;
@ -121,5 +188,109 @@ namespace NzbDrone.Core.Providers
return field.Value; return field.Value;
} }
public virtual int GetJsonVersion(string host, string username, string password)
{
//2 = Dharma
//3 = Eden/Nightly (as of July 2011)
var version = 0;
try
{
var command = new Command { id = 10, method = "JSONRPC.Version" };
var serializer = new JavaScriptSerializer();
var serialized = serializer.Serialize(command);
var response = _httpProvider.PostCommand(host, username, password, serialized);
if (CheckForJsonError(response))
return version;
var result = serializer.Deserialize<VersionResult>(response);
result.Result.TryGetValue("version", out version);
}
catch (Exception ex)
{
Logger.DebugException(ex.Message, ex);
}
return version;
}
public virtual Dictionary<string, bool> GetActivePlayers(string host, string username, string password)
{
//2 = Dharma
//3 = Eden/Nightly (as of July 2011)
try
{
var command = new Command { id = 10, method = "Player.GetActivePlayers" };
var serializer = new JavaScriptSerializer();
var serialized = serializer.Serialize(command);
var response = _httpProvider.PostCommand(host, username, password, serialized);
if (CheckForJsonError(response))
return null;
var result = serializer.Deserialize<ActivePlayersResult>(response);
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
{
var fields = new string[] { "file", "imdbnumber" };
var xbmcParams = new Params { fields = fields };
var command = new Command { id = 10, method = "VideoLibrary.GetTvShows", @params = xbmcParams };
var serializer = new JavaScriptSerializer();
var serialized = serializer.Serialize(command);
var response = _httpProvider.PostCommand(host, username, password, serialized);
if (CheckForJsonError(response))
return null;
var result = serializer.Deserialize<TvShowResult>(response);
var shows = result.Result["tvshows"];
return shows;
}
catch (Exception ex)
{
Logger.DebugException(ex.Message, ex);
}
return null;
}
public virtual bool CheckForJsonError(string response)
{
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;
}
return false;
}
private string GetHostWithoutPort(string address)
{
return address.Split(':')[0];
}
} }
} }

View File

@ -140,20 +140,14 @@ namespace NzbDrone.Web.Controllers
{ {
var model = new NotificationSettingsModel var model = new NotificationSettingsModel
{ {
XbmcEnabled = Convert.ToBoolean(_configProvider.GetValue("XbmcEnabled", false)), XbmcEnabled = _configProvider.XbmcEnabled,
XbmcNotifyOnGrab = Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnGrab", false)), XbmcNotifyOnGrab = _configProvider.XbmcNotifyOnGrab,
XbmcNotifyOnDownload = Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnDownload", false)), XbmcNotifyOnDownload = _configProvider.XbmcNotifyOnDownload,
XbmcNotifyOnRename = Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnRename", false)), XbmcUpdateLibrary = _configProvider.XbmcUpdateLibrary,
XbmcNotificationImage = Convert.ToBoolean(_configProvider.GetValue("XbmcNotificationImage", false)), XbmcCleanLibrary = _configProvider.XbmcCleanLibrary,
XbmcDisplayTime = Convert.ToInt32(_configProvider.GetValue("XbmcDisplayTime", 3)), XbmcHosts = _configProvider.XbmcHosts,
XbmcUpdateOnDownload = Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnDownload ", false)), XbmcUsername = _configProvider.XbmcUsername,
XbmcUpdateOnRename = Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnRename", false)), XbmcPassword = _configProvider.XbmcPassword
XbmcFullUpdate = Convert.ToBoolean(_configProvider.GetValue("XbmcFullUpdate", false)),
XbmcCleanOnDownload = Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnDownload", false)),
XbmcCleanOnRename = Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnRename", false)),
XbmcHosts = _configProvider.GetValue("XbmcHosts", "localhost:80"),
XbmcUsername = _configProvider.GetValue("XbmcUsername", String.Empty),
XbmcPassword = _configProvider.GetValue("XbmcPassword", String.Empty)
}; };
return View(model); return View(model);
@ -401,20 +395,14 @@ namespace NzbDrone.Web.Controllers
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
_configProvider.SetValue("XbmcEnabled", data.XbmcEnabled.ToString()); _configProvider.XbmcEnabled = data.XbmcEnabled;
_configProvider.SetValue("XbmcNotifyOnGrab", data.XbmcNotifyOnGrab.ToString()); _configProvider.XbmcNotifyOnGrab = data.XbmcNotifyOnGrab;
_configProvider.SetValue("XbmcNotifyOnDownload", data.XbmcNotifyOnDownload.ToString()); _configProvider.XbmcNotifyOnDownload = data.XbmcNotifyOnDownload;
_configProvider.SetValue("XbmcNotifyOnRename", data.XbmcNotifyOnRename.ToString()); _configProvider.XbmcUpdateLibrary = data.XbmcUpdateLibrary;
_configProvider.SetValue("XbmcNotificationImage", data.XbmcNotificationImage.ToString()); _configProvider.XbmcCleanLibrary = data.XbmcCleanLibrary;
_configProvider.SetValue("XbmcDisplayTime", data.XbmcDisplayTime.ToString()); _configProvider.XbmcHosts = data.XbmcHosts;
_configProvider.SetValue("XbmcUpdateOnDownload", data.XbmcUpdateOnDownload.ToString()); _configProvider.XbmcUsername = data.XbmcUsername;
_configProvider.SetValue("XbmcUpdateOnRename", data.XbmcUpdateOnRename.ToString()); _configProvider.XbmcPassword = data.XbmcPassword;
_configProvider.SetValue("XbmcFullUpdate", data.XbmcFullUpdate.ToString());
_configProvider.SetValue("XbmcCleanOnDownload", data.XbmcCleanOnDownload.ToString());
_configProvider.SetValue("XbmcCleanOnRename", data.XbmcCleanOnRename.ToString());
_configProvider.SetValue("XbmcHosts", data.XbmcHosts);
_configProvider.SetValue("XbmcUsername", data.XbmcUsername);
_configProvider.SetValue("XbmcPassword", data.XbmcPassword);
basicNotification.Title = SETTINGS_SAVED; basicNotification.Title = SETTINGS_SAVED;
_notificationProvider.Register(basicNotification); _notificationProvider.Register(basicNotification);

View File

@ -17,43 +17,17 @@ namespace NzbDrone.Web.Models
[Description("Send notification when episode is downloaded?")] [Description("Send notification when episode is downloaded?")]
public bool XbmcNotifyOnDownload { get; set; } public bool XbmcNotifyOnDownload { get; set; }
[DisplayName("Notify on Rename")] [DisplayName("Update on Download and Rename")]
[Description("Send notification when episode is renamed?")] [Description("Update XBMC library after episode is downloaded or renamed?")]
public bool XbmcNotifyOnRename { get; set; } public bool XbmcUpdateLibrary { get; set; }
[DisplayName("Image with Notification")] [DisplayName("Clean on Download/Rename")]
[Description("Display NzbDrone image on notifications?")] [Description("Clean XBMC library after an episode is downloaded or renamed?")]
public bool XbmcNotificationImage { get; set; } public bool XbmcCleanLibrary { get; set; }
[Required]
[Range(3, 10, ErrorMessage = "Must be between 3 and 10 seconds")]
[DisplayName("Display Time")]
[Description("How long the notification should be displayed")]
public int XbmcDisplayTime { get; set; }
[DisplayName("Update on Download")]
[Description("Update XBMC library after episode download?")]
public bool XbmcUpdateOnDownload { get; set; }
[DisplayName("Update on Rename")]
[Description("Update XBMC library after episode is renamed?")]
public bool XbmcUpdateOnRename { get; set; }
[DisplayName("Full Update")]
[Description("Perform a full update is series update fails?")]
public bool XbmcFullUpdate { get; set; }
[DisplayName("Clean on Download")]
[Description("Clean XBMC library after episode download?")]
public bool XbmcCleanOnDownload { get; set; }
[DisplayName("Clean on Rename")]
[Description("Clean XBMC library after episode is renamed?")]
public bool XbmcCleanOnRename { get; set; }
[DataType(DataType.Text)] [DataType(DataType.Text)]
[DisplayName("Hosts")] [DisplayName("Hosts")]
[Description("XBMC hosts with port, comma separ")] [Description("XBMC hosts with port, comma separated")]
[DisplayFormat(ConvertEmptyStringToNull = false)] [DisplayFormat(ConvertEmptyStringToNull = false)]
public string XbmcHosts { get; set; } public string XbmcHosts { get; set; }

View File

@ -66,45 +66,15 @@
</label> </label>
@Html.CheckBoxFor(m => m.XbmcNotifyOnDownload, new { @class = "inputClass checkClass" }) @Html.CheckBoxFor(m => m.XbmcNotifyOnDownload, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcNotifyOnRename) <label class="labelClass">@Html.LabelFor(m => m.XbmcUpdateLibrary)
<span class="small">@Html.DescriptionFor(m => m.XbmcNotifyOnRename)</span> <span class="small">@Html.DescriptionFor(m => m.XbmcUpdateLibrary)</span>
</label> </label>
@Html.CheckBoxFor(m => m.XbmcNotifyOnRename, new { @class = "inputClass checkClass" }) @Html.CheckBoxFor(m => m.XbmcUpdateLibrary, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcNotificationImage) <label class="labelClass">@Html.LabelFor(m => m.XbmcCleanLibrary)
<span class="small">@Html.DescriptionFor(m => m.XbmcNotificationImage)</span> <span class="small">@Html.DescriptionFor(m => m.XbmcCleanLibrary)</span>
</label> </label>
@Html.CheckBoxFor(m => m.XbmcNotificationImage, new { @class = "inputClass checkClass" }) @Html.CheckBoxFor(m => m.XbmcCleanLibrary, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcDisplayTime)
<span class="small">@Html.DescriptionFor(m => m.XbmcDisplayTime)</span>
</label>
@Html.TextBoxFor(m => m.XbmcDisplayTime, new { @class = "inputClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcUpdateOnDownload)
<span class="small">@Html.DescriptionFor(m => m.XbmcUpdateOnDownload)</span>
</label>
@Html.CheckBoxFor(m => m.XbmcUpdateOnDownload, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcUpdateOnRename)
<span class="small">@Html.DescriptionFor(m => m.XbmcUpdateOnRename)</span>
</label>
@Html.CheckBoxFor(m => m.XbmcUpdateOnRename, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcFullUpdate)
<span class="small">@Html.DescriptionFor(m => m.XbmcFullUpdate)</span>
</label>
@Html.CheckBoxFor(m => m.XbmcFullUpdate, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcCleanOnDownload)
<span class="small">@Html.DescriptionFor(m => m.XbmcCleanOnDownload)</span>
</label>
@Html.CheckBoxFor(m => m.XbmcCleanOnDownload, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcCleanOnRename)
<span class="small">@Html.DescriptionFor(m => m.XbmcCleanOnRename)</span>
</label>
@Html.CheckBoxFor(m => m.XbmcCleanOnRename, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.XbmcHosts) <label class="labelClass">@Html.LabelFor(m => m.XbmcHosts)
<span class="small">@Html.DescriptionFor(m => m.XbmcHosts)</span> <span class="small">@Html.DescriptionFor(m => m.XbmcHosts)</span>

View File

@ -42,7 +42,6 @@ Settings
<br /> <br />
<button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> <button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/>
</div> </div>
} }
</div> </div>
<div id="result" class="hiddenResult"> <div id="result" class="hiddenResult">