diff --git a/Gruntfile.js b/Gruntfile.js
index 50c273d60..c781b8ee1 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -16,13 +16,16 @@ module.exports = function (grunt) {
'UI/JsLibraries/jquery.cookie.js' : 'http://raw.github.com/carhartl/jquery-cookie/master/jquery.cookie.js',
'UI/JsLibraries/jquery.js' : 'http://code.jquery.com/jquery.js',
'UI/JsLibraries/jquery.backstretch.js' : 'http://raw.github.com/srobbin/jquery-backstretch/master/jquery.backstretch.js',
+
+ 'UI/JsLibraries/jquery.signalR.js' : 'https://raw.github.com/SignalR/SignalR/master/samples/Microsoft.AspNet.SignalR.Hosting.AspNet.Samples/Scripts/jquery.signalR.js',
+
'UI/JsLibraries/require.js' : 'http://raw.github.com/jrburke/requirejs/master/require.js',
'UI/JsLibraries/sugar.js' : 'http://raw.github.com/andrewplummer/Sugar/master/release/sugar-full.development.js',
'UI/JsLibraries/underscore.js' : 'http://underscorejs.org/underscore.js',
'UI/JsLibraries/backbone.pageable.js' : 'http://raw.github.com/wyuenho/backbone-pageable/master/lib/backbone-pageable.js',
'UI/JsLibraries/backbone.backgrid.js' : 'http://raw.github.com/wyuenho/backgrid/master/lib/backgrid.js',
'UI/JsLibraries/backbone.backgrid.paginator.js' : 'http://raw.github.com/wyuenho/backgrid/master/lib/extensions/paginator/backgrid-paginator.js',
- 'UI/JsLibraries/backbone.backgrid.filter.js' : 'http://raw.github.com/wyuenho/backgrid/master/lib/extensions/filter/backgrid-filter.js',
+ 'UI/JsLibraries/backbone.backgrid.filter.js' : 'http://raw.github.com/wyuenho/backgrid/master/lib/extensions/filter/backgrid-filter.js',
'UI/JsLibraries/messenger.js' : 'http://raw.github.com/HubSpot/messenger/master/build/js/messenger.js',
'UI/JsLibraries/lunr.js' : 'http://raw.github.com/olivernn/lunr.js/master/lunr.js',
'UI/Content/messenger.css' : 'http://raw.github.com/HubSpot/messenger/master/build/css/messenger.css',
diff --git a/NzbDrone.Api/Frontend/IndexModule.cs b/NzbDrone.Api/Frontend/IndexModule.cs
index 8a6e15f80..042cae893 100644
--- a/NzbDrone.Api/Frontend/IndexModule.cs
+++ b/NzbDrone.Api/Frontend/IndexModule.cs
@@ -16,7 +16,8 @@ private object Index()
if(
Request.Path.Contains(".")
|| Request.Path.StartsWith("/static", StringComparison.CurrentCultureIgnoreCase)
- || Request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase))
+ || Request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase)
+ || Request.Path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase))
{
return new NotFoundResponse();
}
diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj
index f2bb6115f..0e3ca2be7 100644
--- a/NzbDrone.Api/NzbDrone.Api.csproj
+++ b/NzbDrone.Api/NzbDrone.Api.csproj
@@ -63,6 +63,9 @@
False
..\packages\FluentValidation.4.0.0.0\lib\Net40\FluentValidation.dll
+
+ ..\packages\Microsoft.AspNet.SignalR.Core.1.0.1\lib\net40\Microsoft.AspNet.SignalR.Core.dll
+
False
..\packages\Nancy.0.16.1\lib\net40\Nancy.dll
@@ -119,6 +122,7 @@
+
@@ -139,6 +143,9 @@
+
+
+
diff --git a/NzbDrone.Api/Series/SeriesConnection.cs b/NzbDrone.Api/Series/SeriesConnection.cs
new file mode 100644
index 000000000..7a31077ba
--- /dev/null
+++ b/NzbDrone.Api/Series/SeriesConnection.cs
@@ -0,0 +1,13 @@
+using NLog;
+using NzbDrone.Api.SignalR;
+
+namespace NzbDrone.Api.Series
+{
+ public class SeriesConnection : BasicResourceConnection
+ {
+ public override string Resource
+ {
+ get { return "/Series"; }
+ }
+ }
+}
diff --git a/NzbDrone.Api/SignalR/BasicResourceConnection.cs b/NzbDrone.Api/SignalR/BasicResourceConnection.cs
new file mode 100644
index 000000000..1a3a2af78
--- /dev/null
+++ b/NzbDrone.Api/SignalR/BasicResourceConnection.cs
@@ -0,0 +1,39 @@
+using System.Threading.Tasks;
+using Microsoft.AspNet.SignalR;
+using NLog;
+using NzbDrone.Common.Messaging;
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Datastore.Events;
+
+namespace NzbDrone.Api.SignalR
+{
+ public abstract class BasicResourceConnection :
+ NzbDronePersistentConnection,
+ IHandleAsync>
+ where T : ModelBase
+ {
+ private readonly Logger _logger;
+
+ public BasicResourceConnection()
+ {
+ _logger = LogManager.GetCurrentClassLogger();
+ }
+
+ protected override Task OnConnected(IRequest request, string connectionId)
+ {
+ _logger.Debug("SignalR client connected. ID:{0}", connectionId);
+ return base.OnConnected(request, connectionId);
+ }
+
+ public override Task ProcessRequest(Microsoft.AspNet.SignalR.Hosting.HostContext context)
+ {
+ _logger.Debug("Request: {0}", context);
+ return base.ProcessRequest(context);
+ }
+
+ public void HandleAsync(ModelEvent message)
+ {
+ Connection.Broadcast(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NzbDrone.Api/SignalR/NzbDronePersistentConnection.cs b/NzbDrone.Api/SignalR/NzbDronePersistentConnection.cs
new file mode 100644
index 000000000..2e4c8444d
--- /dev/null
+++ b/NzbDrone.Api/SignalR/NzbDronePersistentConnection.cs
@@ -0,0 +1,9 @@
+using Microsoft.AspNet.SignalR;
+
+namespace NzbDrone.Api.SignalR
+{
+ public abstract class NzbDronePersistentConnection : PersistentConnection
+ {
+ public abstract string Resource { get; }
+ }
+}
\ No newline at end of file
diff --git a/NzbDrone.Api/SignalR/SignalrDependencyResolver.cs b/NzbDrone.Api/SignalR/SignalrDependencyResolver.cs
new file mode 100644
index 000000000..e17d3623d
--- /dev/null
+++ b/NzbDrone.Api/SignalR/SignalrDependencyResolver.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNet.SignalR;
+using TinyIoC;
+
+namespace NzbDrone.Api.SignalR
+{
+ public class SignalrDependencyResolver : DefaultDependencyResolver
+ {
+ private readonly TinyIoCContainer _container;
+
+ public static void Register(TinyIoCContainer container)
+ {
+ GlobalHost.DependencyResolver = new SignalrDependencyResolver(container);
+ }
+
+ private SignalrDependencyResolver(TinyIoCContainer container)
+ {
+ _container = container;
+ }
+
+ public override object GetService(Type serviceType)
+ {
+ return _container.CanResolve(serviceType) ? _container.Resolve(serviceType) : base.GetService(serviceType);
+ }
+
+ public override IEnumerable
+
+ ..\packages\Microsoft.AspNet.SignalR.Core.1.0.1\lib\net40\Microsoft.AspNet.SignalR.Core.dll
+
+
+ ..\packages\Microsoft.AspNet.SignalR.Owin.1.0.1\lib\net40\Microsoft.AspNet.SignalR.Owin.dll
+
..\packages\Microsoft.Owin.Host.HttpListener.0.21.0-pre\lib\net40\Microsoft.Owin.Host.HttpListener.dll
@@ -106,6 +112,10 @@
False
..\packages\Nancy.Owin.0.16.1\lib\net40\Nancy.Owin.dll
+
+ False
+ ..\packages\Newtonsoft.Json.4.5.11\lib\net35\Newtonsoft.Json.dll
+
False
..\packages\NLog.2.0.1.2\lib\net40\NLog.dll
diff --git a/NzbDrone/Owin/MiddleWare/IOwinMiddleWare.cs b/NzbDrone/Owin/MiddleWare/IOwinMiddleWare.cs
index 80a85102f..bcba8d456 100644
--- a/NzbDrone/Owin/MiddleWare/IOwinMiddleWare.cs
+++ b/NzbDrone/Owin/MiddleWare/IOwinMiddleWare.cs
@@ -4,6 +4,7 @@ namespace NzbDrone.Owin.MiddleWare
{
public interface IOwinMiddleWare
{
+ int Order { get; }
void Attach(IAppBuilder appBuilder);
}
}
\ No newline at end of file
diff --git a/NzbDrone/Owin/MiddleWare/NancyMiddleWare.cs b/NzbDrone/Owin/MiddleWare/NancyMiddleWare.cs
index a30a5ea8b..84be7e9b7 100644
--- a/NzbDrone/Owin/MiddleWare/NancyMiddleWare.cs
+++ b/NzbDrone/Owin/MiddleWare/NancyMiddleWare.cs
@@ -16,6 +16,8 @@ public NancyMiddleWare(INancyBootstrapper nancyBootstrapper)
_nancyBootstrapper = nancyBootstrapper;
}
+ public int Order { get { return 1; } }
+
public void Attach(IAppBuilder appBuilder)
{
var nancyOwinHost = new NancyOwinHost(null, _nancyBootstrapper);
diff --git a/NzbDrone/Owin/MiddleWare/SignalRMiddleWare.cs b/NzbDrone/Owin/MiddleWare/SignalRMiddleWare.cs
index fddb7c0b4..2b21f9a7b 100644
--- a/NzbDrone/Owin/MiddleWare/SignalRMiddleWare.cs
+++ b/NzbDrone/Owin/MiddleWare/SignalRMiddleWare.cs
@@ -1,26 +1,31 @@
-using System;
using System.Collections.Generic;
-using System.Threading.Tasks;
-using Nancy.Bootstrapper;
-using Nancy.Owin;
+using Microsoft.AspNet.SignalR;
+using NzbDrone.Api.SignalR;
using Owin;
+using TinyIoC;
namespace NzbDrone.Owin.MiddleWare
{
public class SignalRMiddleWare : IOwinMiddleWare
{
- private readonly INancyBootstrapper _nancyBootstrapper;
+ private readonly IEnumerable _persistentConnections;
- public SignalRMiddleWare(INancyBootstrapper nancyBootstrapper)
+ public int Order { get { return 0; } }
+
+ public SignalRMiddleWare(IEnumerable persistentConnections, TinyIoCContainer container)
{
- _nancyBootstrapper = nancyBootstrapper;
+ _persistentConnections = persistentConnections;
+
+ SignalrDependencyResolver.Register(container);
}
public void Attach(IAppBuilder appBuilder)
{
- return;
- var nancyOwinHost = new NancyOwinHost(null, _nancyBootstrapper);
- appBuilder.Use((Func, Task>, Func, Task>>)(next => (Func, Task>)nancyOwinHost.Invoke), new object[0]);
+ foreach (var nzbDronePersistentConnection in _persistentConnections)
+ {
+ appBuilder.MapConnection("signalr/series", nzbDronePersistentConnection.GetType(), new ConnectionConfiguration { EnableCrossDomain = true });
+ }
+
}
}
}
\ No newline at end of file
diff --git a/NzbDrone/Owin/OwinHostController.cs b/NzbDrone/Owin/OwinHostController.cs
index cafe31a80..a2f30b020 100644
--- a/NzbDrone/Owin/OwinHostController.cs
+++ b/NzbDrone/Owin/OwinHostController.cs
@@ -8,6 +8,7 @@
using NzbDrone.Common;
using NzbDrone.Owin.MiddleWare;
using Owin;
+using System.Linq;
namespace NzbDrone.Owin
{
@@ -27,13 +28,20 @@ public OwinHostController(ConfigFileProvider configFileProvider, IEnumerable c.Order))
{
+ _logger.Debug("Attaching {0} to host", middleWare.GetType().Name);
middleWare.Attach(appBuilder);
}
}
diff --git a/NzbDrone/packages.config b/NzbDrone/packages.config
index 934bc606d..9b4b4b0a7 100644
--- a/NzbDrone/packages.config
+++ b/NzbDrone/packages.config
@@ -1,10 +1,13 @@
+
+
+
diff --git a/UI/Index.html b/UI/Index.html
index a95c090dd..96b565b08 100644
--- a/UI/Index.html
+++ b/UI/Index.html
@@ -52,17 +52,6 @@
-
-