Fixed: Connecting to XBMC when user name and password are configured

pull/4/head
Mark McDowall 10 years ago
parent 6ee4b6d428
commit fc46018c9a

@ -1,8 +1,7 @@
using FluentAssertions; using System;
using Moq; using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc; using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Notifications.Xbmc.Model; using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -17,16 +16,15 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_settings = new XbmcSettings _settings = Builder<XbmcSettings>.CreateNew()
.Build();
}
private void GivenVersionResponse(String response)
{ {
Host = "localhost", Mocker.GetMock<IXbmcJsonApiProxy>()
Port = 8080, .Setup(s => s.GetJsonVersion(_settings))
Username = "xbmc", .Returns(response);
Password = "xbmc",
AlwaysUpdate = false,
CleanLibrary = false,
UpdateLibrary = true
};
} }
[TestCase(3)] [TestCase(3)]
@ -34,11 +32,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
[TestCase(0)] [TestCase(0)]
public void should_get_version_from_major_only(int number) public void should_get_version_from_major_only(int number)
{ {
var message = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":" + number + "}}"; GivenVersionResponse("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":" + number + "}}");
var fakeHttp = Mocker.GetMock<IHttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(number)); Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(number));
} }
@ -50,11 +44,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
[TestCase(0, 0, 0)] [TestCase(0, 0, 0)]
public void should_get_version_from_semantic_version(int major, int minor, int patch) public void should_get_version_from_semantic_version(int major, int minor, int patch)
{ {
var message = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":{\"major\":" + major + ",\"minor\":" + minor + ",\"patch\":" + patch + "}}}"; GivenVersionResponse("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":{\"major\":" + major + ",\"minor\":" + minor + ",\"patch\":" + patch + "}}}");
var fakeHttp = Mocker.GetMock<IHttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(major, minor, patch)); Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(major, minor, patch));
} }
@ -62,11 +52,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
[Test] [Test]
public void should_get_version_zero_when_an_error_is_received() public void should_get_version_zero_when_an_error_is_received()
{ {
var message = "{\"error\":{\"code\":-32601,\"message\":\"Method not found.\"},\"id\":10,\"jsonrpc\":\"2.0\"}"; GivenVersionResponse("{\"error\":{\"code\":-32601,\"message\":\"Method not found.\"},\"id\":10,\"jsonrpc\":\"2.0\"}");
var fakeHttp = Mocker.GetMock<IHttpProvider>();
fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny<string>()))
.Returns(message);
Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(0)); Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(0));
} }

@ -1,120 +0,0 @@
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
{
[TestFixture]
public class ActivePlayersFixture : CoreTest<JsonApiProvider>
{
private XbmcSettings _settings;
private void WithNoActivePlayers()
{
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.IsAny<string>()))
.Returns("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":[]}");
}
private void WithVideoPlayerActive()
{
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.IsAny<string>()))
.Returns("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":[{\"playerid\":1,\"type\":\"video\"}]}");
}
private void WithAudioPlayerActive()
{
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.IsAny<string>()))
.Returns("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":[{\"playerid\":1,\"type\":\"audio\"}]}");
}
private void WithPicturePlayerActive()
{
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.IsAny<string>()))
.Returns("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":[{\"playerid\":1,\"type\":\"picture\"}]}");
}
private void WithAllPlayersActive()
{
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.IsAny<string>()))
.Returns("{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":[{\"playerid\":1,\"type\":\"audio\"},{\"playerid\":2,\"type\":\"picture\"},{\"playerid\":3,\"type\":\"video\"}]}");
}
[SetUp]
public void Setup()
{
_settings = new XbmcSettings
{
Host = "localhost",
Port = 8080,
Username = "xbmc",
Password = "xbmc",
AlwaysUpdate = false,
CleanLibrary = false,
UpdateLibrary = true
};
}
[Test]
public void _should_be_empty_when_no_active_players()
{
WithNoActivePlayers();
Subject.GetActivePlayers(_settings).Should().BeEmpty();
}
[Test]
public void should_have_active_video_player()
{
WithVideoPlayerActive();
var result = Subject.GetActivePlayers(_settings);
result.Should().HaveCount(1);
result.First().Type.Should().Be("video");
}
[Test]
public void should_have_active_audio_player()
{
WithAudioPlayerActive();
var result = Subject.GetActivePlayers(_settings);
result.Should().HaveCount(1);
result.First().Type.Should().Be("audio");
}
[Test]
public void should_have_active_picture_player()
{
WithPicturePlayerActive();
var result = Subject.GetActivePlayers(_settings);
result.Should().HaveCount(1);
result.First().Type.Should().Be("picture");
}
[Test]
public void should_have_all_players_active()
{
WithAllPlayersActive();
var result = Subject.GetActivePlayers(_settings);
result.Should().HaveCount(3);
result.Select(a => a.PlayerId).Distinct().Should().HaveCount(3);
result.Select(a => a.Type).Distinct().Should().HaveCount(3);
}
}
}

@ -1,36 +0,0 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
{
[TestFixture]
public class CheckForErrorFixture : CoreTest<JsonApiProvider>
{
[Test]
public void should_be_true_when_the_response_contains_an_error()
{
const string response = "{\"error\":{\"code\":-32601,\"message\":\"Method not found.\"},\"id\":10,\"jsonrpc\":\"2.0\"}";
Subject.CheckForError(response).Should().BeTrue();
}
[Test]
public void JsonError_true_empty_response()
{
var response = String.Empty;
Subject.CheckForError(response).Should().BeTrue();
}
[Test]
public void JsonError_false()
{
const string response = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":3}}";
Subject.CheckForError(response).Should().BeFalse();
}
}
}

@ -1,9 +1,14 @@
using FluentAssertions; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc; using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -15,42 +20,28 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
private XbmcSettings _settings; private XbmcSettings _settings;
private Series _series; private Series _series;
private string _response; private string _response;
private List<TvShow> _xbmcSeries;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_settings = new XbmcSettings _settings = Builder<XbmcSettings>.CreateNew()
{ .Build();
Host = "localhost",
Port = 8080,
Username = "xbmc",
Password = "xbmc",
AlwaysUpdate = false,
CleanLibrary = false,
UpdateLibrary = true
};
_response = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"limits\":" + _xbmcSeries = Builder<TvShow>.CreateListOfSize(3)
"{\"end\":5,\"start\":0,\"total\":5},\"tvshows\":[{\"file\"" + .Build()
":\"smb://HOMESERVER/TV/7th Heaven/\",\"imdbnumber\":\"73928\"," + .ToList();
"\"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}]}}";
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IXbmcJsonApiProxy>()
.Setup( .Setup(s => s.GetSeries(_settings))
s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.IsAny<string>())) .Returns(_xbmcSeries);
.Returns(_response);
} }
private void WithMatchingTvdbId() private void WithMatchingTvdbId()
{ {
_series = new Series _series = new Series
{ {
TvdbId = 78461, TvdbId = _xbmcSeries.First().ImdbNumber,
Title = "TV Show" Title = "TV Show"
}; };
} }
@ -59,8 +50,8 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
{ {
_series = new Series _series = new Series
{ {
TvdbId = 1, TvdbId = 1000,
Title = "30 Rock" Title = _xbmcSeries.First().Label
}; };
} }
@ -68,7 +59,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
{ {
_series = new Series _series = new Series
{ {
TvdbId = 1, TvdbId = 1000,
Title = "Does not exist" Title = "Does not exist"
}; };
} }
@ -86,7 +77,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
{ {
WithMatchingTvdbId(); WithMatchingTvdbId();
Subject.GetSeriesPath(_settings, _series).Should().Be("smb://HOMESERVER/TV/8 Simple Rules/"); Subject.GetSeriesPath(_settings, _series).Should().Be(_xbmcSeries.First().File);
} }
[Test] [Test]
@ -94,7 +85,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
{ {
WithMatchingTitle(); WithMatchingTitle();
Subject.GetSeriesPath(_settings, _series).Should().Be("smb://HOMESERVER/TV/30 Rock/"); Subject.GetSeriesPath(_settings, _series).Should().Be(_xbmcSeries.First().File);
} }
} }
} }

@ -1,10 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc; using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -14,88 +15,53 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
public class UpdateFixture : CoreTest<JsonApiProvider> public class UpdateFixture : CoreTest<JsonApiProvider>
{ {
private XbmcSettings _settings; private XbmcSettings _settings;
const string _expectedJson = "{\"jsonrpc\":\"2.0\",\"method\":\"VideoLibrary.GetTvShows\",\"params\":{\"properties\":[\"file\",\"imdbnumber\"]},\"id\":"; private Series _series;
private List<TvShow> _xbmcSeries;
private const string _tvshowsResponse = "{\"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}]}}";
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_settings = new XbmcSettings _settings = Builder<XbmcSettings>.CreateNew()
{ .Build();
Host = "localhost",
Port = 8080, _xbmcSeries = Builder<TvShow>.CreateListOfSize(3)
Username = "xbmc", .Build()
Password = "xbmc", .ToList();
AlwaysUpdate = false,
CleanLibrary = false, Mocker.GetMock<IXbmcJsonApiProxy>()
UpdateLibrary = true .Setup(s => s.GetSeries(_settings))
}; .Returns(_xbmcSeries);
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IXbmcJsonApiProxy>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, .Setup(s => s.GetActivePlayers(_settings))
It.Is<string>(e => e.Replace(" ", "").Replace("\r\n", "").Replace("\t", "").Contains(_expectedJson.Replace(" ", ""))))) .Returns(new List<ActivePlayer>());
.Returns(_tvshowsResponse);
} }
[Test] [Test]
public void should_update_using_series_path() public void should_update_using_series_path()
{ {
var fakeSeries = Builder<Series>.CreateNew() var series = Builder<Series>.CreateNew()
.With(s => s.TvdbId = 79488) .With(s => s.TvdbId = _xbmcSeries.First().ImdbNumber)
.With(s => s.Title = "30 Rock")
.Build(); .Build();
Mocker.GetMock<IHttpProvider>() Subject.Update(_settings, series);
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.Is<String>(
e => e.Replace(" ", "")
.Replace("\r\n", "")
.Replace("\t", "")
.Contains("\"params\":{\"directory\":\"smb://HOMESERVER/TV/30Rock/\"}"))))
.Returns("{\"id\":55,\"jsonrpc\":\"2.0\",\"result\":\"OK\"}");
Subject.Update(_settings, fakeSeries);
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IXbmcJsonApiProxy>()
.Verify(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.Is<String>( .Verify(v => v.UpdateLibrary(_settings, It.IsAny<String>()), Times.Once());
e => e.Replace(" ", "")
.Replace("\r\n", "")
.Replace("\t", "")
.Contains("\"params\":{\"directory\":\"smb://HOMESERVER/TV/30Rock/\"}"))), Times.Once());
} }
[Test] [Test]
public void should_update_all_paths_when_series_path_not_found() public void should_update_all_paths_when_series_path_not_found()
{ {
var fakeSeries = Builder<Series>.CreateNew() var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.TvdbId = 1) .With(s => s.TvdbId = 1000)
.With(s => s.Title = "Not 30 Rock") .With(s => s.Title = "Not 30 Rock")
.Build(); .Build();
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.Is<String>(
e => !e.Replace(" ", "")
.Replace("\r\n", "")
.Replace("\t", "")
.Contains("\"params\":{\"directory\":\"smb://HOMESERVER/TV/30Rock/\"}"))))
.Returns("{\"id\":55,\"jsonrpc\":\"2.0\",\"result\":\"OK\"}");
Subject.Update(_settings, fakeSeries); Subject.Update(_settings, fakeSeries);
Mocker.GetMock<IHttpProvider>() Mocker.GetMock<IXbmcJsonApiProxy>()
.Verify(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password, It.Is<String>( .Verify(v => v.UpdateLibrary(_settings, null), Times.Once());
e => e.Replace(" ", "")
.Replace("\r\n", "")
.Replace("\t", "")
.Contains("\"params\":{\"directory\":\"smb://HOMESERVER/TV/30Rock/\"}"))), Times.Never());
} }
} }
} }

@ -208,8 +208,6 @@
<Compile Include="NotificationTests\Xbmc\Http\CheckForErrorFixture.cs" /> <Compile Include="NotificationTests\Xbmc\Http\CheckForErrorFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Http\GetSeriesPathFixture.cs" /> <Compile Include="NotificationTests\Xbmc\Http\GetSeriesPathFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Http\UpdateFixture.cs" /> <Compile Include="NotificationTests\Xbmc\Http\UpdateFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\ActivePlayersFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\CheckForErrorFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\GetSeriesPathFixture.cs" /> <Compile Include="NotificationTests\Xbmc\Json\GetSeriesPathFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\UpdateFixture.cs" /> <Compile Include="NotificationTests\Xbmc\Json\UpdateFixture.cs" />
<Compile Include="NotificationTests\Xbmc\OnDownloadFixture.cs" /> <Compile Include="NotificationTests\Xbmc\OnDownloadFixture.cs" />

@ -22,6 +22,11 @@ namespace NzbDrone.Core.Notifications.Xbmc
_logger = logger; _logger = logger;
} }
public bool CanHandle(XbmcVersion version)
{
return version < new XbmcVersion(5);
}
public void Notify(XbmcSettings settings, string title, string message) public void Notify(XbmcSettings settings, string title, string message)
{ {
var notification = String.Format("Notification({0},{1},{2},{3})", title, message, settings.DisplayTime * 1000, "https://raw.github.com/NzbDrone/NzbDrone/develop/Logo/64.png"); var notification = String.Format("Notification({0},{1},{2},{3})", title, message, settings.DisplayTime * 1000, "https://raw.github.com/NzbDrone/NzbDrone/develop/Logo/64.png");
@ -55,7 +60,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
SendCommand(settings, command); SendCommand(settings, command);
} }
public List<ActivePlayer> GetActivePlayers(XbmcSettings settings) internal List<ActivePlayer> GetActivePlayers(XbmcSettings settings)
{ {
try try
{ {
@ -76,31 +81,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
return new List<ActivePlayer>(); return new List<ActivePlayer>();
} }
public bool CheckForError(string response) internal string GetSeriesPath(XbmcSettings settings, Series series)
{
_logger.Debug("Looking for error in response: {0}", response);
if (String.IsNullOrWhiteSpace(response))
{
_logger.Debug("Invalid response from XBMC, the response is not valid JSON");
return true;
}
var errorIndex = response.IndexOf("Error", StringComparison.InvariantCultureIgnoreCase);
if (errorIndex > -1)
{
var errorMessage = response.Substring(errorIndex + 6);
errorMessage = errorMessage.Substring(0, errorMessage.IndexOfAny(new char[] { '<', ';' }));
_logger.Debug("Error found in response: {0}", errorMessage);
return true;
}
return false;
}
public string GetSeriesPath(XbmcSettings settings, Series series)
{ {
var query = var query =
String.Format( String.Format(
@ -133,9 +114,28 @@ namespace NzbDrone.Core.Notifications.Xbmc
return field.Value; return field.Value;
} }
public bool CanHandle(XbmcVersion version) internal bool CheckForError(string response)
{ {
return version < new XbmcVersion(5); _logger.Debug("Looking for error in response: {0}", response);
if (String.IsNullOrWhiteSpace(response))
{
_logger.Debug("Invalid response from XBMC, the response is not valid JSON");
return true;
}
var errorIndex = response.IndexOf("Error", StringComparison.InvariantCultureIgnoreCase);
if (errorIndex > -1)
{
var errorMessage = response.Substring(errorIndex + 6);
errorMessage = errorMessage.Substring(0, errorMessage.IndexOfAny(new char[] { '<', ';' }));
_logger.Debug("Error found in response: {0}", errorMessage);
return true;
}
return false;
} }
private void UpdateLibrary(XbmcSettings settings, Series series) private void UpdateLibrary(XbmcSettings settings, Series series)

@ -1,5 +1,4 @@
using System.Collections.Generic; using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Xbmc namespace NzbDrone.Core.Notifications.Xbmc
@ -9,9 +8,6 @@ namespace NzbDrone.Core.Notifications.Xbmc
void Notify(XbmcSettings settings, string title, string message); void Notify(XbmcSettings settings, string title, string message);
void Update(XbmcSettings settings, Series series); void Update(XbmcSettings settings, Series series);
void Clean(XbmcSettings settings); void Clean(XbmcSettings settings);
List<ActivePlayer> GetActivePlayers(XbmcSettings settings);
bool CheckForError(string response);
string GetSeriesPath(XbmcSettings settings, Series series);
bool CanHandle(XbmcVersion version); bool CanHandle(XbmcVersion version);
} }
} }

@ -13,26 +13,23 @@ namespace NzbDrone.Core.Notifications.Xbmc
{ {
public class JsonApiProvider : IApiProvider public class JsonApiProvider : IApiProvider
{ {
private readonly IHttpProvider _httpProvider; private readonly IXbmcJsonApiProxy _proxy;
private readonly Logger _logger; private readonly Logger _logger;
public JsonApiProvider(IHttpProvider httpProvider, Logger logger) public JsonApiProvider(IXbmcJsonApiProxy proxy, Logger logger)
{ {
_httpProvider = httpProvider; _proxy = proxy;
_logger = logger; _logger = logger;
} }
public void Notify(XbmcSettings settings, string title, string message) public bool CanHandle(XbmcVersion version)
{ {
var parameters = new JObject( return version >= new XbmcVersion(5);
new JProperty("title", title), }
new JProperty("message", message),
new JProperty("image", "https://raw.github.com/NzbDrone/NzbDrone/develop/Logo/64.png"),
new JProperty("displaytime", settings.DisplayTime * 1000));
var postJson = BuildJsonRequest("GUI.ShowNotification", parameters);
_httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString()); public void Notify(XbmcSettings settings, string title, string message)
{
_proxy.Notify(settings, title, message);
} }
public void Update(XbmcSettings settings, Series series) public void Update(XbmcSettings settings, Series series)
@ -40,7 +37,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
if (!settings.AlwaysUpdate) if (!settings.AlwaysUpdate)
{ {
_logger.Debug("Determining if there are any active players on XBMC host: {0}", settings.Address); _logger.Debug("Determining if there are any active players on XBMC host: {0}", settings.Address);
var activePlayers = GetActivePlayers(settings); var activePlayers = _proxy.GetActivePlayers(settings);
if (activePlayers.Any(a => a.Type.Equals("video"))) if (activePlayers.Any(a => a.Type.Equals("video")))
{ {
@ -54,64 +51,17 @@ namespace NzbDrone.Core.Notifications.Xbmc
public void Clean(XbmcSettings settings) public void Clean(XbmcSettings settings)
{ {
var postJson = BuildJsonRequest("VideoLibrary.Clean"); _proxy.CleanLibrary(settings);
_httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString());
} }
public List<ActivePlayer> GetActivePlayers(XbmcSettings settings) public List<ActivePlayer> GetActivePlayers(XbmcSettings settings)
{ {
try return _proxy.GetActivePlayers(settings);
{
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(settings.Address, settings.Username, settings.Password, postJson.ToString());
if (CheckForError(response))
return new List<ActivePlayer>();
var result = Json.Deserialize<ActivePlayersEdenResult>(response);
return result.Result;
}
catch (Exception ex)
{
_logger.DebugException(ex.Message, ex);
}
return new List<ActivePlayer>();
}
public bool CheckForError(string response)
{
_logger.Debug("Looking for error in response: {0}", response);
if (String.IsNullOrWhiteSpace(response))
{
_logger.Debug("Invalid response from XBMC, the response is not valid JSON");
return true;
} }
if (response.StartsWith("{\"error\"")) public String GetSeriesPath(XbmcSettings settings, Series series)
{ {
var error = Json.Deserialize<ErrorResult>(response); var allSeries = _proxy.GetSeries(settings);
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;
}
public string GetSeriesPath(XbmcSettings settings, Series series)
{
var allSeries = GetSeries(settings);
if (!allSeries.Any()) if (!allSeries.Any())
{ {
@ -126,11 +76,6 @@ namespace NzbDrone.Core.Notifications.Xbmc
return null; return null;
} }
public bool CanHandle(XbmcVersion version)
{
return version >= new XbmcVersion(5);
}
private void UpdateLibrary(XbmcSettings settings, Series series) private void UpdateLibrary(XbmcSettings settings, Series series)
{ {
try try
@ -142,27 +87,17 @@ namespace NzbDrone.Core.Notifications.Xbmc
if (seriesPath != null) if (seriesPath != null)
{ {
_logger.Debug("Updating series {0} (Path: {1}) on XBMC host: {2}", series, seriesPath, settings.Address); _logger.Debug("Updating series {0} (Path: {1}) on XBMC host: {2}", series, seriesPath, settings.Address);
var parameters = new JObject(new JObject(new JProperty("directory", seriesPath)));
postJson = BuildJsonRequest("VideoLibrary.Scan", parameters);
} }
else else
{ {
_logger.Debug("Series {0} doesn't exist on XBMC host: {1}, Updating Entire Library", series, _logger.Debug("Series {0} doesn't exist on XBMC host: {1}, Updating Entire Library", series,
settings.Address); settings.Address);
postJson = BuildJsonRequest("VideoLibrary.Scan");
} }
var response = _httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString()); var response = _proxy.UpdateLibrary(settings, null);
if (CheckForError(response)) return; if (!response.Equals("OK", StringComparison.InvariantCultureIgnoreCase))
_logger.Debug(" from response");
var result = Json.Deserialize<XbmcJsonResult<String>>(response);
if (!result.Result.Equals("OK", StringComparison.InvariantCultureIgnoreCase))
{ {
_logger.Debug("Failed to update library for: {0}", settings.Address); _logger.Debug("Failed to update library for: {0}", settings.Address);
} }
@ -173,51 +108,5 @@ namespace NzbDrone.Core.Notifications.Xbmc
_logger.DebugException(ex.Message, ex); _logger.DebugException(ex.Message, ex);
} }
} }
private List<TvShow> GetSeries(XbmcSettings settings)
{
try
{
var properties = new JObject { new JProperty("properties", new[] { "file", "imdbnumber" }) };
var postJson = BuildJsonRequest("VideoLibrary.GetTvShows", properties);
var response = _httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString());
if (CheckForError(response))
return new List<TvShow>();
var result = Json.Deserialize<TvShowResponse>(response);
var shows = result.Result.TvShows;
return shows;
}
catch (Exception ex)
{
_logger.DebugException(ex.Message, ex);
}
return new List<TvShow>();
}
private JObject BuildJsonRequest(string method)
{
return BuildJsonRequest(method, null);
}
private JObject BuildJsonRequest(string method, JObject parameters)
{
var postJson = new JObject();
postJson.Add(new JProperty("jsonrpc", "2.0"));
postJson.Add(new JProperty("method", method));
if (parameters != null)
{
postJson.Add(new JProperty("params", parameters));
}
postJson.Add(new JProperty("id", 2));
return postJson;
}
} }
} }

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Rest;
using NzbDrone.Core.Tv;
using RestSharp;
namespace NzbDrone.Core.Notifications.Xbmc
{
public interface IXbmcJsonApiProxy
{
String GetJsonVersion(XbmcSettings settings);
void Notify(XbmcSettings settings, String title, String message);
String UpdateLibrary(XbmcSettings settings, String path);
void CleanLibrary(XbmcSettings settings);
List<ActivePlayer> GetActivePlayers(XbmcSettings settings);
List<TvShow> GetSeries(XbmcSettings settings);
}
public class XbmcJsonApiProxy : IXbmcJsonApiProxy
{
private readonly Logger _logger;
public XbmcJsonApiProxy(Logger logger)
{
_logger = logger;
}
public String GetJsonVersion(XbmcSettings settings)
{
var request = new RestRequest();
return ProcessRequest(request, settings, "JSONRPC.Version");
}
public void Notify(XbmcSettings settings, String title, String message)
{
var request = new RestRequest();
var parameters = new Dictionary<String, Object>();
parameters.Add("title", title);
parameters.Add("message", message);
parameters.Add("image", "https://raw.github.com/NzbDrone/NzbDrone/develop/Logo/64.png");
parameters.Add("displaytime", settings.DisplayTime * 1000);
ProcessRequest(request, settings, "GUI.ShowNotification", parameters);
}
public String UpdateLibrary(XbmcSettings settings, String path)
{
var request = new RestRequest();
var parameters = new Dictionary<String, Object>();
parameters.Add("directory", path );
if (path.IsNullOrWhiteSpace())
{
parameters = null;
}
var response = ProcessRequest(request, settings, "VideoLibrary.Scan", parameters);
return Json.Deserialize<XbmcJsonResult<String>>(response).Result;
}
public void CleanLibrary(XbmcSettings settings)
{
var request = new RestRequest();
ProcessRequest(request, settings, "VideoLibrary.Clean");
}
public List<ActivePlayer> GetActivePlayers(XbmcSettings settings)
{
var request = new RestRequest();
var response = ProcessRequest(request, settings, "Player.GetActivePlayers");
return Json.Deserialize<ActivePlayersEdenResult>(response).Result;
}
public List<TvShow> GetSeries(XbmcSettings settings)
{
var request = new RestRequest();
var parameters = new Dictionary<String, Object>();
parameters.Add("properties", new[] { "file", "imdbnumber" });
var response = ProcessRequest(request, settings, "VideoLibrary.GetTvShows", parameters);
return Json.Deserialize<TvShowResponse>(response).Result.TvShows;
}
private String ProcessRequest(IRestRequest request, XbmcSettings settings, String method, Dictionary<String, Object> parameters = null)
{
var client = BuildClient(settings);
request.Method = Method.POST;
request.RequestFormat = DataFormat.Json;
request.JsonSerializer = new JsonNetSerializer();
request.AddBody(new { jsonrpc = "2.0", method = method, id = 10, @params = parameters });
var response = client.ExecuteAndValidate(request);
_logger.Trace("Response: {0}", response.Content);
CheckForError(response);
return response.Content;
}
private IRestClient BuildClient(XbmcSettings settings)
{
var url = string.Format(@"http://{0}/jsonrpc", settings.Address);
_logger.Debug("Url: " + url);
var client = RestClientFactory.BuildClient(url);
if (!settings.Username.IsNullOrWhiteSpace())
{
client.Authenticator = new HttpBasicAuthenticator(settings.Username, settings.Password);
}
return client;
}
private void CheckForError(IRestResponse response)
{
_logger.Debug("Looking for error in response: {0}", response);
if (String.IsNullOrWhiteSpace(response.Content))
{
throw new XbmcJsonException("Invalid response from XBMC, the response is not valid JSON");
}
if (response.Content.StartsWith("{\"error\""))
{
var error = Json.Deserialize<ErrorResult>(response.Content);
var code = error.Error["code"];
var message = error.Error["message"];
var errorMessage = String.Format("XBMC Json Error. Code = {0}, Message: {1}", code, message);
throw new XbmcJsonException(errorMessage);
}
}
}
}

@ -0,0 +1,16 @@
using System;
namespace NzbDrone.Core.Notifications.Xbmc
{
public class XbmcJsonException : Exception
{
public XbmcJsonException()
{
}
public XbmcJsonException(string message)
: base(message)
{
}
}
}

@ -4,8 +4,6 @@ using System.Linq;
using FluentValidation.Results; using FluentValidation.Results;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Notifications.Xbmc.Model; using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -23,13 +21,13 @@ namespace NzbDrone.Core.Notifications.Xbmc
public class XbmcService : IXbmcService public class XbmcService : IXbmcService
{ {
private readonly IHttpProvider _httpProvider; private readonly IXbmcJsonApiProxy _proxy;
private readonly IEnumerable<IApiProvider> _apiProviders; private readonly IEnumerable<IApiProvider> _apiProviders;
private readonly Logger _logger; private readonly Logger _logger;
public XbmcService(IHttpProvider httpProvider, IEnumerable<IApiProvider> apiProviders, Logger logger) public XbmcService(IXbmcJsonApiProxy proxy, IEnumerable<IApiProvider> apiProviders, Logger logger)
{ {
_httpProvider = httpProvider; _proxy = proxy;
_apiProviders = apiProviders; _apiProviders = apiProviders;
_logger = logger; _logger = logger;
} }
@ -56,12 +54,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
{ {
try try
{ {
var postJson = new JObject(); var response = _proxy.GetJsonVersion(settings);
postJson.Add(new JProperty("jsonrpc", "2.0"));
postJson.Add(new JProperty("method", "JSONRPC.Version"));
postJson.Add(new JProperty("id", 1));
var response = _httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString());
_logger.Debug("Getting version from response: " + response); _logger.Debug("Getting version from response: " + response);
var result = Json.Deserialize<XbmcJsonResult<JObject>>(response); var result = Json.Deserialize<XbmcJsonResult<JObject>>(response);

@ -603,6 +603,7 @@
<Compile Include="Notifications\Pushover\PushoverPriority.cs" /> <Compile Include="Notifications\Pushover\PushoverPriority.cs" />
<Compile Include="Notifications\Pushover\PushoverService.cs" /> <Compile Include="Notifications\Pushover\PushoverService.cs" />
<Compile Include="Notifications\Pushover\PushoverSettings.cs" /> <Compile Include="Notifications\Pushover\PushoverSettings.cs" />
<Compile Include="Notifications\Xbmc\XbmcJsonException.cs" />
<Compile Include="Notifications\Xbmc\HttpApiProvider.cs" /> <Compile Include="Notifications\Xbmc\HttpApiProvider.cs" />
<Compile Include="Notifications\Xbmc\IApiProvider.cs" /> <Compile Include="Notifications\Xbmc\IApiProvider.cs" />
<Compile Include="Notifications\Xbmc\InvalidXbmcVersionException.cs" /> <Compile Include="Notifications\Xbmc\InvalidXbmcVersionException.cs" />
@ -620,6 +621,7 @@
<Compile Include="Notifications\Xbmc\Xbmc.cs"> <Compile Include="Notifications\Xbmc\Xbmc.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Notifications\Xbmc\XbmcJsonApiProxy.cs" />
<Compile Include="Notifications\Xbmc\XbmcService.cs"> <Compile Include="Notifications\Xbmc\XbmcService.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>

Loading…
Cancel
Save