diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj
index 35688a6fe..f91cb053e 100644
--- a/src/Ombi.Core/Ombi.Core.csproj
+++ b/src/Ombi.Core/Ombi.Core.csproj
@@ -37,4 +37,10 @@
+
+
+ ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll
+
+
+
\ No newline at end of file
diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs
index 4d5ef6fd5..1bea848ff 100644
--- a/src/Ombi.DependencyInjection/IocExtensions.cs
+++ b/src/Ombi.DependencyInjection/IocExtensions.cs
@@ -2,6 +2,7 @@
using System.Security.Principal;
using Hangfire;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Ombi.Api.Discord;
diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj
index ec905e718..8cb6122be 100644
--- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj
+++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj
@@ -41,4 +41,10 @@
+
+
+ ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll
+
+
+
\ No newline at end of file
diff --git a/src/Ombi.Hubs/Ombi.Hubs.csproj b/src/Ombi.Hubs/Ombi.Hubs.csproj
new file mode 100644
index 000000000..3331096d0
--- /dev/null
+++ b/src/Ombi.Hubs/Ombi.Hubs.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+ ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll
+
+
+
+
diff --git a/src/Ombi.Hubs/ScheduledJobsHub.cs b/src/Ombi.Hubs/ScheduledJobsHub.cs
new file mode 100644
index 000000000..4d5d4b901
--- /dev/null
+++ b/src/Ombi.Hubs/ScheduledJobsHub.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.SignalR;
+
+namespace Ombi.Hubs
+{
+ public class ScheduledJobsHub : Hub
+ {
+ public Task Send(string data)
+ {
+ return Clients.All.SendAsync("Send", data);
+ }
+ }
+}
diff --git a/src/Ombi.sln b/src/Ombi.sln
index 2b9be2c42..70668294d 100644
--- a/src/Ombi.sln
+++ b/src/Ombi.sln
@@ -96,7 +96,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Notifications", "O
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Helpers.Tests", "Ombi.Helpers.Tests\Ombi.Helpers.Tests.csproj", "{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Helpers.Tests", "Ombi.Helpers.Tests\Ombi.Helpers.Tests.csproj", "{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Hubs", "Ombi.Hubs\Ombi.Hubs.csproj", "{67416CC5-13B2-44BB-98CE-39DA93D6F70E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -256,6 +258,10 @@ Global
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Ombi/ClientApp/angular.json b/src/Ombi/ClientApp/angular.json
index 68d9b9cd8..a01be1215 100644
--- a/src/Ombi/ClientApp/angular.json
+++ b/src/Ombi/ClientApp/angular.json
@@ -27,7 +27,6 @@
"src/styles/_imports.scss",
"node_modules/bootstrap/scss/bootstrap.scss",
"node_modules/angular-bootstrap-md/scss/mdb-free.scss",
- "node_modules/pace/themes/orange/pace-theme-flat-top.css",
"node_modules/font-awesome/scss/font-awesome.scss"
],
"scripts": [
diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html
index 0aa7d2612..b5229d2a4 100644
--- a/src/Ombi/ClientApp/src/app/app.component.html
+++ b/src/Ombi/ClientApp/src/app/app.component.html
@@ -164,7 +164,6 @@
-->
-
diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts
index ca4b4262c..cc0ae6542 100644
--- a/src/Ombi/ClientApp/src/app/app.component.ts
+++ b/src/Ombi/ClientApp/src/app/app.component.ts
@@ -6,9 +6,14 @@ import { AuthService } from "./auth/auth.service";
import { ILocalUser } from "./auth/IUserLogin";
import { IdentityService, NotificationService } from "./services";
import { JobService, SettingsService } from "./services";
+import { MatSnackBar } from '@angular/material';
import { ICustomizationSettings, ICustomPage } from "./interfaces";
+import { HubConnection } from '@aspnet/signalr';
+import * as signalR from '@aspnet/signalr';
+
+
@Component({
selector: "app-ombi",
templateUrl: "./app.component.html",
@@ -28,6 +33,8 @@ export class AppComponent implements OnInit {
private checkedForUpdate: boolean;
+ private scheduleHubConnection: HubConnection | undefined;
+
constructor(public notificationService: NotificationService,
public authService: AuthService,
private readonly router: Router,
@@ -35,7 +42,8 @@ export class AppComponent implements OnInit {
private readonly jobService: JobService,
public readonly translate: TranslateService,
private readonly identityService: IdentityService,
- private readonly platformLocation: PlatformLocation) {
+ private readonly platformLocation: PlatformLocation,
+ private readonly snackBar: MatSnackBar) {
const base = this.platformLocation.getBaseHrefFromDOM();
if (base.length > 1) {
@@ -85,6 +93,24 @@ export class AppComponent implements OnInit {
}
}
});
+
+ this.scheduleHubConnection = new signalR.HubConnectionBuilder().withUrl("/hubs/schedules", {
+ accessTokenFactory: () => {
+ return this.authService.getToken();
+ }
+ })
+ .configureLogging(signalR.LogLevel.Information).build();
+
+ this.scheduleHubConnection
+ .start()
+ .then(() => console.info('Connection started!'))
+ .catch(err => console.error(err));
+ this.scheduleHubConnection.on("Send", (data: any) => {
+ this.snackBar.open(data,"OK", {
+ duration: 3000
+ });
+ });
+
}
public roleClass() {
diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts
index 7d33aa6fa..2949c833c 100644
--- a/src/Ombi/ClientApp/src/app/app.module.ts
+++ b/src/Ombi/ClientApp/src/app/app.module.ts
@@ -8,7 +8,6 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { RouterModule, Routes } from "@angular/router";
import { JwtModule } from "@auth0/angular-jwt";
-
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
@@ -19,7 +18,7 @@ import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule,
TooltipModule } from "primeng/primeng";
import {
- MatButtonModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, MatAutocompleteModule, MatCheckboxModule} from '@angular/material';
+ MatButtonModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, MatAutocompleteModule, MatCheckboxModule, MatSnackBarModule} from '@angular/material';
import { MatCardModule, MatInputModule, MatTabsModule } from "@angular/material";
import { MDBBootstrapModule, CardsFreeModule, NavbarModule } from "angular-bootstrap-md";
@@ -103,6 +102,7 @@ export function JwtTokenGetter() {
DataTableModule,
SharedModule,
NgxEditorModule,
+ MatSnackBarModule,
DialogModule,
MatButtonModule,
NavbarModule,
diff --git a/src/Ombi/ClientApp/src/app/auth/auth.service.ts b/src/Ombi/ClientApp/src/app/auth/auth.service.ts
index 2115b2516..3f9768c71 100644
--- a/src/Ombi/ClientApp/src/app/auth/auth.service.ts
+++ b/src/Ombi/ClientApp/src/app/auth/auth.service.ts
@@ -26,6 +26,10 @@ export class AuthService extends ServiceHelpers {
return this.http.post(`${this.url}/requirePassword`, JSON.stringify(login), {headers: this.headers});
}
+ public getToken() {
+ return this.jwtHelperService.tokenGetter();
+ }
+
public loggedIn() {
const token: string = this.jwtHelperService.tokenGetter();
diff --git a/src/Ombi/ClientApp/src/app/shared/shared.module.ts b/src/Ombi/ClientApp/src/app/shared/shared.module.ts
index 34678f4b7..985c95120 100644
--- a/src/Ombi/ClientApp/src/app/shared/shared.module.ts
+++ b/src/Ombi/ClientApp/src/app/shared/shared.module.ts
@@ -11,7 +11,7 @@ import { InputSwitchModule, SidebarModule } from "primeng/primeng";
import {
MatButtonModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, MatTooltipModule} from '@angular/material';
- import { MatCardModule, MatInputModule, MatTabsModule, MatAutocompleteModule, MatCheckboxModule, MatExpansionModule, MatDialogModule } from "@angular/material";
+ import { MatCardModule, MatInputModule, MatTabsModule, MatAutocompleteModule, MatCheckboxModule, MatExpansionModule, MatDialogModule, MatSnackBarModule } from "@angular/material";
@NgModule({
declarations: [
@@ -37,6 +37,7 @@ import {
MatCheckboxModule,
MatExpansionModule,
MatDialogModule,
+ MatSnackBarModule,
],
exports: [
TranslateModule,
@@ -52,6 +53,7 @@ import {
MatButtonModule,
MatNativeDateModule,
MatIconModule,
+ MatSnackBarModule,
MatSidenavModule,
MatListModule,
MatToolbarModule,
diff --git a/src/Ombi/ClientApp/src/main.ts b/src/Ombi/ClientApp/src/main.ts
index 738e001df..54b38fb28 100644
--- a/src/Ombi/ClientApp/src/main.ts
+++ b/src/Ombi/ClientApp/src/main.ts
@@ -1,8 +1,4 @@
// Main
-
-import * as Pace from "pace";
-Pace.start();
-
import "jquery";
import "bootstrap/dist/js/bootstrap";
diff --git a/src/Ombi/ClientApp/src/typings/globals.d.ts b/src/Ombi/ClientApp/src/typings/globals.d.ts
index a52b20fa0..ff6860d41 100644
--- a/src/Ombi/ClientApp/src/typings/globals.d.ts
+++ b/src/Ombi/ClientApp/src/typings/globals.d.ts
@@ -1,6 +1,4 @@
// Globals
-
-declare module "pace";
declare var __webpack_public_path__: any;
declare var module: any;
diff --git a/src/Ombi/Controllers/V2/HubController.cs b/src/Ombi/Controllers/V2/HubController.cs
new file mode 100644
index 000000000..aa2bd604c
--- /dev/null
+++ b/src/Ombi/Controllers/V2/HubController.cs
@@ -0,0 +1,40 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Ombi.Api.TheMovieDb.Models;
+using Ombi.Core.Engine.V2;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.SignalR;
+using Ombi.Core;
+using Ombi.Core.Engine.Interfaces;
+using Ombi.Core.Models.Search;
+using Ombi.Core.Models.Search.V2;
+using Ombi.Hubs;
+using Ombi.Models;
+
+namespace Ombi.Controllers.V2
+{
+ [ApiV2]
+ [Authorize]
+ [ApiController]
+ public class HubController : ControllerBase
+ {
+ public HubController(IHubContext hub)
+ {
+ _hub = hub;
+ }
+
+ private readonly IHubContext _hub;
+
+ ///
+ /// Returns search results for both TV and Movies
+ ///
+ ///
+ [HttpGet("{searchTerm}")]
+ public async Task MultiSearch(string searchTerm)
+ {
+ await _hub.Clients.All.SendAsync("Send", searchTerm);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj
index d02a2698a..5aab971c9 100644
--- a/src/Ombi/Ombi.csproj
+++ b/src/Ombi/Ombi.csproj
@@ -87,6 +87,7 @@
+
diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs
index de8dbdc25..3d71dc643 100644
--- a/src/Ombi/Startup.cs
+++ b/src/Ombi/Startup.cs
@@ -21,6 +21,7 @@ using Ombi.Core.Authentication;
using Ombi.Core.Settings;
using Ombi.DependencyInjection;
using Ombi.Helpers;
+using Ombi.Hubs;
using Ombi.Mapping;
using Ombi.Schedule;
using Ombi.Settings.Settings.Models;
@@ -63,7 +64,6 @@ namespace Ombi
}
Log.Logger = config;
-
//}
//if (env.IsProduction())
//{
@@ -135,10 +135,11 @@ namespace Ombi
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
- .AllowAnyHeader();
+ .AllowAnyHeader().AllowCredentials();
}));
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
+ services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
+ services.AddSignalR();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
@@ -241,6 +242,8 @@ namespace Ombi
c.SwaggerEndpoint("/swagger/v2/swagger.json", "API V2");
});
+ app.UseSignalR(routes => { routes.MapHub("/hubs/schedules"); });
+
app.UseMvc(routes =>
{
routes.MapRoute(
diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs
index f0bec1383..cd26e8223 100644
--- a/src/Ombi/StartupExtensions.cs
+++ b/src/Ombi/StartupExtensions.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
@@ -136,6 +137,23 @@ namespace Ombi
{
x.Audience = "Ombi";
x.TokenValidationParameters = tokenValidationParameters;
+ x.Events = new JwtBearerEvents
+ {
+ OnMessageReceived = context =>
+ {
+ var accessToken = context.Request.Headers["id_token"];
+
+ // If the request is for our hub...
+ var path = context.HttpContext.Request.Path;
+ if (!string.IsNullOrEmpty(accessToken) &&
+ (path.StartsWithSegments("/hubs/")))
+ {
+ // Read the token out of the query string
+ context.Token = accessToken;
+ }
+ return Task.CompletedTask;
+ }
+ };
});
}
}