Merge branch 'feature/v4' of https://github.com/tidusjar/ombi into feature/v4

pull/3598/head
Jamie Rees 4 years ago
commit 0cec163324

@ -33,10 +33,10 @@ We also now have merch up on Teespring!
___ ___
| Service | Stable | Develop | | Service | Stable | Develop | V4 |
|----------|:---------------------------:|:----------------------------:| |----------|:---------------------------:|:----------------------------:|:----------------------------:|
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/develop?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) | | Build Status | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/develop?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4)
| Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | | Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/ombi.releases/releases) |
# Features # Features
Here are some of the features Ombi V3 has: Here are some of the features Ombi V3 has:
* Now working without crashes on Linux. * Now working without crashes on Linux.

@ -36,7 +36,9 @@ namespace Ombi.Notifications.Agents
{ {
return false; return false;
} }
return !settings.WhatsAppSettings.AccountSid.IsNullOrEmpty() && !settings.WhatsAppSettings.AuthToken.IsNullOrEmpty() && !settings.WhatsAppSettings.From.IsNullOrEmpty(); return (!settings.WhatsAppSettings?.AccountSid?.IsNullOrEmpty() ?? false)
&& (!settings.WhatsAppSettings?.AuthToken?.IsNullOrEmpty() ?? false)
&& (!settings.WhatsAppSettings?.From?.IsNullOrEmpty() ?? false);
} }
protected override async Task NewRequest(NotificationOptions model, TwilioSettings settings) protected override async Task NewRequest(NotificationOptions model, TwilioSettings settings)

@ -78,13 +78,11 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
return; return;
} }
await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) await NotifyClient(recentlyAddedSearch ? "Plex Recently Added Sync Started" : "Plex Content Sync Started");
.SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync Started" : "Plex Content Sync Started");
if (!ValidateSettings(plexSettings)) if (!ValidateSettings(plexSettings))
{ {
Logger.LogError("Plex Settings are not valid"); Logger.LogError("Plex Settings are not valid");
await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) await NotifyClient(recentlyAddedSearch ? "Plex Recently Added Sync, Settings Not Valid" : "Plex Content, Settings Not Valid");
.SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync, Settings Not Valid" : "Plex Content, Settings Not Valid");
return; return;
} }
var processedContent = new ProcessedContent(); var processedContent = new ProcessedContent();
@ -104,13 +102,13 @@ namespace Ombi.Schedule.Jobs.Plex
} }
catch (Exception e) catch (Exception e)
{ {
await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) await NotifyClient(recentlyAddedSearch ? "Plex Recently Added Sync Errored" : "Plex Content Sync Errored");
.SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync Errored" : "Plex Content Sync Errored");
Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content"); Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content");
} }
if (!recentlyAddedSearch) if (!recentlyAddedSearch)
{ {
await NotifyClient("Plex Sync - Starting Episode Sync");
Logger.LogInformation("Starting EP Cacher"); Logger.LogInformation("Starting EP Cacher");
await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex"); await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex");
} }
@ -124,6 +122,7 @@ namespace Ombi.Schedule.Jobs.Plex
} }
else else
{ {
await NotifyClient("Plex Sync - Checking if any requests are now available");
Logger.LogInformation("Kicking off Plex Availability Checker"); Logger.LogInformation("Kicking off Plex Availability Checker");
await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex"); await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex");
} }
@ -141,10 +140,11 @@ namespace Ombi.Schedule.Jobs.Plex
await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex"); await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex");
} }
} }
Logger.LogInformation("Finished Plex Content Cacher, with processed content: {0}, episodes: {1}. Recently Added Scan: {2}", processedContent?.Content?.Count() ?? 0, processedContent?.Episodes?.Count() ?? 0, recentlyAddedSearch); var processedCont = processedContent?.Content?.Count() ?? 0;
var processedEp = processedContent?.Episodes?.Count() ?? 0;
Logger.LogInformation("Finished Plex Content Cacher, with processed content: {0}, episodes: {1}. Recently Added Scan: {2}", processedContent, processedEp, recentlyAddedSearch);
await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) await NotifyClient(recentlyAddedSearch ? $"Plex Recently Added Sync Finished, We processed {processedCont}, and {processedEp} Episodes" : "Plex Content Sync Finished");
.SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync Finished" : "Plex Content Sync Finished");
} }
@ -186,6 +186,7 @@ namespace Ombi.Schedule.Jobs.Plex
var allContent = await GetAllContent(servers, recentlyAddedSearch); var allContent = await GetAllContent(servers, recentlyAddedSearch);
Logger.LogDebug("We found {0} items", allContent.Count); Logger.LogDebug("We found {0} items", allContent.Count);
// Let's now process this. // Let's now process this.
var contentToAdd = new HashSet<PlexServerContent>(); var contentToAdd = new HashSet<PlexServerContent>();
@ -654,7 +655,11 @@ namespace Ombi.Schedule.Jobs.Plex
return libs; return libs;
} }
private async Task NotifyClient(string message)
{
await Notification.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, $"Plex Sync - {message}");
}
private static bool ValidateSettings(PlexSettings plex) private static bool ValidateSettings(PlexSettings plex)
{ {

@ -120,7 +120,7 @@ namespace Ombi.Store.Context
notificationToAdd = new NotificationTemplates notificationToAdd = new NotificationTemplates
{ {
NotificationType = notificationType, NotificationType = notificationType,
Message = "Hello! Your request for {Title} on {ApplicationName}! This is now available! :)", Message = "Hello! Your request for {Title} on {ApplicationName} is now available! :)",
Subject = "{ApplicationName}: {Title} is now available!", Subject = "{ApplicationName}: {Title} is now available!",
Agent = agent, Agent = agent,
Enabled = true, Enabled = true,

@ -13,6 +13,7 @@
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"aot": true,
"progress": true, "progress": true,
"extractCss": true, "extractCss": true,
"outputPath": "dist", "outputPath": "dist",
@ -24,29 +25,33 @@
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles/_imports.scss", "src/styles/_imports.scss",
"node_modules/bootstrap/scss/bootstrap.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", "node_modules/font-awesome/scss/font-awesome.scss",
"node_modules/primeng/resources/primeng.min.css", "node_modules/primeng/resources/primeng.min.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeicons/primeicons.css", "node_modules/primeicons/primeicons.css",
"node_modules/please-wait/src/please-wait.scss", "node_modules/please-wait/src/please-wait.scss",
"node_modules/@fullcalendar/core/main.min.css", "node_modules/@fullcalendar/core/main.min.css",
"node_modules/@fullcalendar/daygrid/main.min.css",
"node_modules/spinkit/scss/spinners/11-folding-cube.scss", "node_modules/spinkit/scss/spinners/11-folding-cube.scss",
"node_modules/spinkit/scss/spinkit.scss" "node_modules/spinkit/scss/spinkit.scss"
], ],
"scripts": [ "scripts": [
"node_modules/jquery/dist/jquery.min.js", "node_modules/jquery/dist/jquery.min.js",
"node_modules/chart.js/dist/Chart.js", "node_modules/chart.js/dist/Chart.js",
"node_modules/hammerjs/hammer.min.js",
"./node_modules/@fullcalendar/core/main.js", "./node_modules/@fullcalendar/core/main.js",
"./node_modules/@fullcalendar/interaction/main.js" "./node_modules/@fullcalendar/interaction/main.js"
] ]
}, },
"configurations": { "configurations": {
"production": { "production": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "src/environments/environment.ts", "replace": "src/environments/environment.ts",
@ -64,6 +69,12 @@
"buildOptimizer": true "buildOptimizer": true
}, },
"hmr": { "hmr": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "src/environments/environment.ts", "replace": "src/environments/environment.ts",

@ -9,23 +9,24 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^8.0.0", "@angular/animations": "^9.1.7",
"@angular/cdk": "^8.0.0", "@angular/cdk": "^9.2.3",
"@angular/common": "^8.0.0", "@angular/common": "^9.1.7",
"@angular/compiler": "^8.0.0", "@angular/compiler": "^9.1.7",
"@angular/core": "^8.0.0", "@angular/core": "^9.1.7",
"@angular/forms": "^8.0.0", "@angular/forms": "^9.1.7",
"@angular/material": "^8.0.0", "@angular/localize": "^9.1.7",
"@angular/platform-browser": "^8.0.0", "@angular/material": "^9.2.3",
"@angular/platform-browser-dynamic": "^8.0.0", "@angular/platform-browser": "^9.1.7",
"@angular/platform-server": "^8.0.0", "@angular/platform-browser-dynamic": "^9.1.7",
"@angular/router": "^8.0.0", "@angular/platform-server": "^9.1.7",
"@angular/router": "^9.1.7",
"@angularclass/hmr": "^2.1.3", "@angularclass/hmr": "^2.1.3",
"@aspnet/signalr": "^1.1.0", "@aspnet/signalr": "^1.1.0",
"@auth0/angular-jwt": "^2.1.0", "@auth0/angular-jwt": "^2.1.0",
"@fullcalendar/core": "^4.2.0", "@fullcalendar/core": "^4.2.0",
"@fullcalendar/daygrid": "^4.4.0",
"@fullcalendar/interaction": "^4.2.0", "@fullcalendar/interaction": "^4.2.0",
"@ng-bootstrap/ng-bootstrap": "^4.0.1",
"@ngu/carousel": "^1.4.9-beta-2", "@ngu/carousel": "^1.4.9-beta-2",
"@ngx-translate/core": "^11.0.1", "@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0", "@ngx-translate/http-loader": "^4.0.0",
@ -33,49 +34,41 @@
"@yellowspot/ng-truncate": "^1.4.0", "@yellowspot/ng-truncate": "^1.4.0",
"angular-bootstrap-md": "^7.5.4", "angular-bootstrap-md": "^7.5.4",
"angular-router-loader": "^0.8.5", "angular-router-loader": "^0.8.5",
"angular2-template-loader": "^0.6.2",
"angularx-qrcode": "^2.1.0", "angularx-qrcode": "^2.1.0",
"aspnet-prerendering": "^3.0.1",
"awesome-typescript-loader": "^5.2.0",
"bootstrap": "^4.2.1", "bootstrap": "^4.2.1",
"chart.js": "2.5.0", "chart.js": "2.5.0",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"eventemitter2": "^5.0.1", "eventemitter2": "^5.0.1",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"fullcalendar": "^4.0.0-alpha.4", "fullcalendar": "^4.0.0-alpha.4",
"hammerjs": "^2.0.8",
"jquery": "3.3.1", "jquery": "3.3.1",
"moment": "^2.23.0", "moment": "^2.23.0",
"ng2-cookies": "^1.0.12", "ng2-cookies": "^1.0.12",
"ngx-bootstrap": "^3.1.4",
"ngx-clipboard": "^12.1.0", "ngx-clipboard": "^12.1.0",
"ngx-infinite-scroll": "^7.1.0", "ngx-infinite-scroll": "^9.0.0",
"ngx-moment": "^3.0.1", "ngx-moment": "^3.0.1",
"ngx-order-pipe": "^2.0.1", "ngx-order-pipe": "^2.0.1",
"ngx-page-scroll": "^5.0.1",
"pace": "github:HubSpot/pace#v1.0.2",
"please-wait": "^0.0.5", "please-wait": "^0.0.5",
"popper.js": "^1.14.3", "popper.js": "^1.14.3",
"primeicons": "^1.0.0", "primeicons": "^1.0.0",
"primeng": "^7.0.3", "primeng": "^9.0.6",
"rxjs": "^6.5.2", "rxjs": "^6.5.2",
"socket.io-client": "^2.2.0",
"spinkit": "^1.2.5", "spinkit": "^1.2.5",
"store": "^2.0.12", "store": "^2.0.12",
"sweetalert2": "^7.33.1", "tslib": "^1.10.0",
"tslint-angular": "^1.1.2", "tslint-angular": "^1.1.2",
"zone.js": "^0.9.1" "zone.js": "~0.10.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^0.803.21", "@angular-devkit/build-angular": "~0.901.6",
"@angular/cli": "~8.3.21", "@angular/cli": "~9.1.6",
"@angular/compiler-cli": "^8.2.14", "@angular/compiler-cli": "^9.1.7",
"@angular/language-service": "^8.2.14", "@angular/language-service": "^9.1.7",
"@types/jasmine": "~2.8.6", "@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4", "@types/node": "^12.11.1",
"codelyzer": "^4.5.0", "codelyzer": "^5.1.2",
"typescript": "~3.4.5" "typescript": "~3.8.3"
}, },
"optionalDependencies": { "optionalDependencies": {
"node-sass": "^4.12.0", "node-sass": "^4.12.0",

@ -7,7 +7,7 @@ import { AuthService } from "./auth/auth.service";
import { ILocalUser } from "./auth/IUserLogin"; import { ILocalUser } from "./auth/IUserLogin";
import { NotificationService, CustomPageService } from "./services"; import { NotificationService, CustomPageService } from "./services";
import { SettingsService } from "./services"; import { SettingsService } from "./services";
import { MatSnackBar } from '@angular/material'; import { MatSnackBar } from '@angular/material/snack-bar';
import { ICustomizationSettings, ICustomPage } from "./interfaces"; import { ICustomizationSettings, ICustomPage } from "./interfaces";
import { StorageService } from './shared/storage/storage-service'; import { StorageService } from './shared/storage/storage-service';

@ -7,21 +7,32 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { JwtModule } from "@auth0/angular-jwt"; import { JwtModule } from "@auth0/angular-jwt";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { TranslateLoader, TranslateModule } from "@ngx-translate/core"; import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { CookieService } from "ng2-cookies"; import { CookieService } from "ng2-cookies";
import { GrowlModule } from "primeng/components/growl/growl";
import {
ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, OverlayPanelModule, SharedModule, SidebarModule,
TooltipModule
} from "primeng/primeng";
import { import { ButtonModule } from "primeng/button";
MatButtonModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, MatAutocompleteModule, MatCheckboxModule, MatSnackBarModule, import { ConfirmDialogModule } from "primeng/confirmdialog";
MatProgressSpinnerModule import { DataViewModule } from "primeng/dataview";
} from '@angular/material'; import { DialogModule } from "primeng/dialog";
import { MatCardModule, MatInputModule, MatTabsModule, MatSlideToggleModule } from "@angular/material"; import { OverlayPanelModule } from "primeng/overlaypanel";
import { TooltipModule } from "primeng/tooltip";
import { SidebarModule } from "primeng/sidebar";
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatNativeDateModule } from '@angular/material/core';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatCardModule } from "@angular/material/card";
import { MatInputModule } from "@angular/material/input";
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
import { MatTabsModule } from "@angular/material/tabs";
import { MDBBootstrapModule, CardsFreeModule, NavbarModule } from "angular-bootstrap-md"; import { MDBBootstrapModule, CardsFreeModule, NavbarModule } from "angular-bootstrap-md";
@ -70,9 +81,8 @@ const routes: Routes = [
{ loadChildren: () => import("./settings/settings.module").then(m => m.SettingsModule), path: "Settings" }, { loadChildren: () => import("./settings/settings.module").then(m => m.SettingsModule), path: "Settings" },
{ loadChildren: () => import("./wizard/wizard.module").then(m => m.WizardModule), path: "Wizard" }, { loadChildren: () => import("./wizard/wizard.module").then(m => m.WizardModule), path: "Wizard" },
{ loadChildren: () => import("./usermanagement/usermanagement.module").then(m => m.UserManagementModule), path: "usermanagement" }, { loadChildren: () => import("./usermanagement/usermanagement.module").then(m => m.UserManagementModule), path: "usermanagement" },
{ loadChildren: () => import("./requests/requests.module").then(m => m.RequestsModule), path: "requestsOld" }, // { loadChildren: () => import("./requests/requests.module").then(m => m.RequestsModule), path: "requestsOld" },
{ loadChildren: () => import("./requests-list/requests-list.module").then(m => m.RequestsListModule), path: "requests-list" }, { loadChildren: () => import("./requests-list/requests-list.module").then(m => m.RequestsListModule), path: "requests-list" },
{ loadChildren: () => import("./recentlyAdded/recentlyAdded.module").then(m => m.RecentlyAddedModule), path: "recentlyadded" },
{ loadChildren: () => import("./vote/vote.module").then(m => m.VoteModule), path: "vote" }, { loadChildren: () => import("./vote/vote.module").then(m => m.VoteModule), path: "vote" },
{ loadChildren: () => import("./media-details/media-details.module").then(m => m.MediaDetailsModule), path: "details" }, { loadChildren: () => import("./media-details/media-details.module").then(m => m.MediaDetailsModule), path: "details" },
{ loadChildren: () => import("./user-preferences/user-preferences.module").then(m => m.UserPreferencesModule), path: "user-preferences" }, { loadChildren: () => import("./user-preferences/user-preferences.module").then(m => m.UserPreferencesModule), path: "user-preferences" },
@ -106,23 +116,19 @@ export function JwtTokenGetter() {
BrowserModule, BrowserModule,
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
GrowlModule,
ButtonModule, ButtonModule,
FormsModule, FormsModule,
DataTableModule, DataViewModule,
MatSnackBarModule, MatSnackBarModule,
SharedModule,
MatSnackBarModule, MatSnackBarModule,
DialogModule, DialogModule,
MatButtonModule, MatButtonModule,
NavbarModule, NavbarModule,
NgbModule.forRoot(),
MatCardModule, MatCardModule,
MatInputModule, MatInputModule,
MatTabsModule, MatTabsModule,
ReactiveFormsModule, ReactiveFormsModule,
MatAutocompleteModule, MatAutocompleteModule,
CaptchaModule,
TooltipModule, TooltipModule,
ConfirmDialogModule, ConfirmDialogModule,
OverlayPanelModule, OverlayPanelModule,
@ -168,7 +174,6 @@ export function JwtTokenGetter() {
IdentityService, IdentityService,
StatusService, StatusService,
LandingPageService, LandingPageService,
ConfirmationService,
ImageService, ImageService,
CustomPageService, CustomPageService,
CookieService, CookieService,

@ -1,3 +1,3 @@
<div class="small-middle-container"> <div class="small-middle-container" *ngIf="entries">
<p-fullCalendar [events]="entries" [options]="options"></p-fullCalendar> <p-fullCalendar [events]="entries" [options]="options"></p-fullCalendar>
</div> </div>

@ -3,6 +3,9 @@ import { Component, OnInit } from "@angular/core";
import { CalendarService } from "../../services/calendar.service"; import { CalendarService } from "../../services/calendar.service";
import { ICalendarModel } from "../../interfaces/ICalendar"; import { ICalendarModel } from "../../interfaces/ICalendar";
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
@Component({ @Component({
templateUrl: "./calendar.component.html", templateUrl: "./calendar.component.html",
styleUrls: ["./calendar.component.scss"], styleUrls: ["./calendar.component.scss"],
@ -18,9 +21,8 @@ export class CalendarComponent implements OnInit {
public async ngOnInit() { public async ngOnInit() {
this.loading() this.loading()
this.entries = await this.calendarService.getCalendarEntries();
this.options = { this.options = {
plugins: [dayGridPlugin, interactionPlugin],
defaultDate: new Date(), defaultDate: new Date(),
header: { header: {
left: 'prev,next', left: 'prev,next',
@ -31,6 +33,8 @@ export class CalendarComponent implements OnInit {
e.preventDefault(); e.preventDefault();
} }
}; };
this.entries = await this.calendarService.getCalendarEntries();
this.finishLoading(); this.finishLoading();
} }

@ -1,5 +1,5 @@
import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core"; import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from "@angular/material"; import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from "@angular/material/dialog";
import { IDiscoverCardResult } from "../../interfaces"; import { IDiscoverCardResult } from "../../interfaces";
import { SearchV2Service, RequestService, MessageService } from "../../../services"; import { SearchV2Service, RequestService, MessageService } from "../../../services";
import { RequestType } from "../../../interfaces"; import { RequestType } from "../../../interfaces";

@ -2,7 +2,7 @@ import { Component, OnInit, Input } from "@angular/core";
import { IDiscoverCardResult } from "../../interfaces"; import { IDiscoverCardResult } from "../../interfaces";
import { RequestType, ISearchTvResult, ISearchMovieResult } from "../../../interfaces"; import { RequestType, ISearchTvResult, ISearchMovieResult } from "../../../interfaces";
import { SearchV2Service } from "../../../services"; import { SearchV2Service } from "../../../services";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
import { DiscoverCardDetailsComponent } from "./discover-card-details.component"; import { DiscoverCardDetailsComponent } from "./discover-card-details.component";
import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2";
import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";

@ -35,6 +35,8 @@ export class DiscoverComponent implements OnInit {
public loadingFlag: boolean; public loadingFlag: boolean;
public scrollDisabled: boolean; public scrollDisabled: boolean;
private amountToLoad = 14;
private contentLoaded: number; private contentLoaded: number;
private isScrolling: boolean = false; private isScrolling: boolean = false;
private mediaTypeStorageKey = "DiscoverOptions"; private mediaTypeStorageKey = "DiscoverOptions";
@ -51,14 +53,14 @@ export class DiscoverComponent implements OnInit {
this.scrollDisabled = true; this.scrollDisabled = true;
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.popularMoviesByPage(0, 12); this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
this.tvShows = await this.searchService.popularTvByPage(0, 12); this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.popularMoviesByPage(0, 12); this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.popularTvByPage(0, 12); this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
break; break;
} }
@ -69,6 +71,7 @@ export class DiscoverComponent implements OnInit {
} }
public async onScroll() { public async onScroll() {
console.log("scrolled");
if (!this.contentLoaded) { if (!this.contentLoaded) {
return; return;
} }
@ -78,42 +81,42 @@ export class DiscoverComponent implements OnInit {
if (this.popularActive) { if (this.popularActive) {
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.popularMoviesByPage(this.contentLoaded, 12); this.movies = await this.searchService.popularMoviesByPage(this.contentLoaded, this.amountToLoad);
this.tvShows = await this.searchService.popularTvByPage(this.contentLoaded, 12); this.tvShows = await this.searchService.popularTvByPage(this.contentLoaded, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.popularMoviesByPage(this.contentLoaded, 12); this.movies = await this.searchService.popularMoviesByPage(this.contentLoaded, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.popularTvByPage(this.contentLoaded, 12); this.tvShows = await this.searchService.popularTvByPage(this.contentLoaded, this.amountToLoad);
break; break;
} }
} }
if (this.trendingActive) { if (this.trendingActive) {
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.nowPlayingMoviesByPage(this.contentLoaded, 12); this.movies = await this.searchService.nowPlayingMoviesByPage(this.contentLoaded, this.amountToLoad);
this.tvShows = await this.searchService.trendingTvByPage(this.contentLoaded, 12); this.tvShows = await this.searchService.trendingTvByPage(this.contentLoaded, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.nowPlayingMoviesByPage(this.contentLoaded, 12); this.movies = await this.searchService.nowPlayingMoviesByPage(this.contentLoaded, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.trendingTvByPage(this.contentLoaded, 12); this.tvShows = await this.searchService.trendingTvByPage(this.contentLoaded, this.amountToLoad);
break; break;
} }
} }
if (this.upcomingActive) { if (this.upcomingActive) {
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.upcomingMoviesByPage(this.contentLoaded, 12); this.movies = await this.searchService.upcomingMoviesByPage(this.contentLoaded, this.amountToLoad);
this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, 12); this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.upcomingMoviesByPage(this.contentLoaded, 12); this.movies = await this.searchService.upcomingMoviesByPage(this.contentLoaded, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, 12); this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, this.amountToLoad);
break; break;
} }
} }
@ -135,14 +138,14 @@ export class DiscoverComponent implements OnInit {
this.upcomingActive = false; this.upcomingActive = false;
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.popularMoviesByPage(0, 12); this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
this.tvShows = await this.searchService.popularTvByPage(0, 12); this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.popularMoviesByPage(0, 12); this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.popularTvByPage(0, 12); this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
break; break;
} }
@ -162,14 +165,14 @@ export class DiscoverComponent implements OnInit {
this.upcomingActive = false; this.upcomingActive = false;
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.nowPlayingMoviesByPage(0, 12); this.movies = await this.searchService.nowPlayingMoviesByPage(0, this.amountToLoad);
this.tvShows = await this.searchService.trendingTvByPage(0, 12); this.tvShows = await this.searchService.trendingTvByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.nowPlayingMoviesByPage(0, 12); this.movies = await this.searchService.nowPlayingMoviesByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.trendingTvByPage(0, 12); this.tvShows = await this.searchService.trendingTvByPage(0, this.amountToLoad);
break; break;
} }
@ -188,14 +191,14 @@ export class DiscoverComponent implements OnInit {
this.upcomingActive = true; this.upcomingActive = true;
switch (this.discoverOptions) { switch (this.discoverOptions) {
case DiscoverOption.Combined: case DiscoverOption.Combined:
this.movies = await this.searchService.upcomingMoviesByPage(0, 12); this.movies = await this.searchService.upcomingMoviesByPage(0, this.amountToLoad);
this.tvShows = await this.searchService.anticipatedTvByPage(0, 12); this.tvShows = await this.searchService.anticipatedTvByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Movie: case DiscoverOption.Movie:
this.movies = await this.searchService.upcomingMoviesByPage(0, 12); this.movies = await this.searchService.upcomingMoviesByPage(0, this.amountToLoad);
break; break;
case DiscoverOption.Tv: case DiscoverOption.Tv:
this.tvShows = await this.searchService.anticipatedTvByPage(0, 12); this.tvShows = await this.searchService.anticipatedTvByPage(0, this.amountToLoad);
break; break;
} }

@ -6,7 +6,7 @@ import { DiscoverCardComponent } from "./card/discover-card.component";
import { Routes } from "@angular/router"; import { Routes } from "@angular/router";
import { AuthGuard } from "../../auth/auth.guard"; import { AuthGuard } from "../../auth/auth.guard";
import { SearchService, RequestService } from "../../services"; import { SearchService, RequestService } from "../../services";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
export const components: any[] = [ export const components: any[] = [

@ -1,9 +1,7 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { OrderModule } from "ngx-order-pipe"; import { OrderModule } from "ngx-order-pipe";
import { PaginatorModule, SharedModule, TabViewModule } from "primeng/primeng";
import { IdentityService, SearchService } from "../services"; import { IdentityService, SearchService } from "../services";
@ -16,7 +14,6 @@ import { IssuesComponent } from "./issues.component";
import { IssuesTableComponent } from "./issuestable.component"; import { IssuesTableComponent } from "./issuestable.component";
import { PipeModule } from "../pipes/pipe.module"; import { PipeModule } from "../pipes/pipe.module";
import { IssuesListComponent } from "./components/issues-list/issues-list.component";
import * as fromComponents from "./components"; import * as fromComponents from "./components";
@ -28,13 +25,9 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild(routes), RouterModule.forChild(routes),
NgbModule.forRoot(),
SharedModule,
OrderModule, OrderModule,
PipeModule, PipeModule,
OmbiShared, OmbiShared,
PaginatorModule,
TabViewModule,
], ],
declarations: [ declarations: [
IssuesComponent, IssuesComponent,

@ -15,7 +15,7 @@ import { ImageService } from "../services";
import { fadeInOutAnimation } from "../animations/fadeinout"; import { fadeInOutAnimation } from "../animations/fadeinout";
import { StorageService } from "../shared/storage/storage-service"; import { StorageService } from "../shared/storage/storage-service";
import { MatSnackBar } from "@angular/material"; import { MatSnackBar } from "@angular/material/snack-bar";
@Component({ @Component({
templateUrl: "./login.component.html", templateUrl: "./login.component.html",

@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, MessageService } from "../../../services"; import { ImageService, SearchV2Service, RequestService, MessageService } from "../../../services";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component"; import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component"; import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component";

@ -3,7 +3,7 @@ import { ImageService, SearchV2Service, RequestService, MessageService } from ".
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component"; import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { IMovieRequests, RequestType, IAdvancedData } from "../../../interfaces"; import { IMovieRequests, RequestType, IAdvancedData } from "../../../interfaces";

@ -1,7 +1,7 @@
import { Component, Input, OnInit, EventEmitter, Output } from "@angular/core"; import { Component, Input, OnInit, EventEmitter, Output } from "@angular/core";
import { RadarrService } from "../../../../../services"; import { RadarrService } from "../../../../../services";
import { IRadarrProfile, IRadarrRootFolder, IMovieRequests, IAdvancedData } from "../../../../../interfaces"; import { IRadarrProfile, IRadarrRootFolder, IMovieRequests, IAdvancedData } from "../../../../../interfaces";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
import { MovieAdvancedOptionsComponent } from "../movie-advanced-options/movie-advanced-options.component"; import { MovieAdvancedOptionsComponent } from "../movie-advanced-options/movie-advanced-options.component";
import { RequestServiceV2 } from "../../../../../services/requestV2.service"; import { RequestServiceV2 } from "../../../../../services/requestV2.service";

@ -1,5 +1,5 @@
import { Component, Inject } from "@angular/core"; import { Component, Inject } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { IAdvancedData } from "../../../../../interfaces"; import { IAdvancedData } from "../../../../../interfaces";
@Component({ @Component({

@ -3,8 +3,8 @@
<mat-card-content> <mat-card-content>
<p-carousel [value]="cast" [numVisible]="5" easing="easeOutStrong"> <p-carousel [value]="cast" [numVisible]="5" easing="easeOutStrong">
<ng-template let-item pTemplate="item"> <ng-template let-item pTemplate="item">
<div class="row justify-content-md-center mat-card mat-card-flat"> <div class="row justify-content-md-center mat-card mat-card-flat col-12 carousel-item">
<div class="col-12"> <div class="col-12 bottom-space">
<a *ngIf="item.profile_path" [routerLink]="'/discover/actor/' + item.id"> <a *ngIf="item.profile_path" [routerLink]="'/discover/actor/' + item.id">
<img class="cast-profile-img" src="https://image.tmdb.org/t/p/w300/{{item.profile_path}}"> <img class="cast-profile-img" src="https://image.tmdb.org/t/p/w300/{{item.profile_path}}">
</a> </a>
@ -26,4 +26,4 @@
</ng-template> </ng-template>
</p-carousel> </p-carousel>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>

@ -1,7 +1,64 @@
@import "~@angular/material/theming"; @import "~@angular/material/theming";
@import "~styles/variables.scss"; @import "~styles/variables.scss";
.actor-background { ::ng-deep body .ui-carousel .ui-carousel-content .ui-carousel-prev,
.dark & { body .ui-carousel .ui-carousel-content .ui-carousel-next {
background: $backgroundTint-dark; background-color: #ffffff;
} border: solid 1px rgba(178, 193, 205, 0.64);
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
margin: 0.2em;
color: #333333;
-moz-transition: color 0.2s;
-o-transition: color 0.2s;
-webkit-transition: color 0.2s;
transition: color 0.2s;
}
::ng-deep body .ui-carousel .ui-carousel-content .ui-carousel-prev:not(.ui-state-disabled):hover,
body .ui-carousel .ui-carousel-content .ui-carousel-next:not(.ui-state-disabled):hover {
background-color: #ffffff;
color: #000;
border-color: solid 1px rgba(178, 193, 205, 0.64);
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item>.ui-button {
border-color: transparent;
background-color: transparent;
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item .ui-carousel-dot-icon {
width: 20px;
height: 6px;
background-color: rgba(255, 255, 255, 0.44);
margin: 0 0.2em;
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item .ui-carousel-dot-icon::before {
content: " ";
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item.ui-state-highlight .ui-carousel-dot-icon {
background-color: #FFF;
}
.carousel-item {
text-align: center;
}
::ng-deep .ui-carousel-next {
background-color: #ffffff;
border: solid 1px rgba(178, 193, 205, 0.64);
border-radius: 50%;
margin: 0.2em;
color: #333333;
transition: color 0.2s;
}
::ng-deep .ui-carousel-content button:focus {
outline: none;
}
.bottom-space {
padding-bottom: 10px;
} }

@ -1,6 +1,6 @@
import { Component, Inject, Output, EventEmitter } from "@angular/core"; import { Component, Inject, Output, EventEmitter } from "@angular/core";
import { IDenyDialogData } from "../interfaces/interfaces"; import { IDenyDialogData } from "../interfaces/interfaces";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { RequestService, MessageService } from "../../../../services"; import { RequestService, MessageService } from "../../../../services";
import { RequestType, IRequestEngineResult } from "../../../../interfaces"; import { RequestType, IRequestEngineResult } from "../../../../interfaces";

@ -1,6 +1,6 @@
import { Component, Inject, OnInit } from "@angular/core"; import { Component, Inject, OnInit } from "@angular/core";
import { IDenyDialogData, IIssueDialogData } from "../interfaces/interfaces"; import { IDenyDialogData, IIssueDialogData } from "../interfaces/interfaces";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { MessageService, IssuesService } from "../../../../services"; import { MessageService, IssuesService } from "../../../../services";
import { IIssues, IIssueCategory, IssueStatus, RequestType } from "../../../../interfaces"; import { IIssues, IIssueCategory, IssueStatus, RequestType } from "../../../../interfaces";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";

@ -1,5 +1,5 @@
import { Component, Inject } from "@angular/core"; import { Component, Inject } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
@Component({ @Component({
selector: "youtube-trailer", selector: "youtube-trailer",

@ -2,7 +2,7 @@ import { Component, Input } from "@angular/core";
import { IChildRequests, RequestType } from "../../../../../interfaces"; import { IChildRequests, RequestType } from "../../../../../interfaces";
import { RequestService } from "../../../../../services/request.service"; import { RequestService } from "../../../../../services/request.service";
import { MessageService } from "../../../../../services"; import { MessageService } from "../../../../../services";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component"; import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component";
@Component({ @Component({

@ -3,7 +3,7 @@ import { ImageService, SearchV2Service, MessageService, RequestService } from ".
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2";
import { MatDialog } from "@angular/material"; import { MatDialog } from "@angular/material/dialog";
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component"; import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component";
import { IChildRequests, RequestType } from "../../../interfaces"; import { IChildRequests, RequestType } from "../../../interfaces";

@ -63,7 +63,7 @@
// changed bottom to 10px so when you overlay a link it won't get blocked by URL // changed bottom to 10px so when you overlay a link it won't get blocked by URL
.bottom-nav-link { .bottom-nav-link {
bottom: 10px; bottom: 10px;
position: absolute; position: absolute !important;
//background-color:#E84C3D; //background-color:#E84C3D;
} }

@ -12,9 +12,8 @@ import { empty, of } from "rxjs";
import { SearchV2Service } from "../services/searchV2.service"; import { SearchV2Service } from "../services/searchV2.service";
import { IMultiSearchResult } from "../interfaces"; import { IMultiSearchResult } from "../interfaces";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { NgbTypeaheadSelectItemEvent } from "@ng-bootstrap/ng-bootstrap";
import { FormGroup, FormBuilder } from "@angular/forms"; import { FormGroup, FormBuilder } from "@angular/forms";
import { MatAutocompleteSelectedEvent } from "@angular/material"; import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
@Component({ @Component({
selector: "app-nav-search", selector: "app-nav-search",

@ -11,7 +11,7 @@ import { QualityPipe } from "./QualityPipe";
}) })
export class PipeModule { export class PipeModule {
public static forRoot(): ModuleWithProviders { public static forRoot(): ModuleWithProviders<PipeModule> {
return { return {
ngModule: PipeModule, ngModule: PipeModule,
providers: [], providers: [],

@ -1,51 +0,0 @@
<h1>Recently Added</h1>
<input type="checkbox" [(ngModel)]="groupTv" (click)="change()" />
<hr />
<p-calendar [(ngModel)]="range" showButtonBar="true" selectionMode="range" (onClose)="close()"></p-calendar>
<hr />
<style>
.img-conatiner {
position: relative;
text-align: center;
color: white;
}
/* Bottom left text */
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
</style>
<ngu-carousel [inputs]="carouselTile">
<ngu-tile NguCarouselItem *ngFor="let movie of movies">
<div class="img-container">
<img class="img-responsive poster" src="{{movie.posterPath}}" style="width: 300px" alt="poster">
<div class="bottom-left"> {{movie.title}}</div>
</div>
</ngu-tile>
<button NguCarouselPrev class='leftRs'><i class="fa fa-arrow-left"></i></button>
<button NguCarouselNext class='rightRs'><i class="fa fa-arrow-right"></i></button>
</ngu-carousel>
<hr/>
<ngu-carousel [inputs]="carouselTile">
<ngu-tile NguCarouselItem *ngFor="let t of tv">
<img class="img-responsive poster" src="{{t.posterPath}}" style="width: 300px" alt="poster">
<b>{{t.title}}</b>
<br>
<b>Season: {{t.seasonNumber}}</b>
<br>
<b>Episode: {{t.episodeNumber}}</b>
</ngu-tile>
<button NguCarouselPrev class='leftRs'><i class="fa fa-arrow-left"></i></button>
<button NguCarouselNext class='rightRs'><i class="fa fa-arrow-right"></i></button>
</ngu-carousel>

@ -1,131 +0,0 @@
import { Component, OnInit } from "@angular/core";
import { NguCarouselConfig } from "@ngu/carousel";
import { IRecentlyAddedMovies, IRecentlyAddedTvShows } from "../interfaces";
import { ImageService, RecentlyAddedService } from "../services";
@Component({
templateUrl: "recentlyAdded.component.html",
styles: [`
.leftRs {
position: absolute;
margin: auto;
top: 0;
bottom: 0;
width: 50px;
height: 50px;
box-shadow: 1px 2px 10px -1px rgba(0, 0, 0, .3);
border-radius: 100%;
left: 0;
background: #df691a;
}
.rightRs {
position: absolute;
margin: auto;
top: 0;
bottom: 0;
width: 50px;
height: 50px;
box-shadow: 1px 2px 10px -1px rgba(0, 0, 0, .3);
border-radius: 100%;
right: 0;
background: #df691a;
}
`],
})
export class RecentlyAddedComponent implements OnInit {
public movies: IRecentlyAddedMovies[];
public tv: IRecentlyAddedTvShows[];
public range: Date[];
public groupTv: boolean = false;
// https://github.com/sheikalthaf/ngu-carousel
public carouselTile: NguCarouselConfig;
constructor(private recentlyAddedService: RecentlyAddedService,
private imageService: ImageService) {}
public ngOnInit() {
this.getMovies();
this.getShows();
this.carouselTile = {
grid: {xs: 2, sm: 3, md: 3, lg: 5, all: 0},
slide: 2,
speed: 400,
animation: "lazy",
point: {
visible: true,
},
load: 2,
touch: true,
easing: "ease",
};
}
public close() {
if (this.range.length < 2) {
return;
}
if (!this.range[1]) {
// If we do not have a second date then just set it to now
this.range[1] = new Date();
}
this.getMovies();
}
public change() {
this.getShows();
}
private getShows() {
if (this.groupTv) {
this.recentlyAddedService.getRecentlyAddedTvGrouped().subscribe(x => {
this.tv = x;
this.tv.forEach((t) => {
this.imageService.getTvPoster(t.tvDbId).subscribe(p => {
if (p) {
t.posterPath = p;
}
});
});
});
} else {
this.recentlyAddedService.getRecentlyAddedTv().subscribe(x => {
this.tv = x;
this.tv.forEach((t) => {
this.imageService.getTvPoster(t.tvDbId).subscribe(p => {
if (p) {
t.posterPath = p;
}
});
});
});
}
}
private getMovies() {
this.recentlyAddedService.getRecentlyAddedMovies().subscribe(x => {
this.movies = x;
this.movies.forEach((movie) => {
if (movie.theMovieDbId) {
this.imageService.getMoviePoster(movie.theMovieDbId).subscribe(p => {
movie.posterPath = p;
});
} else if (movie.imdbId) {
this.imageService.getMoviePoster(movie.imdbId).subscribe(p => {
movie.posterPath = p;
});
} else {
movie.posterPath = "";
}
});
});
}
}

@ -1,47 +0,0 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { OrderModule } from "ngx-order-pipe";
import { CalendarModule, PaginatorModule, SharedModule, TabViewModule } from "primeng/primeng";
import { IdentityService, ImageService, RecentlyAddedService } from "../services";
import { AuthGuard } from "../auth/auth.guard";
import { SharedModule as OmbiShared } from "../shared/shared.module";
import { RecentlyAddedComponent } from "./recentlyAdded.component";
import { NguCarouselModule } from "@ngu/carousel";
const routes: Routes = [
{ path: "", component: RecentlyAddedComponent, canActivate: [AuthGuard] },
];
@NgModule({
imports: [
RouterModule.forChild(routes),
NgbModule.forRoot(),
SharedModule,
OrderModule,
OmbiShared,
PaginatorModule,
TabViewModule,
CalendarModule,
NguCarouselModule,
],
declarations: [
RecentlyAddedComponent,
],
exports: [
RouterModule,
],
providers: [
IdentityService,
RecentlyAddedService,
ImageService,
],
})
export class RecentlyAddedModule { }

@ -1,6 +1,7 @@
import { Component, AfterViewInit, ViewChild, EventEmitter, Output, ChangeDetectorRef, OnInit } from "@angular/core"; import { Component, AfterViewInit, ViewChild, EventEmitter, Output, ChangeDetectorRef, OnInit } from "@angular/core";
import { IMovieRequests, IRequestsViewModel } from "../../../interfaces"; import { IMovieRequests, IRequestsViewModel } from "../../../interfaces";
import { MatPaginator, MatSort } from "@angular/material"; import { MatPaginator } from "@angular/material/paginator";
import { MatSort } from "@angular/material/sort";
import { merge, Observable, of as observableOf } from 'rxjs'; import { merge, Observable, of as observableOf } from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators'; import { catchError, map, startWith, switchMap } from 'rxjs/operators';
@ -18,7 +19,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
public dataSource: IMovieRequests[] = []; public dataSource: IMovieRequests[] = [];
public resultsLength: number; public resultsLength: number;
public isLoadingResults = true; public isLoadingResults = true;
public displayedColumns: string[] = ['requestedUser.requestedBy', 'title', 'requestedDate', 'status', 'requestStatus', 'actions']; public displayedColumns: string[] = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate', 'actions'];
public gridCount: string = "15"; public gridCount: string = "15";
public isAdmin: boolean; public isAdmin: boolean;
public defaultSort: string = "requestedDate"; public defaultSort: string = "requestedDate";
@ -35,8 +36,8 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
@Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any }>(); @Output() public onOpenOptions = new EventEmitter<{ request: any, filter: any, onChange: any }>();
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort, { static: false }) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
private auth: AuthService, private storageService: StorageService) { private auth: AuthService, private storageService: StorageService) {

@ -1,5 +1,5 @@
import { Component, ViewChild } from "@angular/core"; import { Component, ViewChild } from "@angular/core";
import { MatBottomSheet } from "@angular/material"; import { MatBottomSheet } from "@angular/material/bottom-sheet";
import { RequestOptionsComponent } from "./options/request-options.component"; import { RequestOptionsComponent } from "./options/request-options.component";
import { UpdateType } from "../models/UpdateType"; import { UpdateType } from "../models/UpdateType";
import { MoviesGridComponent } from "./movies-grid/movies-grid.component"; import { MoviesGridComponent } from "./movies-grid/movies-grid.component";

@ -1,6 +1,7 @@
import { Component, AfterViewInit, ViewChild, Output, EventEmitter, ChangeDetectorRef, OnInit } from "@angular/core"; import { Component, AfterViewInit, ViewChild, Output, EventEmitter, ChangeDetectorRef, OnInit } from "@angular/core";
import { IRequestsViewModel, IChildRequests } from "../../../interfaces"; import { IRequestsViewModel, IChildRequests } from "../../../interfaces";
import { MatPaginator, MatSort } from "@angular/material"; import { MatPaginator } from "@angular/material/paginator";
import { MatSort } from "@angular/material/sort";
import { merge, of as observableOf, Observable } from 'rxjs'; import { merge, of as observableOf, Observable } from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators'; import { catchError, map, startWith, switchMap } from 'rxjs/operators';
@ -34,8 +35,8 @@ export class TvGridComponent implements OnInit, AfterViewInit {
@Output() public onOpenOptions = new EventEmitter<{request: any, filter: any, onChange: any}>(); @Output() public onOpenOptions = new EventEmitter<{request: any, filter: any, onChange: any}>();
@ViewChild(MatPaginator, {static: false}) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort, {static: false}) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
constructor(private requestService: RequestServiceV2, private auth: AuthService, constructor(private requestService: RequestServiceV2, private auth: AuthService,
private ref: ChangeDetectorRef, private storageService: StorageService) { private ref: ChangeDetectorRef, private storageService: StorageService) {

@ -9,7 +9,7 @@ import { AuthGuard } from "../auth/auth.guard";
import * as fromComponents from './components'; import * as fromComponents from './components';
import { RequestsListComponent } from "./components/requests-list.component"; import { RequestsListComponent } from "./components/requests-list.component";
import { MatBottomSheetModule } from "@angular/material"; import { MatBottomSheetModule } from "@angular/material/bottom-sheet";
const routes: Routes = [ const routes: Routes = [
{ path: "", component: RequestsListComponent, canActivate: [AuthGuard] }, { path: "", component: RequestsListComponent, canActivate: [AuthGuard] },

@ -1,386 +1,386 @@
import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; // import { PlatformLocation, APP_BASE_HREF } from "@angular/common";
import { Component, Input, OnInit, Inject } from "@angular/core"; // import { Component, Input, OnInit, Inject } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser"; // import { DomSanitizer } from "@angular/platform-browser";
import { Subject } from "rxjs"; // import { Subject } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators"; // import { debounceTime, distinctUntilChanged } from "rxjs/operators";
import { AuthService } from "../auth/auth.service"; // import { AuthService } from "../auth/auth.service";
import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces"; // import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces";
import { NotificationService, RadarrService, RequestService } from "../services"; // import { NotificationService, RadarrService, RequestService } from "../services";
@Component({ // @Component({
selector: "movie-requests", // selector: "movie-requests",
templateUrl: "./movierequests.component.html", // templateUrl: "./movierequests.component.html",
}) // })
export class MovieRequestsComponent implements OnInit { // export class MovieRequestsComponent implements OnInit {
public movieRequests: IMovieRequests[]; // public movieRequests: IMovieRequests[];
public defaultPoster: string; // public defaultPoster: string;
public searchChanged: Subject<string> = new Subject<string>(); // public searchChanged: Subject<string> = new Subject<string>();
public searchText: string; // public searchText: string;
public isAdmin: boolean; // Also PowerUser // public isAdmin: boolean; // Also PowerUser
public radarrProfiles: IRadarrProfile[]; // public radarrProfiles: IRadarrProfile[];
public radarrRootFolders: IRadarrRootFolder[]; // public radarrRootFolders: IRadarrRootFolder[];
@Input() public issueCategories: IIssueCategory[]; // @Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean; // @Input() public issuesEnabled: boolean;
public issuesBarVisible = false; // public issuesBarVisible = false;
public issueRequest: IMovieRequests; // public issueRequest: IMovieRequests;
public issueProviderId: string; // public issueProviderId: string;
public issueCategorySelected: IIssueCategory; // public issueCategorySelected: IIssueCategory;
public filterDisplay: boolean; // public filterDisplay: boolean;
public filter: IFilter; // public filter: IFilter;
public filterType = FilterType; // public filterType = FilterType;
public orderType: OrderType = OrderType.RequestedDateDesc; // public orderType: OrderType = OrderType.RequestedDateDesc;
public OrderType = OrderType; // public OrderType = OrderType;
public denyDisplay: boolean; // public denyDisplay: boolean;
public requestToDeny: IMovieRequests; // public requestToDeny: IMovieRequests;
public rejectionReason: string; // public rejectionReason: string;
public totalMovies: number = 100; // public totalMovies: number = 100;
public currentlyLoaded: number; // public currentlyLoaded: number;
private amountToLoad: number; // private amountToLoad: number;
private href: string; // private href: string;
constructor( // constructor(
private requestService: RequestService, // private requestService: RequestService,
private auth: AuthService, // private auth: AuthService,
private notificationService: NotificationService, // private notificationService: NotificationService,
private radarrService: RadarrService, // private radarrService: RadarrService,
private sanitizer: DomSanitizer, // private sanitizer: DomSanitizer,
@Inject(APP_BASE_HREF) href:string) { // @Inject(APP_BASE_HREF) href:string) {
this.href = href; // this.href = href;
this.searchChanged.pipe( // this.searchChanged.pipe(
debounceTime(600), // Wait Xms after the last event before emitting last event // debounceTime(600), // Wait Xms after the last event before emitting last event
distinctUntilChanged(), // only emit if value is different from previous value // distinctUntilChanged(), // only emit if value is different from previous value
).subscribe(x => { // ).subscribe(x => {
this.searchText = x as string; // this.searchText = x as string;
if (this.searchText === "") { // if (this.searchText === "") {
this.resetSearch(); // this.resetSearch();
return; // return;
} // }
this.requestService.searchMovieRequests(this.searchText) // this.requestService.searchMovieRequests(this.searchText)
.subscribe(m => { // .subscribe(m => {
this.setOverrides(m); // this.setOverrides(m);
this.movieRequests = m; // this.movieRequests = m;
}); // });
}); // });
this.defaultPoster = "../../../images/default_movie_poster.png"; // this.defaultPoster = "../../../images/default_movie_poster.png";
const base = this.href; // const base = this.href;
if (base) { // if (base) {
this.defaultPoster = "../../.." + base + "/images/default_movie_poster.png"; // this.defaultPoster = "../../.." + base + "/images/default_movie_poster.png";
} // }
} // }
public ngOnInit() { // public ngOnInit() {
this.amountToLoad = 10; // this.amountToLoad = 10;
this.currentlyLoaded = 10; // this.currentlyLoaded = 10;
this.filter = { // this.filter = {
availabilityFilter: FilterType.None, // availabilityFilter: FilterType.None,
statusFilter: FilterType.None, // statusFilter: FilterType.None,
}; // };
this.loadInit(); // this.loadInit();
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); // this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
} // }
public paginate(event: IPagenator) { // public paginate(event: IPagenator) {
const skipAmount = event.first; // const skipAmount = event.first;
this.loadRequests(this.amountToLoad, skipAmount); // this.loadRequests(this.amountToLoad, skipAmount);
} // }
public search(text: any) { // public search(text: any) {
this.searchChanged.next(text.target.value); // this.searchChanged.next(text.target.value);
} // }
public removeRequest(request: IMovieRequests) { // public removeRequest(request: IMovieRequests) {
this.requestService.removeMovieRequest(request.id); // this.requestService.removeMovieRequest(request.id);
this.removeRequestFromUi(request); // this.removeRequestFromUi(request);
this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); // this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0);
} // }
public changeAvailability(request: IMovieRequests, available: boolean) { // public changeAvailability(request: IMovieRequests, available: boolean) {
request.available = available; // request.available = available;
if (available) { // if (available) {
this.requestService.markMovieAvailable({ id: request.id }).subscribe(x => { // this.requestService.markMovieAvailable({ id: request.id }).subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`${request.title} Is now available`); // `${request.title} Is now available`);
} else { // } else {
this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} else { // } else {
this.requestService.markMovieUnavailable({ id: request.id }).subscribe(x => { // this.requestService.markMovieUnavailable({ id: request.id }).subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`${request.title} Is now unavailable`); // `${request.title} Is now unavailable`);
} else { // } else {
this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} // }
} // }
public approve(request: IMovieRequests) { // public approve(request: IMovieRequests) {
request.approved = true; // request.approved = true;
this.approveRequest(request); // this.approveRequest(request);
} // }
public deny(request: IMovieRequests) { // public deny(request: IMovieRequests) {
this.requestToDeny = request; // this.requestToDeny = request;
this.denyDisplay = true; // this.denyDisplay = true;
} // }
public denyRequest() { // public denyRequest() {
this.requestService.denyMovie({ id: this.requestToDeny.id, reason: this.rejectionReason }) // this.requestService.denyMovie({ id: this.requestToDeny.id, reason: this.rejectionReason })
.subscribe(x => { // .subscribe(x => {
this.denyDisplay = false; // this.denyDisplay = false;
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`Request for ${this.requestToDeny.title} has been denied successfully`); // `Request for ${this.requestToDeny.title} has been denied successfully`);
const index = this.movieRequests.indexOf(this.requestToDeny, 0); // const index = this.movieRequests.indexOf(this.requestToDeny, 0);
if (index > -1) { // if (index > -1) {
this.movieRequests[index].denied = true; // this.movieRequests[index].denied = true;
} // }
} else { // } else {
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
this.requestToDeny.denied = false; // this.requestToDeny.denied = false;
} // }
}); // });
} // }
public selectRootFolder(searchResult: IMovieRequests, rootFolderSelected: IRadarrRootFolder, event: any) { // public selectRootFolder(searchResult: IMovieRequests, rootFolderSelected: IRadarrRootFolder, event: any) {
event.preventDefault(); // event.preventDefault();
searchResult.rootPathOverride = rootFolderSelected.id; // searchResult.rootPathOverride = rootFolderSelected.id;
this.setOverride(searchResult); // this.setOverride(searchResult);
this.updateRequest(searchResult); // this.updateRequest(searchResult);
} // }
public selectQualityProfile(searchResult: IMovieRequests, profileSelected: IRadarrProfile, event: any) { // public selectQualityProfile(searchResult: IMovieRequests, profileSelected: IRadarrProfile, event: any) {
event.preventDefault(); // event.preventDefault();
searchResult.qualityOverride = profileSelected.id; // searchResult.qualityOverride = profileSelected.id;
this.setOverride(searchResult); // this.setOverride(searchResult);
this.updateRequest(searchResult); // this.updateRequest(searchResult);
} // }
public reportIssue(catId: IIssueCategory, req: IMovieRequests) { // public reportIssue(catId: IIssueCategory, req: IMovieRequests) {
this.issueRequest = req; // this.issueRequest = req;
this.issueCategorySelected = catId; // this.issueCategorySelected = catId;
this.issuesBarVisible = true; // this.issuesBarVisible = true;
this.issueProviderId = req.theMovieDbId.toString(); // this.issueProviderId = req.theMovieDbId.toString();
} // }
public ignore(event: any): void { // public ignore(event: any): void {
event.preventDefault(); // event.preventDefault();
} // }
public clearFilter(el: any) { // public clearFilter(el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement; // el = el.toElement || el.relatedTarget || el.target || el.srcElement;
el = el.parentElement; // el = el.parentElement;
el = el.querySelectorAll("INPUT"); // el = el.querySelectorAll("INPUT");
for (el of el) { // for (el of el) {
el.checked = false; // el.checked = false;
el.parentElement.classList.remove("active"); // el.parentElement.classList.remove("active");
} // }
this.filterDisplay = false; // this.filterDisplay = false;
this.filter.availabilityFilter = FilterType.None; // this.filter.availabilityFilter = FilterType.None;
this.filter.statusFilter = FilterType.None; // this.filter.statusFilter = FilterType.None;
this.resetSearch(); // this.resetSearch();
} // }
public filterAvailability(filter: FilterType, el: any) { // public filterAvailability(filter: FilterType, el: any) {
this.filterActiveStyle(el); // this.filterActiveStyle(el);
this.filter.availabilityFilter = filter; // this.filter.availabilityFilter = filter;
this.loadInit(); // this.loadInit();
} // }
public filterStatus(filter: FilterType, el: any) { // public filterStatus(filter: FilterType, el: any) {
this.filterActiveStyle(el); // this.filterActiveStyle(el);
this.filter.statusFilter = filter; // this.filter.statusFilter = filter;
this.loadInit(); // this.loadInit();
} // }
public setOrder(value: OrderType, el: any) { // public setOrder(value: OrderType, el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement; // el = el.toElement || el.relatedTarget || el.target || el.srcElement;
const parent = el.parentElement; // const parent = el.parentElement;
const previousFilter = parent.querySelector(".active"); // const previousFilter = parent.querySelector(".active");
previousFilter.className = ""; // previousFilter.className = "";
el.className = "active"; // el.className = "active";
this.orderType = value; // this.orderType = value;
this.loadInit(); // this.loadInit();
} // }
public subscribe(request: IMovieRequests) { // public subscribe(request: IMovieRequests) {
request.subscribed = true; // request.subscribed = true;
this.requestService.subscribeToMovie(request.id) // this.requestService.subscribeToMovie(request.id)
.subscribe(x => { // .subscribe(x => {
this.notificationService.success("Subscribed To Movie!"); // this.notificationService.success("Subscribed To Movie!");
}); // });
} // }
public unSubscribe(request: IMovieRequests) { // public unSubscribe(request: IMovieRequests) {
request.subscribed = false; // request.subscribed = false;
this.requestService.unSubscribeToMovie(request.id) // this.requestService.unSubscribeToMovie(request.id)
.subscribe(x => { // .subscribe(x => {
this.notificationService.success("Unsubscribed Movie!"); // this.notificationService.success("Unsubscribed Movie!");
}); // });
} // }
public isRequestUser(request: IMovieRequests) { // public isRequestUser(request: IMovieRequests) {
if (request.requestedUser.userName === this.auth.claims().name) { // if (request.requestedUser.userName === this.auth.claims().name) {
return true; // return true;
} // }
return false; // return false;
} // }
private filterActiveStyle(el: any) { // private filterActiveStyle(el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement; // el = el.toElement || el.relatedTarget || el.target || el.srcElement;
el = el.parentElement; //gets radio div // el = el.parentElement; //gets radio div
el = el.parentElement; //gets form group div // el = el.parentElement; //gets form group div
el = el.parentElement; //gets status filter div // el = el.parentElement; //gets status filter div
el = el.querySelectorAll("INPUT"); // el = el.querySelectorAll("INPUT");
for (el of el) { // for (el of el) {
if (el.checked) { // if (el.checked) {
if (!el.parentElement.classList.contains("active")) { // if (!el.parentElement.classList.contains("active")) {
el.parentElement.className += " active"; // el.parentElement.className += " active";
} // }
} else { // } else {
el.parentElement.classList.remove("active"); // el.parentElement.classList.remove("active");
} // }
} // }
} // }
private loadRequests(amountToLoad: number, currentlyLoaded: number) { // private loadRequests(amountToLoad: number, currentlyLoaded: number) {
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter) // this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter)
.subscribe(x => { // .subscribe(x => {
this.setOverrides(x.collection); // this.setOverrides(x.collection);
if (!this.movieRequests) { // if (!this.movieRequests) {
this.movieRequests = []; // this.movieRequests = [];
} // }
this.movieRequests = x.collection; // this.movieRequests = x.collection;
this.totalMovies = x.total; // this.totalMovies = x.total;
this.currentlyLoaded = currentlyLoaded + amountToLoad; // this.currentlyLoaded = currentlyLoaded + amountToLoad;
}); // });
} // }
private updateRequest(request: IMovieRequests) { // private updateRequest(request: IMovieRequests) {
this.requestService.updateMovieRequest(request) // this.requestService.updateMovieRequest(request)
.subscribe(x => { // .subscribe(x => {
this.setOverride(x); // this.setOverride(x);
request = x; // request = x;
}); // });
} // }
private approveRequest(request: IMovieRequests) { // private approveRequest(request: IMovieRequests) {
this.requestService.approveMovie({ id: request.id }) // this.requestService.approveMovie({ id: request.id })
.subscribe(x => { // .subscribe(x => {
request.approved = true; // request.approved = true;
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`Request for ${request.title} has been approved successfully`); // `Request for ${request.title} has been approved successfully`);
} else { // } else {
this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} // }
private loadInit() { // private loadInit() {
this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter) // this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter)
.subscribe(x => { // .subscribe(x => {
this.movieRequests = x.collection; // this.movieRequests = x.collection;
this.totalMovies = x.total; // this.totalMovies = x.total;
this.movieRequests.forEach((req) => { // this.movieRequests.forEach((req) => {
this.setBackground(req); // this.setBackground(req);
this.setPoster(req); // this.setPoster(req);
}); // });
if (this.isAdmin) { // if (this.isAdmin) {
this.radarrService.getQualityProfilesFromSettings().subscribe(c => { // this.radarrService.getQualityProfilesFromSettings().subscribe(c => {
this.radarrProfiles = c; // this.radarrProfiles = c;
this.movieRequests.forEach((req) => this.setQualityOverrides(req)); // this.movieRequests.forEach((req) => this.setQualityOverrides(req));
}); // });
this.radarrService.getRootFoldersFromSettings().subscribe(c => { // this.radarrService.getRootFoldersFromSettings().subscribe(c => {
this.radarrRootFolders = c; // this.radarrRootFolders = c;
this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); // this.movieRequests.forEach((req) => this.setRootFolderOverrides(req));
}); // });
} // }
}); // });
} // }
private resetSearch() { // private resetSearch() {
this.currentlyLoaded = 5; // this.currentlyLoaded = 5;
this.loadInit(); // this.loadInit();
} // }
private removeRequestFromUi(key: IMovieRequests) { // private removeRequestFromUi(key: IMovieRequests) {
const index = this.movieRequests.indexOf(key, 0); // const index = this.movieRequests.indexOf(key, 0);
if (index > -1) { // if (index > -1) {
this.movieRequests.splice(index, 1); // this.movieRequests.splice(index, 1);
} // }
} // }
private setOverrides(requests: IMovieRequests[]): void { // private setOverrides(requests: IMovieRequests[]): void {
requests.forEach((req) => { // requests.forEach((req) => {
this.setOverride(req); // this.setOverride(req);
}); // });
} // }
private setQualityOverrides(req: IMovieRequests): void { // private setQualityOverrides(req: IMovieRequests): void {
if (this.radarrProfiles) { // if (this.radarrProfiles) {
const profile = this.radarrProfiles.filter((p) => { // const profile = this.radarrProfiles.filter((p) => {
return p.id === req.qualityOverride; // return p.id === req.qualityOverride;
}); // });
if (profile.length > 0) { // if (profile.length > 0) {
req.qualityOverrideTitle = profile[0].name; // req.qualityOverrideTitle = profile[0].name;
} // }
} // }
} // }
private setRootFolderOverrides(req: IMovieRequests): void { // private setRootFolderOverrides(req: IMovieRequests): void {
if (this.radarrRootFolders) { // if (this.radarrRootFolders) {
const path = this.radarrRootFolders.filter((folder) => { // const path = this.radarrRootFolders.filter((folder) => {
return folder.id === req.rootPathOverride; // return folder.id === req.rootPathOverride;
}); // });
if (path.length > 0) { // if (path.length > 0) {
req.rootPathOverrideTitle = path[0].path; // req.rootPathOverrideTitle = path[0].path;
} // }
} // }
} // }
private setOverride(req: IMovieRequests): void { // private setOverride(req: IMovieRequests): void {
this.setPoster(req); // this.setPoster(req);
this.setBackground(req); // this.setBackground(req);
this.setQualityOverrides(req); // this.setQualityOverrides(req);
this.setRootFolderOverrides(req); // this.setRootFolderOverrides(req);
} // }
private setPoster(req: IMovieRequests): void { // private setPoster(req: IMovieRequests): void {
if (req.posterPath === null) { // if (req.posterPath === null) {
req.posterPath = this.defaultPoster; // req.posterPath = this.defaultPoster;
} else { // } else {
req.posterPath = "https://image.tmdb.org/t/p/w300/" + req.posterPath; // req.posterPath = "https://image.tmdb.org/t/p/w300/" + req.posterPath;
} // }
} // }
private setBackground(req: IMovieRequests): void { // private setBackground(req: IMovieRequests): void {
req.backgroundPath = this.sanitizer.bypassSecurityTrustStyle // req.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")"); // ("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")");
} // }
} // }

@ -1,349 +1,349 @@
import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; // import { PlatformLocation, APP_BASE_HREF } from "@angular/common";
import { Component, Input, OnInit, Inject } from "@angular/core"; // import { Component, Input, OnInit, Inject } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser"; // import { DomSanitizer } from "@angular/platform-browser";
import { Subject } from "rxjs"; // import { Subject } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators"; // import { debounceTime, distinctUntilChanged } from "rxjs/operators";
import { AuthService } from "../../auth/auth.service"; // import { AuthService } from "../../auth/auth.service";
import { FilterType, IAlbumRequest, IFilter, IIssueCategory, IPagenator, OrderType } from "../../interfaces"; // import { FilterType, IAlbumRequest, IFilter, IIssueCategory, IPagenator, OrderType } from "../../interfaces";
import { NotificationService, RequestService } from "../../services"; // import { NotificationService, RequestService } from "../../services";
@Component({ // @Component({
selector: "music-requests", // selector: "music-requests",
templateUrl: "./musicrequests.component.html", // templateUrl: "./musicrequests.component.html",
}) // })
export class MusicRequestsComponent implements OnInit { // export class MusicRequestsComponent implements OnInit {
public albumRequests: IAlbumRequest[]; // public albumRequests: IAlbumRequest[];
public defaultPoster: string; // public defaultPoster: string;
public searchChanged: Subject<string> = new Subject<string>(); // public searchChanged: Subject<string> = new Subject<string>();
public searchText: string; // public searchText: string;
public isAdmin: boolean; // Also PowerUser // public isAdmin: boolean; // Also PowerUser
@Input() public issueCategories: IIssueCategory[]; // @Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean; // @Input() public issuesEnabled: boolean;
public issuesBarVisible = false; // public issuesBarVisible = false;
public issueRequest: IAlbumRequest; // public issueRequest: IAlbumRequest;
public issueProviderId: string; // public issueProviderId: string;
public issueCategorySelected: IIssueCategory; // public issueCategorySelected: IIssueCategory;
public filterDisplay: boolean; // public filterDisplay: boolean;
public filter: IFilter; // public filter: IFilter;
public filterType = FilterType; // public filterType = FilterType;
public orderType: OrderType = OrderType.RequestedDateDesc; // public orderType: OrderType = OrderType.RequestedDateDesc;
public OrderType = OrderType; // public OrderType = OrderType;
public denyDisplay: boolean; // public denyDisplay: boolean;
public requestToDeny: IAlbumRequest; // public requestToDeny: IAlbumRequest;
public rejectionReason: string; // public rejectionReason: string;
public totalAlbums: number = 100; // public totalAlbums: number = 100;
public currentlyLoaded: number; // public currentlyLoaded: number;
private amountToLoad: number; // private amountToLoad: number;
private href: string; // private href: string;
constructor( // constructor(
private requestService: RequestService, // private requestService: RequestService,
private auth: AuthService, // private auth: AuthService,
private notificationService: NotificationService, // private notificationService: NotificationService,
private sanitizer: DomSanitizer, // private sanitizer: DomSanitizer,
@Inject(APP_BASE_HREF) href:string) { // @Inject(APP_BASE_HREF) href:string) {
this.href = href; // this.href = href;
this.searchChanged.pipe( // this.searchChanged.pipe(
debounceTime(600), // Wait Xms after the last event before emitting last event // debounceTime(600), // Wait Xms after the last event before emitting last event
distinctUntilChanged(), // only emit if value is different from previous value // distinctUntilChanged(), // only emit if value is different from previous value
).subscribe(x => { // ).subscribe(x => {
this.searchText = x as string; // this.searchText = x as string;
if (this.searchText === "") { // if (this.searchText === "") {
this.resetSearch(); // this.resetSearch();
return; // return;
} // }
this.requestService.searchAlbumRequests(this.searchText) // this.requestService.searchAlbumRequests(this.searchText)
.subscribe(m => { // .subscribe(m => {
this.setOverrides(m); // this.setOverrides(m);
this.albumRequests = m; // this.albumRequests = m;
}); // });
}); // });
this.defaultPoster = "../../../images/default-music-placeholder.png"; // this.defaultPoster = "../../../images/default-music-placeholder.png";
const base = this.href; // const base = this.href;
if (base) { // if (base) {
this.defaultPoster = "../../.." + base + "/images/default-music-placeholder.png"; // this.defaultPoster = "../../.." + base + "/images/default-music-placeholder.png";
} // }
} // }
public ngOnInit() { // public ngOnInit() {
this.amountToLoad = 10; // this.amountToLoad = 10;
this.currentlyLoaded = 10; // this.currentlyLoaded = 10;
this.filter = { // this.filter = {
availabilityFilter: FilterType.None, // availabilityFilter: FilterType.None,
statusFilter: FilterType.None, // statusFilter: FilterType.None,
}; // };
this.loadInit(); // this.loadInit();
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); // this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
} // }
public paginate(event: IPagenator) { // public paginate(event: IPagenator) {
const skipAmount = event.first; // const skipAmount = event.first;
this.loadRequests(this.amountToLoad, skipAmount); // this.loadRequests(this.amountToLoad, skipAmount);
} // }
public search(text: any) { // public search(text: any) {
this.searchChanged.next(text.target.value); // this.searchChanged.next(text.target.value);
} // }
public async removeRequest(request: IAlbumRequest) { // public async removeRequest(request: IAlbumRequest) {
await this.requestService.removeAlbumRequest(request).toPromise(); // await this.requestService.removeAlbumRequest(request).toPromise();
this.removeRequestFromUi(request); // this.removeRequestFromUi(request);
this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); // this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0);
} // }
public changeAvailability(request: IAlbumRequest, available: boolean) { // public changeAvailability(request: IAlbumRequest, available: boolean) {
request.available = available; // request.available = available;
if (available) { // if (available) {
this.requestService.markAlbumAvailable({ id: request.id }).subscribe(x => { // this.requestService.markAlbumAvailable({ id: request.id }).subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`${request.title} Is now available`); // `${request.title} Is now available`);
} else { // } else {
this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} else { // } else {
this.requestService.markAlbumUnavailable({ id: request.id }).subscribe(x => { // this.requestService.markAlbumUnavailable({ id: request.id }).subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`${request.title} Is now unavailable`); // `${request.title} Is now unavailable`);
} else { // } else {
this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} // }
} // }
public approve(request: IAlbumRequest) { // public approve(request: IAlbumRequest) {
request.approved = true; // request.approved = true;
this.approveRequest(request); // this.approveRequest(request);
} // }
public deny(request: IAlbumRequest) { // public deny(request: IAlbumRequest) {
this.requestToDeny = request; // this.requestToDeny = request;
this.denyDisplay = true; // this.denyDisplay = true;
} // }
public denyRequest() { // public denyRequest() {
this.requestService.denyAlbum({ id: this.requestToDeny.id, reason: this.rejectionReason }) // this.requestService.denyAlbum({ id: this.requestToDeny.id, reason: this.rejectionReason })
.subscribe(x => { // .subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`Request for ${this.requestToDeny.title} has been denied successfully`); // `Request for ${this.requestToDeny.title} has been denied successfully`);
} else { // } else {
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
this.requestToDeny.denied = false; // this.requestToDeny.denied = false;
} // }
}); // });
} // }
public reportIssue(catId: IIssueCategory, req: IAlbumRequest) { // public reportIssue(catId: IIssueCategory, req: IAlbumRequest) {
this.issueRequest = req; // this.issueRequest = req;
this.issueCategorySelected = catId; // this.issueCategorySelected = catId;
this.issuesBarVisible = true; // this.issuesBarVisible = true;
this.issueProviderId = req.foreignAlbumId; // this.issueProviderId = req.foreignAlbumId;
} // }
public ignore(event: any): void { // public ignore(event: any): void {
event.preventDefault(); // event.preventDefault();
} // }
public clearFilter(el: any) { // public clearFilter(el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement; // el = el.toElement || el.relatedTarget || el.target || el.srcElement;
el = el.parentElement; // el = el.parentElement;
el = el.querySelectorAll("INPUT"); // el = el.querySelectorAll("INPUT");
for (el of el) { // for (el of el) {
el.checked = false; // el.checked = false;
el.parentElement.classList.remove("active"); // el.parentElement.classList.remove("active");
} // }
this.filterDisplay = false; // this.filterDisplay = false;
this.filter.availabilityFilter = FilterType.None; // this.filter.availabilityFilter = FilterType.None;
this.filter.statusFilter = FilterType.None; // this.filter.statusFilter = FilterType.None;
this.resetSearch(); // this.resetSearch();
} // }
public filterAvailability(filter: FilterType, el: any) { // public filterAvailability(filter: FilterType, el: any) {
this.filterActiveStyle(el); // this.filterActiveStyle(el);
this.filter.availabilityFilter = filter; // this.filter.availabilityFilter = filter;
this.loadInit(); // this.loadInit();
} // }
public filterStatus(filter: FilterType, el: any) { // public filterStatus(filter: FilterType, el: any) {
this.filterActiveStyle(el); // this.filterActiveStyle(el);
this.filter.statusFilter = filter; // this.filter.statusFilter = filter;
this.loadInit(); // this.loadInit();
} // }
public setOrder(value: OrderType, el: any) { // public setOrder(value: OrderType, el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement; // el = el.toElement || el.relatedTarget || el.target || el.srcElement;
const parent = el.parentElement; // const parent = el.parentElement;
const previousFilter = parent.querySelector(".active"); // const previousFilter = parent.querySelector(".active");
previousFilter.className = ""; // previousFilter.className = "";
el.className = "active"; // el.className = "active";
this.orderType = value; // this.orderType = value;
this.loadInit(); // this.loadInit();
} // }
public isRequestUser(request: IAlbumRequest) { // public isRequestUser(request: IAlbumRequest) {
if (request.requestedUser.userName === this.auth.claims().name) { // if (request.requestedUser.userName === this.auth.claims().name) {
return true; // return true;
} // }
return false; // return false;
} // }
// public subscribe(request: IAlbumRequest) { // // public subscribe(request: IAlbumRequest) {
// request.subscribed = true; // // request.subscribed = true;
// this.requestService.subscribeToMovie(request.id) // // this.requestService.subscribeToMovie(request.id)
// .subscribe(x => { // // .subscribe(x => {
// this.notificationService.success("Subscribed To Movie!"); // // this.notificationService.success("Subscribed To Movie!");
// }); // // });
// } // // }
// public unSubscribe(request: IMovieRequests) { // // public unSubscribe(request: IMovieRequests) {
// request.subscribed = false; // // request.subscribed = false;
// this.requestService.unSubscribeToMovie(request.id) // // this.requestService.unSubscribeToMovie(request.id)
// .subscribe(x => { // // .subscribe(x => {
// this.notificationService.success("Unsubscribed Movie!"); // // this.notificationService.success("Unsubscribed Movie!");
// }); // // });
// } // // }
private filterActiveStyle(el: any) { // private filterActiveStyle(el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement; // el = el.toElement || el.relatedTarget || el.target || el.srcElement;
el = el.parentElement; //gets radio div // el = el.parentElement; //gets radio div
el = el.parentElement; //gets form group div // el = el.parentElement; //gets form group div
el = el.parentElement; //gets status filter div // el = el.parentElement; //gets status filter div
el = el.querySelectorAll("INPUT"); // el = el.querySelectorAll("INPUT");
for (el of el) { // for (el of el) {
if (el.checked) { // if (el.checked) {
if (!el.parentElement.classList.contains("active")) { // if (!el.parentElement.classList.contains("active")) {
el.parentElement.className += " active"; // el.parentElement.className += " active";
} // }
} else { // } else {
el.parentElement.classList.remove("active"); // el.parentElement.classList.remove("active");
} // }
} // }
} // }
private loadRequests(amountToLoad: number, currentlyLoaded: number) { // private loadRequests(amountToLoad: number, currentlyLoaded: number) {
this.requestService.getAlbumRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter) // this.requestService.getAlbumRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter)
.subscribe(x => { // .subscribe(x => {
this.setOverrides(x.collection); // this.setOverrides(x.collection);
if (!this.albumRequests) { // if (!this.albumRequests) {
this.albumRequests = []; // this.albumRequests = [];
} // }
this.albumRequests = x.collection; // this.albumRequests = x.collection;
this.totalAlbums = x.total; // this.totalAlbums = x.total;
this.currentlyLoaded = currentlyLoaded + amountToLoad; // this.currentlyLoaded = currentlyLoaded + amountToLoad;
}); // });
} // }
private approveRequest(request: IAlbumRequest) { // private approveRequest(request: IAlbumRequest) {
this.requestService.approveAlbum({ id: request.id }) // this.requestService.approveAlbum({ id: request.id })
.subscribe(x => { // .subscribe(x => {
request.approved = true; // request.approved = true;
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`Request for ${request.title} has been approved successfully`); // `Request for ${request.title} has been approved successfully`);
} else { // } else {
this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} // }
private loadInit() { // private loadInit() {
this.requestService.getAlbumRequests(this.amountToLoad, 0, this.orderType, this.filter) // this.requestService.getAlbumRequests(this.amountToLoad, 0, this.orderType, this.filter)
.subscribe(x => { // .subscribe(x => {
this.albumRequests = x.collection; // this.albumRequests = x.collection;
this.totalAlbums = x.total; // this.totalAlbums = x.total;
this.setOverrides(this.albumRequests); // this.setOverrides(this.albumRequests);
if (this.isAdmin) { // if (this.isAdmin) {
// this.radarrService.getQualityProfilesFromSettings().subscribe(c => { // // this.radarrService.getQualityProfilesFromSettings().subscribe(c => {
// this.radarrProfiles = c; // // this.radarrProfiles = c;
// this.albumRequests.forEach((req) => this.setQualityOverrides(req)); // // this.albumRequests.forEach((req) => this.setQualityOverrides(req));
// }); // // });
// this.radarrService.getRootFoldersFromSettings().subscribe(c => { // // this.radarrService.getRootFoldersFromSettings().subscribe(c => {
// this.radarrRootFolders = c; // // this.radarrRootFolders = c;
// this.albumRequests.forEach((req) => this.setRootFolderOverrides(req)); // // this.albumRequests.forEach((req) => this.setRootFolderOverrides(req));
// }); // // });
} // }
}); // });
} // }
private resetSearch() { // private resetSearch() {
this.currentlyLoaded = 5; // this.currentlyLoaded = 5;
this.loadInit(); // this.loadInit();
} // }
private removeRequestFromUi(key: IAlbumRequest) { // private removeRequestFromUi(key: IAlbumRequest) {
const index = this.albumRequests.indexOf(key, 0); // const index = this.albumRequests.indexOf(key, 0);
if (index > -1) { // if (index > -1) {
this.albumRequests.splice(index, 1); // this.albumRequests.splice(index, 1);
} // }
} // }
private setOverrides(requests: IAlbumRequest[]): void { // private setOverrides(requests: IAlbumRequest[]): void {
requests.forEach((req) => { // requests.forEach((req) => {
this.setOverride(req); // this.setOverride(req);
}); // });
} // }
// private setQualityOverrides(req: IMovieRequests): void { // // private setQualityOverrides(req: IMovieRequests): void {
// if (this.radarrProfiles) { // // if (this.radarrProfiles) {
// const profile = this.radarrProfiles.filter((p) => { // // const profile = this.radarrProfiles.filter((p) => {
// return p.id === req.qualityOverride; // // return p.id === req.qualityOverride;
// }); // // });
// if (profile.length > 0) { // // if (profile.length > 0) {
// req.qualityOverrideTitle = profile[0].name; // // req.qualityOverrideTitle = profile[0].name;
// } // // }
// } // // }
// } // // }
// private setRootFolderOverrides(req: IMovieRequests): void { // // private setRootFolderOverrides(req: IMovieRequests): void {
// if (this.radarrRootFolders) { // // if (this.radarrRootFolders) {
// const path = this.radarrRootFolders.filter((folder) => { // // const path = this.radarrRootFolders.filter((folder) => {
// return folder.id === req.rootPathOverride; // // return folder.id === req.rootPathOverride;
// }); // // });
// if (path.length > 0) { // // if (path.length > 0) {
// req.rootPathOverrideTitle = path[0].path; // // req.rootPathOverrideTitle = path[0].path;
// } // // }
// } // // }
// } // // }
private setOverride(req: IAlbumRequest): void { // private setOverride(req: IAlbumRequest): void {
this.setAlbumBackground(req); // this.setAlbumBackground(req);
// this.setQualityOverrides(req); // // this.setQualityOverrides(req);
// this.setRootFolderOverrides(req); // // this.setRootFolderOverrides(req);
} // }
private setAlbumBackground(req: IAlbumRequest) { // private setAlbumBackground(req: IAlbumRequest) {
if (req.disk === null) { // if (req.disk === null) {
if (req.cover === null) { // if (req.cover === null) {
req.disk = this.defaultPoster; // req.disk = this.defaultPoster;
} else { // } else {
req.disk = req.cover; // req.disk = req.cover;
} // }
} // }
req.background = this.sanitizer.bypassSecurityTrustStyle // req.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + req.cover + ")"); // ("url(" + req.cover + ")");
} // }
} // }

@ -1,68 +1,68 @@
import { IRemainingRequests } from "../interfaces/IRemainingRequests"; // import { IRemainingRequests } from "../interfaces/IRemainingRequests";
import { RequestService } from "../services"; // import { RequestService } from "../services";
import { Component, Input, OnInit } from "@angular/core"; // import { Component, Input, OnInit } from "@angular/core";
import { Observable } from "rxjs"; // import { Observable } from "rxjs";
@Component({ // @Component({
selector: "remaining-requests", // selector: "remaining-requests",
templateUrl: "./remainingrequests.component.html", // templateUrl: "./remainingrequests.component.html",
}) // })
export class RemainingRequestsComponent implements OnInit { // export class RemainingRequestsComponent implements OnInit {
public remaining: IRemainingRequests; // public remaining: IRemainingRequests;
@Input() public movie: boolean; // @Input() public movie: boolean;
@Input() public tv: boolean; // @Input() public tv: boolean;
@Input() public music: boolean; // @Input() public music: boolean;
public daysUntil: number; // public daysUntil: number;
public hoursUntil: number; // public hoursUntil: number;
public minutesUntil: number; // public minutesUntil: number;
@Input() public quotaRefreshEvents: Observable<void>; // @Input() public quotaRefreshEvents: Observable<void>;
constructor(private requestService: RequestService) { // constructor(private requestService: RequestService) {
} // }
public ngOnInit() { // public ngOnInit() {
this.update(); // this.update();
this.quotaRefreshEvents.subscribe(() => { // this.quotaRefreshEvents.subscribe(() => {
this.update(); // this.update();
}); // });
} // }
public update(): void { // public update(): void {
const callback = (remaining => { // const callback = (remaining => {
this.remaining = remaining; // this.remaining = remaining;
if(this.remaining) { // if(this.remaining) {
this.calculateTime(); // this.calculateTime();
} // }
}); // });
if (this.movie) { // if (this.movie) {
this.requestService.getRemainingMovieRequests().subscribe(callback); // this.requestService.getRemainingMovieRequests().subscribe(callback);
} // }
if(this.tv) { // if(this.tv) {
this.requestService.getRemainingTvRequests().subscribe(callback); // this.requestService.getRemainingTvRequests().subscribe(callback);
} // }
if(this.music) { // if(this.music) {
this.requestService.getRemainingMusicRequests().subscribe(callback); // this.requestService.getRemainingMusicRequests().subscribe(callback);
} // }
} // }
private calculateTime(): void { // private calculateTime(): void {
this.daysUntil = Math.ceil(this.daysUntilNextRequest()); // this.daysUntil = Math.ceil(this.daysUntilNextRequest());
this.hoursUntil = Math.ceil(this.hoursUntilNextRequest()); // this.hoursUntil = Math.ceil(this.hoursUntilNextRequest());
this.minutesUntil = Math.ceil(this.minutesUntilNextRequest()); // this.minutesUntil = Math.ceil(this.minutesUntilNextRequest());
} // }
private daysUntilNextRequest(): number { // private daysUntilNextRequest(): number {
return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60 / 24; // return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60 / 24;
} // }
private hoursUntilNextRequest(): number { // private hoursUntilNextRequest(): number {
return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60; // return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60;
} // }
private minutesUntilNextRequest(): number { // private minutesUntilNextRequest(): number {
return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60; // return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60;
} // }
} // }

@ -2,18 +2,11 @@
import { FormsModule } from "@angular/forms"; import { FormsModule } from "@angular/forms";
import { RouterModule } from "@angular/router"; import { RouterModule } from "@angular/router";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { SidebarModule, TooltipModule, TreeTableModule } from "primeng/primeng";
import { RequestService } from "../services"; import { RequestService } from "../services";
@NgModule({ @NgModule({
imports: [ imports: [
FormsModule, FormsModule
NgbModule.forRoot(),
TreeTableModule,
SidebarModule,
TooltipModule,
], ],
declarations: [ declarations: [
], ],

@ -1,48 +1,48 @@
 
import { Component, OnInit } from "@angular/core"; // import { Component, OnInit } from "@angular/core";
import { IIssueCategory } from "../interfaces"; // import { IIssueCategory } from "../interfaces";
import { IssuesService, SettingsService } from "../services"; // import { IssuesService, SettingsService } from "../services";
@Component({ // @Component({
templateUrl: "./request.component.html", // templateUrl: "./request.component.html",
}) // })
export class RequestComponent implements OnInit { // export class RequestComponent implements OnInit {
public showMovie = true; // public showMovie = true;
public showTv = false; // public showTv = false;
public showAlbums = false; // public showAlbums = false;
public issueCategories: IIssueCategory[]; // public issueCategories: IIssueCategory[];
public issuesEnabled = false; // public issuesEnabled = false;
public musicEnabled: boolean; // public musicEnabled: boolean;
constructor(private issuesService: IssuesService, // constructor(private issuesService: IssuesService,
private settingsService: SettingsService) { // private settingsService: SettingsService) {
} // }
public ngOnInit(): void { // public ngOnInit(): void {
this.issuesService.getCategories().subscribe(x => this.issueCategories = x); // this.issuesService.getCategories().subscribe(x => this.issueCategories = x);
this.settingsService.lidarrEnabled().subscribe(x => this.musicEnabled = x); // this.settingsService.lidarrEnabled().subscribe(x => this.musicEnabled = x);
this.settingsService.getIssueSettings().subscribe(x => this.issuesEnabled = x.enabled); // this.settingsService.getIssueSettings().subscribe(x => this.issuesEnabled = x.enabled);
} // }
public selectMovieTab() { // public selectMovieTab() {
this.showMovie = true; // this.showMovie = true;
this.showTv = false; // this.showTv = false;
this.showAlbums = false; // this.showAlbums = false;
} // }
public selectTvTab() { // public selectTvTab() {
this.showMovie = false; // this.showMovie = false;
this.showTv = true; // this.showTv = true;
this.showAlbums = false; // this.showAlbums = false;
} // }
public selectMusicTab() { // public selectMusicTab() {
this.showMovie = false; // this.showMovie = false;
this.showTv = false; // this.showTv = false;
this.showAlbums = true; // this.showAlbums = true;
} // }
} // }

@ -1,60 +1,49 @@
import { NgModule } from "@angular/core"; // import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router"; // import { RouterModule, Routes } from "@angular/router";
// import { OrderModule } from "ngx-order-pipe";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { OrderModule } from "ngx-order-pipe"; // import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { InfiniteScrollModule } from "ngx-infinite-scroll"; // import { MovieRequestsComponent } from "./movierequests.component";
// import { MusicRequestsComponent } from "./music/musicrequests.component";
import { ButtonModule, DialogModule, PaginatorModule } from "primeng/primeng"; // // Request
import { MovieRequestsComponent } from "./movierequests.component"; // import { RequestComponent } from "./request.component";
import { MusicRequestsComponent } from "./music/musicrequests.component"; // import { TvRequestChildrenComponent } from "./tvrequest-children.component";
// Request // import { TvRequestsComponent } from "./tvrequests.component";
import { RequestComponent } from "./request.component";
import { TvRequestChildrenComponent } from "./tvrequest-children.component";
import { TvRequestsComponent } from "./tvrequests.component"; // import { IdentityService, RadarrService, RequestService, SonarrService } from "../services";
import { SidebarModule, TooltipModule, TreeTableModule } from "primeng/primeng"; // import { AuthGuard } from "../auth/auth.guard";
import { IdentityService, RadarrService, RequestService, SonarrService } from "../services"; // import { SharedModule } from "../shared/shared.module";
import { AuthGuard } from "../auth/auth.guard"; // const routes: Routes = [
// { path: "", component: RequestComponent, canActivate: [AuthGuard] },
import { SharedModule } from "../shared/shared.module"; // ];
// @NgModule({
const routes: Routes = [ // imports: [
{ path: "", component: RequestComponent, canActivate: [AuthGuard] }, // RouterModule.forChild(routes),
]; // InfiniteScrollModule,
@NgModule({ // SharedModule,
imports: [ // OrderModule,
RouterModule.forChild(routes), // ],
NgbModule.forRoot(), // declarations: [
InfiniteScrollModule, // RequestComponent,
ButtonModule, // MovieRequestsComponent,
DialogModule, // TvRequestsComponent,
TreeTableModule, // TvRequestChildrenComponent,
SharedModule, // MusicRequestsComponent,
SidebarModule, // ],
OrderModule, // exports: [
PaginatorModule, // RouterModule,
TooltipModule, // ],
], // providers: [
declarations: [ // IdentityService,
RequestComponent, // RequestService,
MovieRequestsComponent, // RadarrService,
TvRequestsComponent, // SonarrService,
TvRequestChildrenComponent, // ],
MusicRequestsComponent,
], // })
exports: [ // export class RequestsModule { }
RouterModule,
],
providers: [
IdentityService,
RequestService,
RadarrService,
SonarrService,
],
})
export class RequestsModule { }

@ -1,138 +1,138 @@
import { Component, EventEmitter, Input, Output } from "@angular/core"; // import { Component, EventEmitter, Input, Output } from "@angular/core";
import { IChildRequests } from "../interfaces"; // import { IChildRequests } from "../interfaces";
import { NotificationService, RequestService } from "../services"; // import { NotificationService, RequestService } from "../services";
@Component({ // @Component({
selector: "tvrequests-children", // selector: "tvrequests-children",
templateUrl: "./tvrequest-children.component.html", // templateUrl: "./tvrequest-children.component.html",
}) // })
export class TvRequestChildrenComponent { // export class TvRequestChildrenComponent {
@Input() public childRequests: IChildRequests[]; // @Input() public childRequests: IChildRequests[];
@Input() public isAdmin: boolean; // @Input() public isAdmin: boolean;
@Input() public currentUser: string; // @Input() public currentUser: string;
public denyDisplay: boolean; // public denyDisplay: boolean;
public requestToDeny: IChildRequests; // public requestToDeny: IChildRequests;
public rejectionReason: string; // public rejectionReason: string;
@Output() public requestDeleted = new EventEmitter<number>(); // @Output() public requestDeleted = new EventEmitter<number>();
constructor(private requestService: RequestService, // constructor(private requestService: RequestService,
private notificationService: NotificationService) { } // private notificationService: NotificationService) { }
public removeRequest(request: IChildRequests) { // public removeRequest(request: IChildRequests) {
this.requestService.deleteChild(request.id) // this.requestService.deleteChild(request.id)
.subscribe(x => { // .subscribe(x => {
this.removeRequestFromUi(request); // this.removeRequestFromUi(request);
this.requestDeleted.emit(request.id); // this.requestDeleted.emit(request.id);
}); // });
} // }
public changeAvailability(request: IChildRequests, available: boolean) { // public changeAvailability(request: IChildRequests, available: boolean) {
request.available = available; // request.available = available;
request.seasonRequests.forEach((season) => { // request.seasonRequests.forEach((season) => {
season.episodes.forEach((ep) => { // season.episodes.forEach((ep) => {
ep.available = available; // ep.available = available;
}); // });
}); // });
if (available) { // if (available) {
this.requestService.markTvAvailable({ id: request.id }).subscribe(x => { // this.requestService.markTvAvailable({ id: request.id }).subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`This request is now available`); // `This request is now available`);
} else { // } else {
this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} else { // } else {
this.requestService.markTvUnavailable({ id: request.id }).subscribe(x => { // this.requestService.markTvUnavailable({ id: request.id }).subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`This request is now unavailable`); // `This request is now unavailable`);
} else { // } else {
this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Available", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} // }
} // }
public deny(request: IChildRequests) { // public deny(request: IChildRequests) {
request.denied = true; // request.denied = true;
this.requestToDeny = request; // this.requestToDeny = request;
this.denyDisplay = true; // this.denyDisplay = true;
request.seasonRequests.forEach((season) => { // request.seasonRequests.forEach((season) => {
season.episodes.forEach((ep) => { // season.episodes.forEach((ep) => {
ep.approved = false; // ep.approved = false;
}); // });
}); // });
} // }
public denyRequest() { // public denyRequest() {
this.requestService.denyChild({ id: this.requestToDeny.id, reason: this.rejectionReason }) // this.requestService.denyChild({ id: this.requestToDeny.id, reason: this.rejectionReason })
.subscribe(x => { // .subscribe(x => {
this.denyDisplay = false; // this.denyDisplay = false;
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`Request has been denied successfully`); // `Request has been denied successfully`);
} else { // } else {
this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage);
this.requestToDeny.approved = false; // this.requestToDeny.approved = false;
} // }
}); // });
} // }
public approve(request: IChildRequests) { // public approve(request: IChildRequests) {
request.approved = true; // request.approved = true;
request.denied = false; // request.denied = false;
request.seasonRequests.forEach((season) => { // request.seasonRequests.forEach((season) => {
season.episodes.forEach((ep) => { // season.episodes.forEach((ep) => {
ep.approved = true; // ep.approved = true;
}); // });
}); // });
this.requestService.approveChild({ id: request.id }) // this.requestService.approveChild({ id: request.id })
.subscribe(x => { // .subscribe(x => {
if (x.result) { // if (x.result) {
this.notificationService.success( // this.notificationService.success(
`Request has been approved successfully`); // `Request has been approved successfully`);
} else { // } else {
this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage); // this.notificationService.warning("Request Approved", x.message ? x.message : x.errorMessage);
request.approved = false; // request.approved = false;
} // }
}); // });
} // }
public subscribe(request: IChildRequests) { // public subscribe(request: IChildRequests) {
request.subscribed = true; // request.subscribed = true;
this.requestService.subscribeToTv(request.id) // this.requestService.subscribeToTv(request.id)
.subscribe(x => { // .subscribe(x => {
this.notificationService.success("Subscribed To TV Show!"); // this.notificationService.success("Subscribed To TV Show!");
}); // });
} // }
public unSubscribe(request: IChildRequests) { // public unSubscribe(request: IChildRequests) {
request.subscribed = false; // request.subscribed = false;
this.requestService.unSubscribeToTv(request.id) // this.requestService.unSubscribeToTv(request.id)
.subscribe(x => { // .subscribe(x => {
this.notificationService.success("Unsubscribed TV Show!"); // this.notificationService.success("Unsubscribed TV Show!");
}); // });
} // }
public isRequestUser(request: IChildRequests) { // public isRequestUser(request: IChildRequests) {
if (request.requestedUser.userName === this.currentUser) { // if (request.requestedUser.userName === this.currentUser) {
return true; // return true;
} // }
return false; // return false;
} // }
private removeRequestFromUi(key: IChildRequests) { // private removeRequestFromUi(key: IChildRequests) {
const index = this.childRequests.indexOf(key, 0); // const index = this.childRequests.indexOf(key, 0);
if (index > -1) { // if (index > -1) {
this.childRequests.splice(index, 1); // this.childRequests.splice(index, 1);
} // }
} // }
} // }

@ -83,8 +83,7 @@
</div> </div>
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn"> <div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" <button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
aria-expanded="true">
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
<span class="caret"></span> <span class="caret"></span>
</button> </button>
@ -110,5 +109,4 @@
<p-paginator [rows]="10" [totalRecords]="totalTv" (onPageChange)="paginate($event)"></p-paginator> <p-paginator [rows]="10" [totalRecords]="totalTv" (onPageChange)="paginate($event)"></p-paginator>
</div> </div>
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title" [issueCategory]="issueCategorySelected" <issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title" [issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>
[id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>

@ -1,226 +1,226 @@
import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; // import { PlatformLocation, APP_BASE_HREF } from "@angular/common";
import { Component, Input, OnInit, Inject } from "@angular/core"; // import { Component, Input, OnInit, Inject } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser"; // import { DomSanitizer } from "@angular/platform-browser";
import { Subject } from "rxjs"; // import { Subject } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators"; // import { debounceTime, distinctUntilChanged } from "rxjs/operators";
import { AuthService } from "../auth/auth.service"; // import { AuthService } from "../auth/auth.service";
import { FilterType, IIssueCategory, IPagenator, IRequestsViewModel, ISonarrProfile, ISonarrRootFolder, ITvRequests, OrderType } from "../interfaces"; // import { FilterType, IIssueCategory, IPagenator, IRequestsViewModel, ISonarrProfile, ISonarrRootFolder, ITvRequests, OrderType } from "../interfaces";
import { NotificationService, RequestService, SonarrService } from "../services"; // import { NotificationService, RequestService, SonarrService } from "../services";
import { ImageService } from "../services/image.service"; // import { ImageService } from "../services/image.service";
@Component({ // @Component({
selector: "tv-requests", // selector: "tv-requests",
templateUrl: "./tvrequests.component.html", // templateUrl: "./tvrequests.component.html",
styleUrls: ["./tvrequests.component.scss"], // styleUrls: ["./tvrequests.component.scss"],
}) // })
export class TvRequestsComponent implements OnInit { // export class TvRequestsComponent implements OnInit {
public tvRequests: IRequestsViewModel<ITvRequests>; // public tvRequests: IRequestsViewModel<ITvRequests>;
public searchChanged = new Subject<string>(); // public searchChanged = new Subject<string>();
public searchText: string; // public searchText: string;
public isAdmin: boolean; // public isAdmin: boolean;
public currentUser: string; // public currentUser: string;
public showChildDialogue = false; // This is for the child modal popup // public showChildDialogue = false; // This is for the child modal popup
public selectedSeason: ITvRequests; // public selectedSeason: ITvRequests;
public defaultPoster: string; // public defaultPoster: string;
@Input() public issueCategories: IIssueCategory[]; // @Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean; // @Input() public issuesEnabled: boolean;
public issueProviderId: string; // public issueProviderId: string;
public issuesBarVisible = false; // public issuesBarVisible = false;
public issueRequest: ITvRequests; // public issueRequest: ITvRequests;
public issueCategorySelected: IIssueCategory; // public issueCategorySelected: IIssueCategory;
public sonarrProfiles: ISonarrProfile[] = []; // public sonarrProfiles: ISonarrProfile[] = [];
public sonarrRootFolders: ISonarrRootFolder[] = []; // public sonarrRootFolders: ISonarrRootFolder[] = [];
public totalTv: number = 100; // public totalTv: number = 100;
private currentlyLoaded: number; // private currentlyLoaded: number;
private amountToLoad: number; // private amountToLoad: number;
private href: string; // private href: string;
constructor( // constructor(
private requestService: RequestService, // private requestService: RequestService,
private auth: AuthService, // private auth: AuthService,
private sanitizer: DomSanitizer, // private sanitizer: DomSanitizer,
private imageService: ImageService, // private imageService: ImageService,
private sonarrService: SonarrService, // private sonarrService: SonarrService,
private notificationService: NotificationService, // private notificationService: NotificationService,
@Inject(APP_BASE_HREF) href:string) { // @Inject(APP_BASE_HREF) href:string) {
this.href= href; // this.href= href;
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); // this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.currentUser = this.auth.claims().name; // this.currentUser = this.auth.claims().name;
if (this.isAdmin) { // if (this.isAdmin) {
this.sonarrService.getQualityProfilesWithoutSettings() // this.sonarrService.getQualityProfilesWithoutSettings()
.subscribe(x => this.sonarrProfiles = x); // .subscribe(x => this.sonarrProfiles = x);
this.sonarrService.getRootFoldersWithoutSettings() // this.sonarrService.getRootFoldersWithoutSettings()
.subscribe(x => this.sonarrRootFolders = x); // .subscribe(x => this.sonarrRootFolders = x);
} // }
} // }
public openClosestTab(node: ITvRequests,el: any) { // public openClosestTab(node: ITvRequests,el: any) {
el.preventDefault(); // el.preventDefault();
node.open = !node.open; // node.open = !node.open;
} // }
public ngOnInit() { // public ngOnInit() {
this.amountToLoad = 10; // this.amountToLoad = 10;
this.currentlyLoaded = 10; // this.currentlyLoaded = 10;
this.tvRequests = {collection:[], total:0}; // this.tvRequests = {collection:[], total:0};
this.searchChanged.pipe( // this.searchChanged.pipe(
debounceTime(600), // Wait Xms after the last event before emitting last event // debounceTime(600), // Wait Xms after the last event before emitting last event
distinctUntilChanged(), // only emit if value is different from previous value // distinctUntilChanged(), // only emit if value is different from previous value
).subscribe(x => { // ).subscribe(x => {
this.searchText = x as string; // this.searchText = x as string;
if (this.searchText === "") { // if (this.searchText === "") {
this.resetSearch(); // this.resetSearch();
return; // return;
} // }
this.requestService.searchTvRequests(this.searchText) // this.requestService.searchTvRequests(this.searchText)
.subscribe(m => { // .subscribe(m => {
this.tvRequests.collection = m; // this.tvRequests.collection = m;
this.tvRequests.collection.forEach((val) => this.loadBackdrop(val)); // this.tvRequests.collection.forEach((val) => this.loadBackdrop(val));
this.tvRequests.collection.forEach((val) => this.setOverride(val)); // this.tvRequests.collection.forEach((val) => this.setOverride(val));
}); // });
}); // });
this.defaultPoster = "../../../images/default_tv_poster.png"; // this.defaultPoster = "../../../images/default_tv_poster.png";
const base = this.href; // const base = this.href;
if (base) { // if (base) {
this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png"; // this.defaultPoster = "../../.." + base + "/images/default_tv_poster.png";
} // }
this.loadInit(); // this.loadInit();
} // }
public paginate(event: IPagenator) { // public paginate(event: IPagenator) {
const skipAmount = event.first; // const skipAmount = event.first;
this.requestService.getTvRequests(this.amountToLoad, skipAmount, OrderType.RequestedDateDesc, FilterType.None, FilterType.None) // this.requestService.getTvRequests(this.amountToLoad, skipAmount, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
.subscribe(x => { // .subscribe(x => {
this.tvRequests = x; // this.tvRequests = x;
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; // this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
}); // });
} // }
public search(text: any) { // public search(text: any) {
this.searchChanged.next(text.target.value); // this.searchChanged.next(text.target.value);
} // }
public showChildren(request: ITvRequests) { // public showChildren(request: ITvRequests) {
this.selectedSeason = request; // this.selectedSeason = request;
this.showChildDialogue = true; // this.showChildDialogue = true;
} // }
public childRequestDeleted(childId: number): void { // public childRequestDeleted(childId: number): void {
// Refresh the UI, hackly way around reloading the data // // Refresh the UI, hackly way around reloading the data
this.ngOnInit(); // this.ngOnInit();
} // }
public selectRootFolder(searchResult: ITvRequests, rootFolderSelected: ISonarrRootFolder, event: any) { // public selectRootFolder(searchResult: ITvRequests, rootFolderSelected: ISonarrRootFolder, event: any) {
event.preventDefault(); // event.preventDefault();
searchResult.rootFolder = rootFolderSelected.id; // searchResult.rootFolder = rootFolderSelected.id;
this.setOverride(searchResult); // this.setOverride(searchResult);
this.setRootFolder(searchResult); // this.setRootFolder(searchResult);
} // }
public selectQualityProfile(searchResult: ITvRequests, profileSelected: ISonarrProfile, event: any) { // public selectQualityProfile(searchResult: ITvRequests, profileSelected: ISonarrProfile, event: any) {
event.preventDefault(); // event.preventDefault();
searchResult.qualityOverride = profileSelected.id; // searchResult.qualityOverride = profileSelected.id;
this.setOverride(searchResult); // this.setOverride(searchResult);
this.setQualityProfile(searchResult); // this.setQualityProfile(searchResult);
} // }
public reportIssue(catId: IIssueCategory, req: ITvRequests) { // public reportIssue(catId: IIssueCategory, req: ITvRequests) {
this.issueRequest = req; // this.issueRequest = req;
this.issueCategorySelected = catId; // this.issueCategorySelected = catId;
this.issuesBarVisible = true; // this.issuesBarVisible = true;
this.issueProviderId = req.id.toString(); // this.issueProviderId = req.id.toString();
} // }
private setOverride(req: ITvRequests): void { // private setOverride(req: ITvRequests): void {
this.setQualityOverrides(req); // this.setQualityOverrides(req);
this.setRootFolderOverrides(req); // this.setRootFolderOverrides(req);
} // }
private setQualityProfile(req: ITvRequests) { // private setQualityProfile(req: ITvRequests) {
this.requestService.setQualityProfile(req.id, req.qualityOverride).subscribe(x => { // this.requestService.setQualityProfile(req.id, req.qualityOverride).subscribe(x => {
if(x) { // if(x) {
this.notificationService.success("Quality profile updated"); // this.notificationService.success("Quality profile updated");
} else { // } else {
this.notificationService.error("Could not update the quality profile"); // this.notificationService.error("Could not update the quality profile");
} // }
}); // });
} // }
private setRootFolder(req: ITvRequests) { // private setRootFolder(req: ITvRequests) {
this.requestService.setRootFolder(req.id, req.rootFolder).subscribe(x => { // this.requestService.setRootFolder(req.id, req.rootFolder).subscribe(x => {
if(x) { // if(x) {
this.notificationService.success("Quality profile updated"); // this.notificationService.success("Quality profile updated");
} else { // } else {
this.notificationService.error("Could not update the quality profile"); // this.notificationService.error("Could not update the quality profile");
} // }
}); // });
} // }
private setQualityOverrides(req: ITvRequests): void { // private setQualityOverrides(req: ITvRequests): void {
if (this.sonarrProfiles) { // if (this.sonarrProfiles) {
const profile = this.sonarrProfiles.filter((p) => { // const profile = this.sonarrProfiles.filter((p) => {
return p.id === req.qualityOverride; // return p.id === req.qualityOverride;
}); // });
if (profile.length > 0) { // if (profile.length > 0) {
req.qualityOverrideTitle = profile[0].name; // req.qualityOverrideTitle = profile[0].name;
} // }
} // }
} // }
private setRootFolderOverrides(req: ITvRequests): void { // private setRootFolderOverrides(req: ITvRequests): void {
if (this.sonarrRootFolders) { // if (this.sonarrRootFolders) {
const path = this.sonarrRootFolders.filter((folder) => { // const path = this.sonarrRootFolders.filter((folder) => {
return folder.id === req.rootFolder; // return folder.id === req.rootFolder;
}); // });
if (path.length > 0) { // if (path.length > 0) {
req.rootPathOverrideTitle = path[0].path; // req.rootPathOverrideTitle = path[0].path;
} // }
} // }
} // }
private loadInit() { // private loadInit() {
this.requestService.getTotalTv().subscribe(x => this.totalTv = x); // this.requestService.getTotalTv().subscribe(x => this.totalTv = x);
this.requestService.getTvRequests(this.amountToLoad, 0, OrderType.RequestedDateDesc, FilterType.None, FilterType.None) // this.requestService.getTvRequests(this.amountToLoad, 0, OrderType.RequestedDateDesc, FilterType.None, FilterType.None)
.subscribe(x => { // .subscribe(x => {
this.tvRequests = x; // this.tvRequests = x;
this.tvRequests.collection.forEach((val, index) => { // this.tvRequests.collection.forEach((val, index) => {
this.setDefaults(val); // this.setDefaults(val);
this.loadBackdrop(val); // this.loadBackdrop(val);
this.setOverride(val); // this.setOverride(val);
}); // });
}); // });
} // }
private resetSearch() { // private resetSearch() {
this.currentlyLoaded = 5; // this.currentlyLoaded = 5;
this.loadInit(); // this.loadInit();
} // }
private setDefaults(val: ITvRequests) { // private setDefaults(val: ITvRequests) {
if (val.posterPath === null) { // if (val.posterPath === null) {
val.posterPath = this.defaultPoster; // val.posterPath = this.defaultPoster;
} // }
} // }
private loadBackdrop(val: ITvRequests): void { // private loadBackdrop(val: ITvRequests): void {
if (val.background != null) { // if (val.background != null) {
val.background = this.sanitizer.bypassSecurityTrustStyle // val.background = this.sanitizer.bypassSecurityTrustStyle
("url(https://image.tmdb.org/t/p/w1280" + val.background + ")"); // ("url(https://image.tmdb.org/t/p/w1280" + val.background + ")");
} else { // } else {
this.imageService.getTvBanner(val.tvDbId).subscribe(x => { // this.imageService.getTvBanner(val.tvDbId).subscribe(x => {
if (x) { // if (x) {
val.background = this.sanitizer.bypassSecurityTrustStyle // val.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")"); // ("url(" + x + ")");
} // }
}); // });
} // }
} // }
} // }

@ -3,7 +3,6 @@ import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms"; import { FormsModule } from "@angular/forms";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { MovieSearchComponent } from "./moviesearch.component"; import { MovieSearchComponent } from "./moviesearch.component";
import { MovieSearchGridComponent } from "./moviesearchgrid.component"; import { MovieSearchGridComponent } from "./moviesearchgrid.component";
@ -16,8 +15,6 @@ import { TvSearchComponent } from "./tvsearch.component";
import { CardsFreeModule } from "angular-bootstrap-md"; import { CardsFreeModule } from "angular-bootstrap-md";
import { SidebarModule, TooltipModule, TreeTableModule } from "primeng/primeng";
import { RequestService } from "../services"; import { RequestService } from "../services";
import { SearchService } from "../services"; import { SearchService } from "../services";
@ -35,7 +32,6 @@ const routes: Routes = [
CommonModule, CommonModule,
FormsModule, FormsModule,
RouterModule.forChild(routes), RouterModule.forChild(routes),
NgbModule.forRoot(),
TreeTableModule, TreeTableModule,
SharedModule, SharedModule,
SidebarModule, SidebarModule,

@ -1,5 +1,5 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { MatSnackBar, MatSnackBarConfig } from "@angular/material"; import { MatSnackBar, MatSnackBarConfig } from "@angular/material/snack-bar";
@Injectable() @Injectable()
export class MessageService { export class MessageService {

@ -1,6 +1,5 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Message } from "primeng/components/common/api"; import { MatSnackBar, MatSnackBarConfig } from "@angular/material/snack-bar";
import { MatSnackBar, MatSnackBarConfig } from "@angular/material";
@Injectable() @Injectable()
export class NotificationService { export class NotificationService {
@ -10,12 +9,6 @@ export class NotificationService {
duration:3000, duration:3000,
} }
public messages: Message[] = [];
public addMessage(message: Message) {
this.clearMessages();
this.messages.push(message);
this.messages = JSON.parse(JSON.stringify(this.messages)); // NOTE: THIS IS A HACK AROUND A BUG https://github.com/primefaces/primeng/issues/2943
}
public success(body: string) { public success(body: string) {
this.snackbar.open(body, "OK", this.config); this.snackbar.open(body, "OK", this.config);
@ -32,8 +25,4 @@ export class NotificationService {
public error(body: string) { public error(body: string) {
this.snackbar.open(body, "OK", this.config); this.snackbar.open(body, "OK", this.config);
} }
public clearMessages() {
this.messages = [];
}
} }

@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { TreeNode } from "primeng/primeng"; import { UITreeNode } from "primeng/tree";
import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IDenyAlbumModel, IDenyMovieModel, IFilter, import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IDenyAlbumModel, IDenyMovieModel, IFilter,
IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvDenyModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces"; IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvDenyModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
import { ITvRequestViewModel } from "../interfaces"; import { ITvRequestViewModel } from "../interfaces";
@ -90,8 +90,8 @@ export class RequestService extends ServiceHelpers {
return this.http.get<IRequestsViewModel<ITvRequests>>(`${this.url}tv/${count}/${position}/${order}/${status}/${availability}`, {headers: this.headers}); return this.http.get<IRequestsViewModel<ITvRequests>>(`${this.url}tv/${count}/${position}/${order}/${status}/${availability}`, {headers: this.headers});
} }
public getTvRequestsTree(count: number, position: number): Observable<TreeNode[]> { public getTvRequestsTree(count: number, position: number): Observable<UITreeNode[]> {
return this.http.get<TreeNode[]>(`${this.url}tv/${count}/${position}/tree`, {headers: this.headers}); return this.http.get<UITreeNode[]>(`${this.url}tv/${count}/${position}/tree`, {headers: this.headers});
} }
public getChildRequests(requestId: number): Observable<IChildRequests[]> { public getChildRequests(requestId: number): Observable<IChildRequests[]> {
@ -102,8 +102,8 @@ export class RequestService extends ServiceHelpers {
return this.http.get<ITvRequests[]>(`${this.url}tv/search/${search}`, {headers: this.headers}); return this.http.get<ITvRequests[]>(`${this.url}tv/search/${search}`, {headers: this.headers});
} }
public searchTvRequestsTree(search: string): Observable<TreeNode[]> { public searchTvRequestsTree(search: string): Observable<UITreeNode[]> {
return this.http.get<TreeNode[]>(`${this.url}tv/search/${search}/tree`, {headers: this.headers}); return this.http.get<UITreeNode[]>(`${this.url}tv/search/${search}/tree`, {headers: this.headers});
} }
public removeTvRequest(requestId: number) { public removeTvRequest(requestId: number) {

@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { TreeNode } from "primeng/primeng"; import { UITreeNode } from "primeng/tree";
import { ISearchMovieResult } from "../interfaces"; import { ISearchMovieResult } from "../interfaces";
import { ISearchTvResult } from "../interfaces"; import { ISearchTvResult } from "../interfaces";
import { ISearchAlbumResult, ISearchArtistResult } from "../interfaces/ISearchMusicResult"; import { ISearchAlbumResult, ISearchArtistResult } from "../interfaces/ISearchMusicResult";
@ -58,12 +58,12 @@ export class SearchService extends ServiceHelpers {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, { headers: this.headers }); return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/${searchTerm}`, { headers: this.headers });
} }
public searchTvTreeNode(searchTerm: string): Observable<TreeNode[]> { public searchTvTreeNode(searchTerm: string): Observable<UITreeNode[]> {
return this.http.get<TreeNode[]>(`${this.url}/Tv/${searchTerm}/tree`, { headers: this.headers }); return this.http.get<UITreeNode[]>(`${this.url}/Tv/${searchTerm}/tree`, { headers: this.headers });
} }
public getShowInformationTreeNode(theTvDbId: number): Observable<TreeNode> { public getShowInformationTreeNode(theTvDbId: number): Observable<UITreeNode> {
return this.http.get<TreeNode>(`${this.url}/Tv/info/${theTvDbId}/Tree`, { headers: this.headers }); return this.http.get<UITreeNode>(`${this.url}/Tv/info/${theTvDbId}/Tree`, { headers: this.headers });
} }
public getShowInformation(theTvDbId: number): Observable<ISearchTvResult> { public getShowInformation(theTvDbId: number): Observable<ISearchTvResult> {

@ -1,20 +1,15 @@
@import "~styles/variables.scss"; @import "~styles/variables.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
::ng-deep .dark .small-middle-container{ .control-label {
background-color: rgba(0, 0, 0, 0.10); font-weight: 400;
padding: 2em;
} }
.control-label{ ::ng-deep .dark .btn:hover {
font-weight:400;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }

@ -1,32 +1,27 @@
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-4{
.col-md-4 {
display: contents; display: contents;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
.col-md-4 {
.col-md-4{
display: inline-table; display: inline-table;
} }
} }

@ -1,42 +1,37 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.col-12 { .col-12 {
display:grid; display: grid;
} }
textarea { textarea {
min-height:100px; min-height: 100px;
height: auto; height: auto;
max-height:800px; max-height: 800px;
} }
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
.btn-danger-outline{ .btn-danger-outline {
background-color: #E84C3D; background-color: #E84C3D;
} }
.btn-success-outline{ .btn-success-outline {
background-color: #1b9d1b; background-color: #1b9d1b;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }

@ -1,16 +1,11 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }

@ -1,45 +1,41 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.col-md-10{ .col-md-10 {
display: grid; display: grid;
} }
.col-md-2{ .col-md-2 {
display: contents; display: contents;
} }
.control-label{
font-weight:400; .control-label {
font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
.btn-danger-outline{ .btn-danger-outline {
background-color: #E84C3D; background-color: #E84C3D;
} }
.btn-success-outline{ .btn-success-outline {
background-color: #1b9d1b; background-color: #1b9d1b;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-2{ .col-md-2 {
display: inline-table; display: inline-table;
} }
} }

@ -2,7 +2,7 @@
import { IEmbyServer, IEmbySettings } from "../../interfaces"; import { IEmbyServer, IEmbySettings } from "../../interfaces";
import { EmbyService, JobService, NotificationService, SettingsService, TesterService } from "../../services"; import { EmbyService, JobService, NotificationService, SettingsService, TesterService } from "../../services";
import { MatTabChangeEvent, MatTabGroup } from "@angular/material"; import { MatTabChangeEvent, MatTabGroup } from "@angular/material/tabs";
import {FormControl} from '@angular/forms'; import {FormControl} from '@angular/forms';
@Component({ @Component({

@ -1,52 +1,50 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-9{
.col-md-9 {
display: inline-table; display: inline-table;
} }
.col-md-3{
.col-md-3 {
display: inline-table; display: inline-table;
} }
.row{ .row {
display:block; display: block;
} }
.btn-danger-outline{ .btn-danger-outline {
background-color: #E84C3D; background-color: #E84C3D;
} }
.btn-success-outline{ .btn-success-outline {
background-color: #1b9d1b; background-color: #1b9d1b;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .categoryResults {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .categoryResults{
background-color: rgba(0, 0, 0, 0.05); background-color: rgba(0, 0, 0, 0.05);
padding: 1em; padding: 1em;
} }
::ng-deep .dark .btn:hover{ ::ng-deep .dark .btn:hover {
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
} }

@ -1,50 +1,44 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-5{ .col-md-5 {
display: contents; display: contents;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
.btn-danger-outline{ .btn-danger-outline {
background-color: #E84C3D; background-color: #E84C3D;
} }
.btn-success-outline{ .btn-success-outline {
background-color: #1b9d1b; background-color: #1b9d1b;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
.col-md-5 {
.col-md-5{
display: inline-table; display: inline-table;
} }
} }

@ -7,7 +7,7 @@ import { NotificationService } from "../../services";
import { MobileService, SettingsService } from "../../services"; import { MobileService, SettingsService } from "../../services";
import { CloudMobileService } from "../../services/cloudmobile.service"; import { CloudMobileService } from "../../services/cloudmobile.service";
import { SelectionModel } from "@angular/cdk/collections"; import { SelectionModel } from "@angular/cdk/collections";
import { MatTableDataSource } from "@angular/material"; import { MatTableDataSource } from "@angular/material/table";
@Component({ @Component({
templateUrl: "./cloudmobile.component.html", templateUrl: "./cloudmobile.component.html",

@ -1,50 +1,44 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-5{ .col-md-5 {
display: contents; display: contents;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
.btn-danger-outline{ .btn-danger-outline {
background-color: #E84C3D; background-color: #E84C3D;
} }
.btn-success-outline{ .btn-success-outline {
background-color: #1b9d1b; background-color: #1b9d1b;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
.col-md-5 {
.col-md-5{
display: inline-table; display: inline-table;
} }
} }

@ -4,7 +4,7 @@ import { takeUntil } from "rxjs/operators";
import { IPlexLibrariesSettings, IPlexServer, IPlexServerResponse, IPlexServerViewModel, IPlexSettings } from "../../interfaces"; import { IPlexLibrariesSettings, IPlexServer, IPlexServerResponse, IPlexServerViewModel, IPlexSettings } from "../../interfaces";
import { JobService, NotificationService, PlexService, SettingsService, TesterService } from "../../services"; import { JobService, NotificationService, PlexService, SettingsService, TesterService } from "../../services";
import { MatTabChangeEvent, MatTabGroup } from "@angular/material"; import { MatTabChangeEvent, MatTabGroup } from "@angular/material/tabs";
import {FormControl} from '@angular/forms'; import {FormControl} from '@angular/forms';
@Component({ @Component({

@ -2,12 +2,18 @@
<div *ngIf="form" class="small-middle-container"> <div *ngIf="form" class="small-middle-container">
<fieldset> <fieldset>
<legend>Radarr Settings</legend> <legend>Radarr Settings</legend>
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:2%;">
<div style="float: right;"> <div style="float: right;">
<div class="md-form-field"><mat-slide-toggle formControlName="enabled">Enable</mat-slide-toggle></div> <div class="md-form-field">
<div class="md-form-field"><mat-slide-toggle [(ngModel)]="advanced">Advanced</mat-slide-toggle></div> <mat-slide-toggle formControlName="enabled">Enable</mat-slide-toggle>
</div>
<div class="md-form-field">
<mat-slide-toggle [(ngModel)]="advanced" [ngModelOptions]="{standalone: true}">Advanced</mat-slide-toggle>
</div>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:2%;">
<div class="col-md-5"> <div class="col-md-5">
<div class="md-form-field"> <div class="md-form-field">
<mat-form-field> <mat-form-field>
@ -77,16 +83,17 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
</div>
<div class="col">
<div class="md-form-field" *ngIf="advanced" style="color:#ff761b"> <div class="md-form-field" *ngIf="advanced" style="color:#ff761b">
<mat-slide-toggle formControlName="addOnly"> <mat-slide-toggle formControlName="addOnly">
Do not search Do not search
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
</div>
<div class="col">
<div class="md-form-field"> <div class="md-form-field">
<div> <div>
@ -103,7 +110,7 @@
</div> </div>
</div> </div>
</form>
</div> </div>
</form>
</fieldset> </fieldset>
</div> </div>

@ -1,56 +1,49 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-5{ .col-md-5 {
display: contents; display: contents;
} }
.col-md-4{ .col-md-4 {
display: contents; display: contents;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
::ng-deep .load { ::ng-deep .load {
max-width: fit-content; max-width: fit-content;
margin-left:3em; margin-left: 3em;
padding: 0.5rem 1.14rem; padding: 0.5rem 1.14rem;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
.col-md-5 {
.col-md-5{
display: inline-table; display: inline-table;
} }
.col-md-4 {
.col-md-4{
display: inline-table; display: inline-table;
} }
} }

@ -2,7 +2,6 @@ import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { NgbAccordionModule, NgbModule } from "@ng-bootstrap/ng-bootstrap";
// import { TagInputModule } from "ngx-chips"; // import { TagInputModule } from "ngx-chips";
import { ClipboardModule } from "ngx-clipboard"; import { ClipboardModule } from "ngx-clipboard";
@ -51,8 +50,16 @@ import { WikiComponent } from "./wiki.component";
import { SettingsMenuComponent } from "./settingsmenu.component"; import { SettingsMenuComponent } from "./settingsmenu.component";
import { AutoCompleteModule, CalendarModule, DialogModule, InputSwitchModule, InputTextModule, MenuModule, RadioButtonModule, TooltipModule } from "primeng/primeng"; import {AutoCompleteModule } from "primeng/autocomplete";
import { MatMenuModule} from "@angular/material"; import {CalendarModule } from "primeng/calendar";
import {InputSwitchModule } from "primeng/inputswitch";
import {InputTextModule } from "primeng/inputtext";
import {DialogModule } from "primeng/dialog";
import {MenuModule } from "primeng/menu";
import {RadioButtonModule } from "primeng/radiobutton";
import {TooltipModule } from "primeng/tooltip";
import { MatMenuModule } from "@angular/material/menu";
import { SharedModule } from "../shared/shared.module"; import { SharedModule } from "../shared/shared.module";
import { HubService } from "../services/hub.service"; import { HubService } from "../services/hub.service";
import { LogsComponent } from "./logs/logs.component"; import { LogsComponent } from "./logs/logs.component";
@ -108,9 +115,7 @@ const routes: Routes = [
MenuModule, MenuModule,
InputSwitchModule, InputSwitchModule,
InputTextModule, InputTextModule,
NgbModule,
TooltipModule, TooltipModule,
NgbAccordionModule,
AutoCompleteModule, AutoCompleteModule,
CalendarModule, CalendarModule,
// TagInputModule, // TagInputModule,

@ -1,15 +1,10 @@
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }

@ -2,11 +2,11 @@
<div *ngIf="form" class="small-middle-container"> <div *ngIf="form" class="small-middle-container">
<fieldset> <fieldset>
<legend>Sonarr Settings</legend> <legend>Sonarr Settings</legend>
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:3%;">
<div style="float: right;"> <div style="float: right;">
<span style="vertical-align: top;">Advanced</span> <span style="vertical-align: top;">Advanced</span>
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch> <p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced" [ngModelOptions]="{standalone: true}"></p-inputSwitch>
</div> </div>
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:3%;">
<div class="col-md-6" style="display:block;"> <div class="col-md-6" style="display:block;">
<div class="col-md-4"> <div class="col-md-4">
@ -43,8 +43,7 @@
<i *ngIf="form.get('ip').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The IP/Hostname is required"></i> <i *ngIf="form.get('ip').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The IP/Hostname is required"></i>
</label> </label>
<input type="text" class="form-control form-control-custom " formControlName="ip" id="Ip" name="Ip" <input type="text" class="form-control form-control-custom " formControlName="ip" id="Ip" name="Ip" placeholder="localhost" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
placeholder="localhost" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
</div> </div>
<div class="form-group"> <div class="form-group">
@ -53,8 +52,7 @@
<i *ngIf="form.get('port').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The Port is required"></i> <i *ngIf="form.get('port').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The Port is required"></i>
</label> </label>
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('port').hasError('required')}" <input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('port').hasError('required')}" formControlName="port" id="portNumber" name="Port" placeholder="Port Number">
formControlName="port" id="portNumber" name="Port" placeholder="Port Number">
</div> </div>
@ -64,14 +62,12 @@
<i *ngIf="form.get('apiKey').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The API Key is required"></i> <i *ngIf="form.get('apiKey').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The API Key is required"></i>
</label> </label>
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}" <input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}" formControlName="apiKey" id="ApiKey" name="ApiKey">
formControlName="apiKey" id="ApiKey" name="ApiKey">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="SubDir" class="control-label">Sonarr Base Url</label> <label for="SubDir" class="control-label">Sonarr Base Url</label>
<div> <div>
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir" <input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir" name="SubDir">
name="SubDir">
</div> </div>
</div> </div>
</div> </div>
@ -82,8 +78,7 @@
<i *ngIf="form.get('qualityProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Default Quality Profile is required"></i> <i *ngIf="form.get('qualityProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Default Quality Profile is required"></i>
</label> </label>
<div id="profiles"> <div id="profiles">
<select class="form-control form-control-custom col-md-5 form-half" [ngClass]="{'form-error': form.get('qualityProfile').hasError('required')}" <select class="form-control form-control-custom col-md-5 form-half" [ngClass]="{'form-error': form.get('qualityProfile').hasError('required')}" id="select" formControlName="qualityProfile">
id="select" formControlName="qualityProfile">
<option *ngFor="let quality of qualities" value="{{quality.id}}">{{quality.name}}</option> <option *ngFor="let quality of qualities" value="{{quality.id}}">{{quality.name}}</option>
</select> </select>
<button type="button" (click)="getProfiles(form)" class="btn btn-primary-outline col-md-4 col-md-push-1 load"> <button type="button" (click)="getProfiles(form)" class="btn btn-primary-outline col-md-4 col-md-push-1 load">
@ -110,8 +105,7 @@
<i *ngIf="form.get('rootPath').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Default Root Path is required"></i> <i *ngIf="form.get('rootPath').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Default Root Path is required"></i>
</label> </label>
<div id="rootFolders"> <div id="rootFolders">
<select class="form-control form-control-custom col-md-5 form-half" formControlName="rootPath" <select class="form-control form-control-custom col-md-5 form-half" formControlName="rootPath" [ngClass]="{'form-error': form.get('rootPath').hasError('required')}">
[ngClass]="{'form-error': form.get('rootPath').hasError('required')}">
<option *ngFor="let folder of rootFolders" value="{{folder.id}}">{{folder.path}}</option> <option *ngFor="let folder of rootFolders" value="{{folder.id}}">{{folder.path}}</option>
</select> </select>
<button type="button" (click)="getRootFolders(form)" class="btn btn-primary-outline load col-md-4 col-md-push-1"> <button type="button" (click)="getRootFolders(form)" class="btn btn-primary-outline load col-md-4 col-md-push-1">
@ -134,8 +128,7 @@
<i *ngIf="form.get('languageProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Language Profile is required"></i> <i *ngIf="form.get('languageProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Language Profile is required"></i>
</label> </label>
<div id="langaugeProfile"> <div id="langaugeProfile">
<select formControlName="languageProfile" class="form-control form-control-custom col-md-5 form-half" <select formControlName="languageProfile" class="form-control form-control-custom col-md-5 form-half" id="select" [ngClass]="{'form-error': form.get('languageProfile').hasError('required')}">
id="select" [ngClass]="{'form-error': form.get('languageProfile').hasError('required')}">
<option *ngFor="let lang of languageProfiles" [ngValue]="lang.id">{{lang.name}}</option> <option *ngFor="let lang of languageProfiles" [ngValue]="lang.id">{{lang.name}}</option>
</select> </select>
<button (click)="getLanguageProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Load <button (click)="getLanguageProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Load

@ -1,56 +1,49 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-5{ .col-md-5 {
display: contents; display: contents;
} }
.col-md-4{ .col-md-4 {
display: contents; display: contents;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
::ng-deep .load { ::ng-deep .load {
max-width: fit-content; max-width: fit-content;
margin-left:3em; margin-left: 3em;
padding: 0.5rem 1.14rem; padding: 0.5rem 1.14rem;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
.col-md-5 {
.col-md-5{
display: inline-table; display: inline-table;
} }
.col-md-4 {
.col-md-4{
display: inline-table; display: inline-table;
} }
} }

@ -1,16 +1,11 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }

@ -1,50 +1,44 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
.col-md-6{ .col-md-6 {
display: contents; display: contents;
} }
.col-md-5{ .col-md-5 {
display: contents; display: contents;
} }
.control-label{ .control-label {
font-weight:400; font-weight: 400;
} }
.row{ .row {
display:block; display: block;
} }
.btn-danger-outline{ .btn-danger-outline {
background-color: #E84C3D; background-color: #E84C3D;
} }
.btn-success-outline{ .btn-success-outline {
background-color: #1b9d1b; background-color: #1b9d1b;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }
@media (min-width:1440px) { @media (min-width:1440px) {
.col-md-6{ .col-md-6 {
display: inline-table; display: inline-table;
} }
.col-md-5 {
.col-md-5{
display: inline-table; display: inline-table;
} }
} }

@ -1,16 +1,11 @@
@import "~styles/shared.scss"; @import "~styles/shared.scss";
.small-middle-container{ .small-middle-container {
margin: auto; margin: auto;
width: 95%; width: 95%;
margin-top:10px; margin-top: 10px;
} }
::ng-deep .dark .small-middle-container{ ::ng-deep .dark .btn:hover {
background-color: rgba(0, 0, 0, 0.10);
padding: 2em;
}
::ng-deep .dark .btn:hover{
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15); box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
color: inherit; color: inherit;
} }

@ -1,5 +1,6 @@
import { Component, OnInit, Inject } from "@angular/core"; import { Component, OnInit, Inject } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA, MatCheckboxChange } from "@angular/material"; import { MatCheckboxChange } from "@angular/material/checkbox";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2"; import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
import { RequestService, MessageService } from "../../services"; import { RequestService, MessageService } from "../../services";
import { ITvRequestViewModel, ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests } from "../../interfaces"; import { ITvRequestViewModel, ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests } from "../../interfaces";

@ -7,13 +7,33 @@ import { MomentModule } from "ngx-moment";
import { IssuesReportComponent } from "./issues-report.component"; import { IssuesReportComponent } from "./issues-report.component";
import { InputSwitchModule, SidebarModule } from "primeng/primeng"; import { SidebarModule } from "primeng/sidebar";
import { InputSwitchModule } from "primeng/inputswitch";
import { import { MatButtonModule } from '@angular/material/button';
MatButtonModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, MatTooltipModule, MatSelectModule, MatTableModule, MatPaginatorModule, MatSortModule, import { MatNativeDateModule } from '@angular/material/core';
MatTreeModule, MatStepperModule, MatSnackBarModule} from '@angular/material'; import { MatIconModule } from '@angular/material/icon';
import { MatCardModule, MatInputModule, MatTabsModule, MatAutocompleteModule, MatCheckboxModule, MatExpansionModule, MatDialogModule, MatProgressSpinnerModule, import { MatListModule } from '@angular/material/list';
MatChipsModule, MatSlideToggleModule } from "@angular/material"; import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSortModule } from '@angular/material/sort';
import { MatStepperModule } from '@angular/material/stepper';
import { MatTableModule } from '@angular/material/table';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTreeModule } from '@angular/material/tree';
import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatCardModule } from "@angular/material/card";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatChipsModule } from "@angular/material/chips";
import { MatDialogModule } from "@angular/material/dialog";
import { MatExpansionModule } from "@angular/material/expansion";
import { MatInputModule } from "@angular/material/input";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
import { MatTabsModule } from "@angular/material/tabs";
import { EpisodeRequestComponent } from "./episode-request/episode-request.component"; import { EpisodeRequestComponent } from "./episode-request/episode-request.component";
@NgModule({ @NgModule({

@ -1,7 +1,7 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router" import { RouterModule } from "@angular/router"
import { MatCheckboxModule } from '@angular/material'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { SharedModule } from "../shared/shared.module"; import { SharedModule } from "../shared/shared.module";
import { QRCodeModule } from 'angularx-qrcode'; import { QRCodeModule } from 'angularx-qrcode';

@ -4,8 +4,6 @@ import { ActivatedRoute, Router } from "@angular/router";
import { ICheckbox, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces"; import { ICheckbox, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces";
import { IdentityService, NotificationService, RadarrService, SonarrService, MessageService } from "../services"; import { IdentityService, NotificationService, RadarrService, SonarrService, MessageService } from "../services";
import { ConfirmationService } from "primeng/primeng";
@Component({ @Component({
templateUrl: "./usermanagement-user.component.html", templateUrl: "./usermanagement-user.component.html",
styleUrls: ["./usermanagement-user.component.scss"], styleUrls: ["./usermanagement-user.component.scss"],
@ -30,7 +28,6 @@ export class UserManagementUserComponent implements OnInit {
private notificationService: MessageService, private notificationService: MessageService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private confirmationService: ConfirmationService,
private sonarrService: SonarrService, private sonarrService: SonarrService,
private radarrService: RadarrService) { private radarrService: RadarrService) {
@ -120,28 +117,28 @@ export class UserManagementUserComponent implements OnInit {
} }
public delete() { public delete() {
// TODO
this.confirmationService.confirm({ // this.confirmationService.confirm({
message: "Are you sure that you want to delete this user? If this user has any requests they will also be deleted.", // message: "Are you sure that you want to delete this user? If this user has any requests they will also be deleted.",
header: "Are you sure?", // header: "Are you sure?",
icon: "fa fa-trash", // icon: "fa fa-trash",
accept: () => { // accept: () => {
this.identityService.deleteUser(this.user).subscribe(x => { // this.identityService.deleteUser(this.user).subscribe(x => {
if (x.successful) { // if (x.successful) {
this.notificationService.send(`The user ${this.user.userName} was deleted`); // this.notificationService.send(`The user ${this.user.userName} was deleted`);
this.router.navigate(["usermanagement"]); // this.router.navigate(["usermanagement"]);
} else { // } else {
x.errors.forEach((val) => { // x.errors.forEach((val) => {
this.notificationService.send(val); // this.notificationService.send(val);
}); // });
} // }
}); // });
}, // },
reject: () => { // reject: () => {
return; // return;
}, // },
}); // });
} }
public resetPassword() { public resetPassword() {

@ -2,7 +2,8 @@
import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces"; import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces";
import { IdentityService, NotificationService, SettingsService } from "../services"; import { IdentityService, NotificationService, SettingsService } from "../services";
import { MatSort, MatTableDataSource } from "@angular/material"; import { MatSort } from "@angular/material/sort";
import { MatTableDataSource } from "@angular/material/table";
import { SelectionModel } from "@angular/cdk/collections"; import { SelectionModel } from "@angular/cdk/collections";
@Component({ @Component({
@ -16,7 +17,7 @@ export class UserManagementComponent implements OnInit {
public dataSource: MatTableDataSource<IUser>; public dataSource: MatTableDataSource<IUser>;
public selection = new SelectionModel<IUser>(true, []); public selection = new SelectionModel<IUser>(true, []);
@ViewChild(MatSort, {static: false}) public sort: MatSort; @ViewChild(MatSort) public sort: MatSort;
public users: IUser[]; public users: IUser[];
public checkAll = false; public checkAll = false;
public emailSettings: IEmailNotificationSettings; public emailSettings: IEmailNotificationSettings;

@ -2,9 +2,11 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { ConfirmationService, ConfirmDialogModule, MultiSelectModule, SidebarModule, TooltipModule } from "primeng/primeng"; import { ConfirmDialogModule } from "primeng/confirmdialog";
import { MultiSelectModule } from "primeng/multiselect";
import { SidebarModule } from "primeng/sidebar";
import { TooltipModule } from "primeng/tooltip";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { UpdateDetailsComponent } from "./updatedetails.component"; import { UpdateDetailsComponent } from "./updatedetails.component";
import { UserManagementUserComponent } from "./usermanagement-user.component"; import { UserManagementUserComponent } from "./usermanagement-user.component";
@ -32,7 +34,6 @@ const routes: Routes = [
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
RouterModule.forChild(routes), RouterModule.forChild(routes),
NgbModule.forRoot(),
MultiSelectModule, MultiSelectModule,
PipeModule, PipeModule,
ConfirmDialogModule, ConfirmDialogModule,
@ -51,7 +52,6 @@ const routes: Routes = [
], ],
providers: [ providers: [
IdentityService, IdentityService,
ConfirmationService,
PlexService, PlexService,
RadarrService, RadarrService,
SonarrService, SonarrService,

@ -1,6 +1,6 @@
import { Component, OnInit, ViewChild } from "@angular/core"; import { Component, OnInit, ViewChild } from "@angular/core";
import { OverlayPanel } from "primeng/primeng"; import { OverlayPanel } from "primeng/overlaypanel";
import { NotificationService, VoteService } from "../services"; import { NotificationService, VoteService } from "../services";
import { IVoteEngineResult, IVoteViewModel, RequestTypes, VoteType } from "../interfaces"; import { IVoteEngineResult, IVoteViewModel, RequestTypes, VoteType } from "../interfaces";
@ -18,7 +18,7 @@ export class VoteComponent implements OnInit {
public completedVotes: IVoteViewModel[]; public completedVotes: IVoteViewModel[];
public VoteType = VoteType; public VoteType = VoteType;
public panelImage: string; public panelImage: string;
@ViewChild("op", {static: false}) public overlayPanel: OverlayPanel; @ViewChild("op") public overlayPanel: OverlayPanel;
constructor(private voteService: VoteService, private notificationSerivce: NotificationService) { } constructor(private voteService: VoteService, private notificationSerivce: NotificationService) { }

@ -1,9 +1,9 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { OrderModule } from "ngx-order-pipe"; import { OrderModule } from "ngx-order-pipe";
import { OverlayPanelModule, SharedModule, TabViewModule } from "primeng/primeng"; import { OverlayPanelModule } from "primeng/overlaypanel";
import { TabViewModule } from "primeng/tabview";
import { VoteService } from "../services"; import { VoteService } from "../services";
@ -20,8 +20,6 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild(routes), RouterModule.forChild(routes),
NgbModule.forRoot(),
SharedModule,
OrderModule, OrderModule,
OmbiShared, OmbiShared,
TabViewModule, TabViewModule,

@ -3,8 +3,7 @@ import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import {ConfirmationService, ConfirmDialogModule } from "primeng/primeng"; import { MatStepperModule } from "@angular/material/stepper";
import { MatStepperModule } from "@angular/material";
import { CreateAdminComponent } from "./createadmin/createadmin.component"; import { CreateAdminComponent } from "./createadmin/createadmin.component";
import { EmbyComponent } from "./emby/emby.component"; import { EmbyComponent } from "./emby/emby.component";
@ -31,7 +30,6 @@ const routes: Routes = [
CommonModule, CommonModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
ConfirmDialogModule,
SharedModule, SharedModule,
MatStepperModule, MatStepperModule,
RouterModule.forChild(routes), RouterModule.forChild(routes),
@ -50,7 +48,6 @@ const routes: Routes = [
PlexService, PlexService,
IdentityService, IdentityService,
EmbyService, EmbyService,
ConfirmationService,
PlexOAuthService, PlexOAuthService,
], ],

@ -53,9 +53,10 @@
<title>Ombi</title> <title>Ombi</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
</head> </head>
<body> <body class="mat-typography">
<app-ombi> <app-ombi>
<script type="text/javascript"> <script type="text/javascript">

@ -9,8 +9,6 @@ import { environment } from "./environments/environment";
import "./polyfills"; import "./polyfills";
import "hammerjs";
import { enableProdMode } from "@angular/core"; import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from "./app/app.module"; import { AppModule } from "./app/app.module";

@ -1,2 +1,6 @@
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';
import "core-js/es7/reflect"; import "core-js/es7/reflect";
import "zone.js/dist/zone"; import "zone.js/dist/zone";

@ -5,3 +5,7 @@
.md-form-field .mat-form-field-infix { .md-form-field .mat-form-field-infix {
width: 380px; width: 380px;
} }
td.mat-cell {
padding: 0.75rem !important;
}

@ -2,6 +2,7 @@
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"baseUrl": "./", "baseUrl": "./",
"module": "esnext",
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
@ -18,5 +19,12 @@
"es2017", "es2017",
"dom" "dom"
] ]
} } ,
"files": [
"main.ts",
"polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
} }

File diff suppressed because it is too large Load Diff

@ -208,7 +208,7 @@ namespace Ombi.Controllers.V1
[HttpPost("comments")] [HttpPost("comments")]
public async Task<IssueComments> AddComment([FromBody] NewIssueCommentViewModel comment) public async Task<IssueComments> AddComment([FromBody] NewIssueCommentViewModel comment)
{ {
var user = await _userManager.Users.Where(x => User.Identity.Name.Equals(x.UserName, StringComparison.InvariantCultureIgnoreCase)) var user = await _userManager.Users.Where(x => User.Identity.Name == x.UserName)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
var issue = await _issues.GetAll().Include(x => x.UserReported).Include(x => x.IssueCategory).FirstOrDefaultAsync(x => x.Id == comment.IssueId); var issue = await _issues.GetAll().Include(x => x.UserReported).Include(x => x.IssueCategory).FirstOrDefaultAsync(x => x.Id == comment.IssueId);
if (issue == null) if (issue == null)
@ -278,7 +278,8 @@ namespace Ombi.Controllers.V1
[HttpPost("status")] [HttpPost("status")]
public async Task<bool> UpdateStatus([FromBody] IssueStateViewModel model) public async Task<bool> UpdateStatus([FromBody] IssueStateViewModel model)
{ {
var user = await _userManager.Users.Where(x => User.Identity.Name.Equals(x.UserName, StringComparison.InvariantCultureIgnoreCase)) var username = User.Identity.Name.ToUpper();
var user = await _userManager.Users.Where(x => username == x.NormalizedUserName)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
var issue = await _issues.GetAll().Include(x => x.UserReported).Include(x => x.IssueCategory).FirstOrDefaultAsync(x => x.Id == model.IssueId); var issue = await _issues.GetAll().Include(x => x.UserReported).Include(x => x.IssueCategory).FirstOrDefaultAsync(x => x.Id == model.IssueId);
if (issue == null) if (issue == null)

Loading…
Cancel
Save