diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj
index 62a0028a6..e1bb25519 100644
--- a/NzbDrone.Common/NzbDrone.Common.csproj
+++ b/NzbDrone.Common/NzbDrone.Common.csproj
@@ -138,7 +138,7 @@
-
+
@@ -155,7 +155,6 @@
-
diff --git a/NzbDrone.Common/StringExtention.cs b/NzbDrone.Common/StringExtension.cs
similarity index 100%
rename from NzbDrone.Common/StringExtention.cs
rename to NzbDrone.Common/StringExtension.cs
diff --git a/NzbDrone.Common/UdpProvider.cs b/NzbDrone.Common/UdpProvider.cs
deleted file mode 100644
index 94956c125..000000000
--- a/NzbDrone.Common/UdpProvider.cs
+++ /dev/null
@@ -1,191 +0,0 @@
-using System.Linq;
-using System;
-using System.Net;
-using System.Net.Sockets;
-using NLog;
-
-namespace NzbDrone.Common
-{
- public class UdpProvider
- {
- private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
-
- 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] = ((ushort)PacketType.Blob & 0xff00) >> 8;
- header[7] = (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)
- {
- Logger.TraceException(exc.Message, exc);
- return null;
- }
- }
-
- private void Disconnect(Socket socket)
- {
- try
- {
- if (socket != null)
- {
- socket.Shutdown(SocketShutdown.Both);
- socket.Close();
- }
- }
- catch
- {
- }
- }
- }
-}
diff --git a/NzbDrone.Core.Test/ProviderTests/GrowlProviderTest.cs b/NzbDrone.Core.Test/NotificationTests/GrowlProviderTest.cs
similarity index 100%
rename from NzbDrone.Core.Test/ProviderTests/GrowlProviderTest.cs
rename to NzbDrone.Core.Test/NotificationTests/GrowlProviderTest.cs
diff --git a/NzbDrone.Core.Test/NotificationTests/NotificationServiceFixture.cs b/NzbDrone.Core.Test/NotificationTests/NotificationServiceFixture.cs
index 8ad4812f4..2c0d86add 100644
--- a/NzbDrone.Core.Test/NotificationTests/NotificationServiceFixture.cs
+++ b/NzbDrone.Core.Test/NotificationTests/NotificationServiceFixture.cs
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.NotificationTests
{
_notifications = new List();
- _notifications.Add(new Xbmc(null, null));
+ _notifications.Add(new Notifications.Xbmc.Xbmc(null, null));
_notifications.Add(new PlexClient(null));
_notifications.Add(new PlexServer(null));
_notifications.Add(new Email(null));
@@ -49,8 +49,8 @@ namespace NzbDrone.Core.Test.NotificationTests
{
Mocker.SetConstant(Mocker.Resolve());
- Mocker.GetMock().Setup(s => s.Resolve(typeof (Xbmc)))
- .Returns(new Xbmc(null, null));
+ Mocker.GetMock().Setup(s => s.Resolve(typeof(Notifications.Xbmc.Xbmc)))
+ .Returns(new Notifications.Xbmc.Xbmc(null, null));
Mocker.GetMock().Setup(s => s.Resolve(typeof(PlexClient)))
.Returns(new PlexClient(null));
diff --git a/NzbDrone.Core.Test/ProviderTests/PlexProviderTest.cs b/NzbDrone.Core.Test/NotificationTests/PlexProviderTest.cs
similarity index 100%
rename from NzbDrone.Core.Test/ProviderTests/PlexProviderTest.cs
rename to NzbDrone.Core.Test/NotificationTests/PlexProviderTest.cs
diff --git a/NzbDrone.Core.Test/ProviderTests/ProwlProviderTest.cs b/NzbDrone.Core.Test/NotificationTests/ProwlProviderTest.cs
similarity index 100%
rename from NzbDrone.Core.Test/ProviderTests/ProwlProviderTest.cs
rename to NzbDrone.Core.Test/NotificationTests/ProwlProviderTest.cs
diff --git a/NzbDrone.Core.Test/NotificationTests/Xbmc/GetJsonVersionFixture.cs b/NzbDrone.Core.Test/NotificationTests/Xbmc/GetJsonVersionFixture.cs
new file mode 100644
index 000000000..388c1cccc
--- /dev/null
+++ b/NzbDrone.Core.Test/NotificationTests/Xbmc/GetJsonVersionFixture.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Notifications.Xbmc;
+using NzbDrone.Core.Tv;
+using NzbDrone.Core.Model.Xbmc;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common.AutoMoq;
+
+namespace NzbDrone.Core.Test.NotificationTests.Xbmc
+{
+ [TestFixture]
+ public class GetJsonVersionFixture : CoreTest
+ {
+ private XbmcSettings _settings;
+
+ [SetUp]
+ public void Setup()
+ {
+ _settings = new XbmcSettings
+ {
+ Host = "localhost",
+ Port = 8080,
+ Username = "xbmc",
+ Password = "xbmc",
+ AlwaysUpdate = false,
+ CleanLibrary = false,
+ UpdateLibrary = true
+ };
+ }
+
+ [TestCase(3)]
+ [TestCase(2)]
+ [TestCase(0)]
+ public void should_get_version_from_major_only(int number)
+ {
+ var message = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"version\":" + number + "}}";
+
+ var fakeHttp = Mocker.GetMock();
+ fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny()))
+ .Returns(message);
+
+ Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(number));
+ }
+
+ [TestCase(5, 0, 0)]
+ [TestCase(6, 0, 0)]
+ [TestCase(6, 1, 0)]
+ [TestCase(6, 0, 23)]
+ [TestCase(0, 0, 0)]
+ 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 + "}}}";
+
+ var fakeHttp = Mocker.GetMock();
+ fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny()))
+ .Returns(message);
+
+ Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(major, minor, patch));
+ }
+
+ [Test]
+ 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\"}";
+
+ var fakeHttp = Mocker.GetMock();
+ fakeHttp.Setup(s => s.PostCommand("localhost:8080", "xbmc", "xbmc", It.IsAny()))
+ .Returns(message);
+
+ Subject.GetJsonVersion(_settings).Should().Be(new XbmcVersion(0));
+ }
+ }
+}
\ No newline at end of file
diff --git a/NzbDrone.Core.Test/NotificationTests/Xbmc/Http/ActivePlayersFixture.cs b/NzbDrone.Core.Test/NotificationTests/Xbmc/Http/ActivePlayersFixture.cs
new file mode 100644
index 000000000..ca412a3c1
--- /dev/null
+++ b/NzbDrone.Core.Test/NotificationTests/Xbmc/Http/ActivePlayersFixture.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Notifications.Xbmc;
+using NzbDrone.Core.Test.Framework;
+
+namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Http
+{
+ [TestFixture]
+ public class ActivePlayersFixture : CoreTest
+ {
+ private XbmcSettings _settings;
+ private string _expectedUrl;
+
+ private void WithNoActivePlayers()
+ {
+ Mocker.GetMock()
+ .Setup(s => s.DownloadString(_expectedUrl, _settings.Username, _settings.Password))
+ .Returns("Filename:[Nothing Playing]");
+ }
+
+ private void WithVideoPlayerActive()
+ {
+ var activePlayers = @"Filename:C:\Test\TV\2 Broke Girls\Season 01\2 Broke Girls - S01E01 - Pilot [SDTV].avi" +
+ "PlayStatus:PlayingVideoNo:0Type:VideoThumb:special://masterprofile/Thumbnails/Video/a/auto-a664d5a2.tbn" +
+ "Time:00:06Duration:21:35Percentage:0File size:183182590Changed:True";
+
+ Mocker.GetMock()
+ .Setup(s => s.DownloadString(_expectedUrl, _settings.Username, _settings.Password))
+ .Returns(activePlayers);
+ }
+
+ [SetUp]
+ public void Setup()
+ {
+ _settings = new XbmcSettings
+ {
+ Host = "localhost",
+ Port = 8080,
+ Username = "xbmc",
+ Password = "xbmc",
+ AlwaysUpdate = false,
+ CleanLibrary = false,
+ UpdateLibrary = true
+ };
+
+ _expectedUrl = String.Format("http://{0}/xbmcCmds/xbmcHttp?command={1}", _settings.Address, "getcurrentlyplaying");
+ }
+
+ [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");
+ }
+ }
+}
diff --git a/NzbDrone.Core.Test/NotificationTests/Xbmc/Http/CheckForErrorFixture.cs b/NzbDrone.Core.Test/NotificationTests/Xbmc/Http/CheckForErrorFixture.cs
new file mode 100644
index 000000000..db4aeeb1d
--- /dev/null
+++ b/NzbDrone.Core.Test/NotificationTests/Xbmc/Http/CheckForErrorFixture.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Notifications.Xbmc;
+using NzbDrone.Core.Test.Framework;
+
+namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Http
+{
+ [TestFixture]
+ public class CheckForErrorFixture : CoreTest
+ {
+ [Test]
+ public void should_be_true_when_the_response_contains_an_error()
+ {
+ const string response = "html>Error:Unknown command